From 2543a87108d2af7d48a43b3d6685c2b1ea279e36 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Wed, 6 Apr 2011 10:43:11 +0200 Subject: workqueue: remove cancel_rearming_delayed_work[queue]() cancel_rearming_delayed_work() and cancel_rearming_delayed_workqueue() can be removed now. Signed-off-by: WANG Cong Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index f584aba78ca9..57b31b3d83bd 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -412,21 +412,6 @@ static inline bool __cancel_delayed_work(struct delayed_work *work) return ret; } -/* Obsolete. use cancel_delayed_work_sync() */ -static inline __deprecated -void cancel_rearming_delayed_workqueue(struct workqueue_struct *wq, - struct delayed_work *work) -{ - cancel_delayed_work_sync(work); -} - -/* Obsolete. use cancel_delayed_work_sync() */ -static inline __deprecated -void cancel_rearming_delayed_work(struct delayed_work *work) -{ - cancel_delayed_work_sync(work); -} - #ifndef CONFIG_SMP static inline long work_on_cpu(unsigned int cpu, long (*fn)(void *), void *arg) { -- cgit v1.2.3 From 9c5a2ba70251ecaab18c7a83e38b3c620223476c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 5 Apr 2011 18:01:44 +0200 Subject: workqueue: separate out drain_workqueue() from destroy_workqueue() There are users which want to drain workqueues without destroying it. Separate out drain functionality from destroy_workqueue() into drain_workqueue() and make it accessible to workqueue users. To guarantee forward-progress, only chain queueing is allowed while drain is in progress. If a new work item which isn't chained from the running or pending work items is queued while draining is in progress, WARN_ON_ONCE() is triggered. Signed-off-by: Tejun Heo Cc: James Bottomley --- include/linux/workqueue.h | 3 +- kernel/workqueue.c | 81 +++++++++++++++++++++++++++++++---------------- 2 files changed, 55 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 57b31b3d83bd..2be2887c6958 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -255,7 +255,7 @@ enum { WQ_HIGHPRI = 1 << 4, /* high priority */ WQ_CPU_INTENSIVE = 1 << 5, /* cpu instensive workqueue */ - WQ_DYING = 1 << 6, /* internal: workqueue is dying */ + WQ_DRAINING = 1 << 6, /* internal: workqueue is draining */ WQ_RESCUER = 1 << 7, /* internal: workqueue has rescuer */ WQ_MAX_ACTIVE = 512, /* I like 512, better ideas? */ @@ -355,6 +355,7 @@ extern int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay); extern void flush_workqueue(struct workqueue_struct *wq); +extern void drain_workqueue(struct workqueue_struct *wq); extern void flush_scheduled_work(void); extern int schedule_work(struct work_struct *work); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index e3378e8d3a5c..25c8afeaeae8 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -221,7 +221,7 @@ typedef unsigned long mayday_mask_t; * per-CPU workqueues: */ struct workqueue_struct { - unsigned int flags; /* I: WQ_* flags */ + unsigned int flags; /* W: WQ_* flags */ union { struct cpu_workqueue_struct __percpu *pcpu; struct cpu_workqueue_struct *single; @@ -240,6 +240,7 @@ struct workqueue_struct { mayday_mask_t mayday_mask; /* cpus requesting rescue */ struct worker *rescuer; /* I: rescue worker */ + int nr_drainers; /* W: drain in progress */ int saved_max_active; /* W: saved cwq max_active */ const char *name; /* I: workqueue name */ #ifdef CONFIG_LOCKDEP @@ -990,7 +991,7 @@ static void __queue_work(unsigned int cpu, struct workqueue_struct *wq, debug_work_activate(work); /* if dying, only works from the same workqueue are allowed */ - if (unlikely(wq->flags & WQ_DYING) && + if (unlikely(wq->flags & WQ_DRAINING) && WARN_ON_ONCE(!is_chained_work(wq))) return; @@ -2381,6 +2382,54 @@ out_unlock: } EXPORT_SYMBOL_GPL(flush_workqueue); +/** + * drain_workqueue - drain a workqueue + * @wq: workqueue to drain + * + * Wait until the workqueue becomes empty. While draining is in progress, + * only chain queueing is allowed. IOW, only currently pending or running + * work items on @wq can queue further work items on it. @wq is flushed + * repeatedly until it becomes empty. The number of flushing is detemined + * by the depth of chaining and should be relatively short. Whine if it + * takes too long. + */ +void drain_workqueue(struct workqueue_struct *wq) +{ + unsigned int flush_cnt = 0; + unsigned int cpu; + + /* + * __queue_work() needs to test whether there are drainers, is much + * hotter than drain_workqueue() and already looks at @wq->flags. + * Use WQ_DRAINING so that queue doesn't have to check nr_drainers. + */ + spin_lock(&workqueue_lock); + if (!wq->nr_drainers++) + wq->flags |= WQ_DRAINING; + spin_unlock(&workqueue_lock); +reflush: + flush_workqueue(wq); + + for_each_cwq_cpu(cpu, wq) { + struct cpu_workqueue_struct *cwq = get_cwq(cpu, wq); + + if (!cwq->nr_active && list_empty(&cwq->delayed_works)) + continue; + + if (++flush_cnt == 10 || + (flush_cnt % 100 == 0 && flush_cnt <= 1000)) + pr_warning("workqueue %s: flush on destruction isn't complete after %u tries\n", + wq->name, flush_cnt); + goto reflush; + } + + spin_lock(&workqueue_lock); + if (!--wq->nr_drainers) + wq->flags &= ~WQ_DRAINING; + spin_unlock(&workqueue_lock); +} +EXPORT_SYMBOL_GPL(drain_workqueue); + static bool start_flush_work(struct work_struct *work, struct wq_barrier *barr, bool wait_executing) { @@ -3011,34 +3060,10 @@ EXPORT_SYMBOL_GPL(__alloc_workqueue_key); */ void destroy_workqueue(struct workqueue_struct *wq) { - unsigned int flush_cnt = 0; unsigned int cpu; - /* - * Mark @wq dying and drain all pending works. Once WQ_DYING is - * set, only chain queueing is allowed. IOW, only currently - * pending or running work items on @wq can queue further work - * items on it. @wq is flushed repeatedly until it becomes empty. - * The number of flushing is detemined by the depth of chaining and - * should be relatively short. Whine if it takes too long. - */ - wq->flags |= WQ_DYING; -reflush: - flush_workqueue(wq); - - for_each_cwq_cpu(cpu, wq) { - struct cpu_workqueue_struct *cwq = get_cwq(cpu, wq); - - if (!cwq->nr_active && list_empty(&cwq->delayed_works)) - continue; - - if (++flush_cnt == 10 || - (flush_cnt % 100 == 0 && flush_cnt <= 1000)) - printk(KERN_WARNING "workqueue %s: flush on " - "destruction isn't complete after %u tries\n", - wq->name, flush_cnt); - goto reflush; - } + /* drain it before proceeding with destruction */ + drain_workqueue(wq); /* * wq list is used to freeze wq, remove from list after -- cgit v1.2.3 From 988acec9ee9d8b38ca0d1187d904cb53588e8c64 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 25 May 2011 15:12:20 +0300 Subject: hwrng: fix spelling mistake in header comment Cc: Herbert Xu Cc: Matt Mackall Signed-off-by: Sasha Levin Signed-off-by: Jiri Kosina --- include/linux/hw_random.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h index 9bede7633f74..b4b0eef5fddf 100644 --- a/include/linux/hw_random.h +++ b/include/linux/hw_random.h @@ -25,7 +25,7 @@ * there is always data available. *OBSOLETE* * @data_read: Read data from the RNG device. * Returns the number of lower random bytes in "data". - * Must not be NULL. *OSOLETE* + * Must not be NULL. *OBSOLETE* * @read: New API. drivers can fill up to max bytes of data * into the buffer. The buffer is aligned for any type. * @priv: Private data, for use by the RNG driver. -- cgit v1.2.3 From a1b383870a28cfbd1657d4922c0fafc634a62ebd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 30 May 2011 11:14:13 -0600 Subject: virtio: add full three-clause BSD text to headers. It's unclear to me if it's important, but it's obviously causing my technical colleages some headaches and I'd hate such imprecision to slow virtio adoption. I've emailed this to all non-trivial contributors for approval, too. Signed-off-by: Rusty Russell Acked-by: Grant Likely Acked-by: Ryan Harper Acked-by: Anthony Liguori Acked-by: Eric Van Hensbergen Acked-by: john cooper Acked-by: Aneesh Kumar K.V Acked-by: Christian Borntraeger Acked-by: Fernando Luis Vazquez Cao --- include/linux/virtio_9p.h | 25 ++++++++++++++++++++++++- include/linux/virtio_balloon.h | 25 ++++++++++++++++++++++++- include/linux/virtio_blk.h | 25 ++++++++++++++++++++++++- include/linux/virtio_config.h | 25 ++++++++++++++++++++++++- include/linux/virtio_console.h | 26 +++++++++++++++++++++++++- include/linux/virtio_ids.h | 24 +++++++++++++++++++++++- include/linux/virtio_net.h | 25 ++++++++++++++++++++++++- include/linux/virtio_pci.h | 23 +++++++++++++++++++++++ include/linux/virtio_ring.h | 23 +++++++++++++++++++++++ 9 files changed, 214 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/virtio_9p.h b/include/linux/virtio_9p.h index e68b439b2860..277c4ad44e84 100644 --- a/include/linux/virtio_9p.h +++ b/include/linux/virtio_9p.h @@ -1,7 +1,30 @@ #ifndef _LINUX_VIRTIO_9P_H #define _LINUX_VIRTIO_9P_H /* This header is BSD licensed so anyone can use the definitions to implement - * compatible drivers/servers. */ + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #include #include #include diff --git a/include/linux/virtio_balloon.h b/include/linux/virtio_balloon.h index a50ecd1b81a2..652dc8bea921 100644 --- a/include/linux/virtio_balloon.h +++ b/include/linux/virtio_balloon.h @@ -1,7 +1,30 @@ #ifndef _LINUX_VIRTIO_BALLOON_H #define _LINUX_VIRTIO_BALLOON_H /* This header is BSD licensed so anyone can use the definitions to implement - * compatible drivers/servers. */ + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #include #include diff --git a/include/linux/virtio_blk.h b/include/linux/virtio_blk.h index 167720d695ed..e0edb40ca7aa 100644 --- a/include/linux/virtio_blk.h +++ b/include/linux/virtio_blk.h @@ -1,7 +1,30 @@ #ifndef _LINUX_VIRTIO_BLK_H #define _LINUX_VIRTIO_BLK_H /* This header is BSD licensed so anyone can use the definitions to implement - * compatible drivers/servers. */ + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #include #include #include diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index 800617b4ddd5..39c88c5ad19d 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -1,7 +1,30 @@ #ifndef _LINUX_VIRTIO_CONFIG_H #define _LINUX_VIRTIO_CONFIG_H /* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so - * anyone can use the definitions to implement compatible drivers/servers. */ + * anyone can use the definitions to implement compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ /* Virtio devices use a standardized configuration space to define their * features and pass configuration information, but each implementation can diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index e4d333543a33..bdf4b0034739 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h @@ -5,7 +5,31 @@ #include /* * This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so - * anyone can use the definitions to implement compatible drivers/servers. + * anyone can use the definitions to implement compatible drivers/servers: + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. * * Copyright (C) Red Hat, Inc., 2009, 2010, 2011 * Copyright (C) Amit Shah , 2009, 2010, 2011 diff --git a/include/linux/virtio_ids.h b/include/linux/virtio_ids.h index 06660c0a78d7..85bb0bb66ffc 100644 --- a/include/linux/virtio_ids.h +++ b/include/linux/virtio_ids.h @@ -5,7 +5,29 @@ * * This header is BSD licensed so anyone can use the definitions to implement * compatible drivers/servers. - */ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #define VIRTIO_ID_NET 1 /* virtio net */ #define VIRTIO_ID_BLOCK 2 /* virtio block */ diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 085e42298ce5..136040bba3e3 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -1,7 +1,30 @@ #ifndef _LINUX_VIRTIO_NET_H #define _LINUX_VIRTIO_NET_H /* This header is BSD licensed so anyone can use the definitions to implement - * compatible drivers/servers. */ + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #include #include #include diff --git a/include/linux/virtio_pci.h b/include/linux/virtio_pci.h index 9a3d7c48c622..ea66f3f60d63 100644 --- a/include/linux/virtio_pci.h +++ b/include/linux/virtio_pci.h @@ -11,6 +11,29 @@ * * This header is BSD licensed so anyone can use the definitions to implement * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #ifndef _LINUX_VIRTIO_PCI_H diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h index e4d144b132b5..a813e5d460eb 100644 --- a/include/linux/virtio_ring.h +++ b/include/linux/virtio_ring.h @@ -7,6 +7,29 @@ * This header is BSD licensed so anyone can use the definitions to implement * compatible drivers/servers. * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * * Copyright Rusty Russell IBM Corporation 2007. */ #include -- cgit v1.2.3 From 770b31a85e000b0194974922f238a30ade4246b6 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 20 May 2011 02:10:17 +0300 Subject: virtio: event index interface Define a new feature bit for the guest and host to utilize an event index (like Xen) instead if a flag bit to enable/disable interrupts and kicks. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- include/linux/virtio_ring.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h index a813e5d460eb..c4eef73deb3f 100644 --- a/include/linux/virtio_ring.h +++ b/include/linux/virtio_ring.h @@ -52,6 +52,12 @@ /* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28 +/* The Guest publishes the used index for which it expects an interrupt + * at the end of the avail ring. Host should ignore the avail->flags field. */ +/* The Host publishes the avail index for which it expects a kick + * at the end of the used ring. Guest should ignore the used->flags field. */ +#define VIRTIO_RING_F_EVENT_IDX 29 + /* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ struct vring_desc { /* Address (guest-physical). */ @@ -106,6 +112,7 @@ struct vring { * __u16 avail_flags; * __u16 avail_idx; * __u16 available[num]; + * __u16 used_event_idx; * * // Padding to the next align boundary. * char pad[]; @@ -114,8 +121,14 @@ struct vring { * __u16 used_flags; * __u16 used_idx; * struct vring_used_elem used[num]; + * __u16 avail_event_idx; * }; */ +/* We publish the used event index at the end of the available ring, and vice + * versa. They are at the end for backwards compatibility. */ +#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num]) +#define vring_avail_event(vr) (*(__u16 *)&(vr)->used->ring[(vr)->num]) + static inline void vring_init(struct vring *vr, unsigned int num, void *p, unsigned long align) { @@ -130,7 +143,7 @@ static inline unsigned vring_size(unsigned int num, unsigned long align) { return ((sizeof(struct vring_desc) * num + sizeof(__u16) * (2 + num) + align - 1) & ~(align - 1)) - + sizeof(__u16) * 2 + sizeof(struct vring_used_elem) * num; + + sizeof(__u16) * 3 + sizeof(struct vring_used_elem) * num; } #ifdef __KERNEL__ -- cgit v1.2.3 From bf7035bf20563a6cadcb9e870406e7b21daf5e30 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 20 May 2011 02:10:27 +0300 Subject: virtio ring: inline function to check for events With the new used_event and avail_event and features, both host and guest need similar logic to check whether events are enabled, so it helps to put the common code in the header. Note that Xen has similar logic for notification hold-off in include/xen/interface/io/ring.h with req_event and req_prod corresponding to event_idx + 1 and new_idx respectively. +1 comes from the fact that req_event and req_prod in Xen start at 1, while event index in virtio starts at 0. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- include/linux/virtio_ring.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include') diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h index c4eef73deb3f..4a32cb6da425 100644 --- a/include/linux/virtio_ring.h +++ b/include/linux/virtio_ring.h @@ -146,6 +146,20 @@ static inline unsigned vring_size(unsigned int num, unsigned long align) + sizeof(__u16) * 3 + sizeof(struct vring_used_elem) * num; } +/* The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX */ +/* Assuming a given event_idx value from the other size, if + * we have just incremented index from old to new_idx, + * should we trigger an event? */ +static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old) +{ + /* Note: Xen has similar logic for notification hold-off + * in include/xen/interface/io/ring.h with req_event and req_prod + * corresponding to event_idx + 1 and new_idx respectively. + * Note also that req_event and req_prod in Xen start at 1, + * event indexes in virtio start at 0. */ + return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old); +} + #ifdef __KERNEL__ #include struct virtio_device; -- cgit v1.2.3 From 7ab358c23cbf15cea08129cd722d1ce77433a94d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 20 May 2011 02:11:14 +0300 Subject: virtio: add api for delayed callbacks Add an API that tells the other side that callbacks should be delayed until a lot of work has been done. Implement using the new event_idx feature. Note: it might seem advantageous to let the drivers ask for a callback after a specific capacity has been reached. However, as a single head can free many entries in the descriptor table, we don't really have a clue about capacity until get_buf is called. The API is the simplest to implement at the moment, we'll see what kind of hints drivers can pass when there's more than one user of the feature. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_ring.c | 27 +++++++++++++++++++++++++++ include/linux/virtio.h | 9 +++++++++ 2 files changed, 36 insertions(+) (limited to 'include') diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index a5fadc40c448..68b9136847af 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -376,6 +376,33 @@ bool virtqueue_enable_cb(struct virtqueue *_vq) } EXPORT_SYMBOL_GPL(virtqueue_enable_cb); +bool virtqueue_enable_cb_delayed(struct virtqueue *_vq) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + u16 bufs; + + START_USE(vq); + + /* We optimistically turn back on interrupts, then check if there was + * more to do. */ + /* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to + * either clear the flags bit or point the event index at the next + * entry. Always do both to keep code simple. */ + vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; + /* TODO: tune this threshold */ + bufs = (u16)(vq->vring.avail->idx - vq->last_used_idx) * 3 / 4; + vring_used_event(&vq->vring) = vq->last_used_idx + bufs; + virtio_mb(); + if (unlikely((u16)(vq->vring.used->idx - vq->last_used_idx) > bufs)) { + END_USE(vq); + return false; + } + + END_USE(vq); + return true; +} +EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed); + void *virtqueue_detach_unused_buf(struct virtqueue *_vq) { struct vring_virtqueue *vq = to_vvq(_vq); diff --git a/include/linux/virtio.h b/include/linux/virtio.h index aff5b4f74041..710885749605 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -51,6 +51,13 @@ struct virtqueue { * This re-enables callbacks; it returns "false" if there are pending * buffers in the queue, to detect a possible race between the driver * checking for more work, and enabling callbacks. + * virtqueue_enable_cb_delayed: restart callbacks after disable_cb. + * vq: the struct virtqueue we're talking about. + * This re-enables callbacks but hints to the other side to delay + * interrupts until most of the available buffers have been processed; + * it returns "false" if there are many pending buffers in the queue, + * to detect a possible race between the driver checking for more work, + * and enabling callbacks. * virtqueue_detach_unused_buf: detach first unused buffer * vq: the struct virtqueue we're talking about. * Returns NULL or the "data" token handed to add_buf @@ -86,6 +93,8 @@ void virtqueue_disable_cb(struct virtqueue *vq); bool virtqueue_enable_cb(struct virtqueue *vq); +bool virtqueue_enable_cb_delayed(struct virtqueue *vq); + void *virtqueue_detach_unused_buf(struct virtqueue *vq); /** -- cgit v1.2.3 From a1706ac4c0201ea0143dc0db0659001b26ceeabb Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 30 May 2011 07:42:51 +0200 Subject: Revert "block: Remove extra discard_alignment from hd_struct." It was not a good idea to start dereferencing disk->queue from the fs sysfs strategy for displaying discard alignment. We ran into first a NULL pointer deref, and after fixing that we sometimes see unvalid disk->queue pointer values. Since discard is the only one of the bunch actually looking into the queue, just revert the change. This reverts commit 23ceb5b7719e9276d4fa72a3ecf94dd396755276. Conflicts: fs/partitions/check.c --- fs/partitions/check.c | 10 +++------- include/linux/genhd.h | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/fs/partitions/check.c b/fs/partitions/check.c index f82e762eeca2..d545e97d99c3 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -255,13 +255,7 @@ ssize_t part_discard_alignment_show(struct device *dev, struct device_attribute *attr, char *buf) { struct hd_struct *p = dev_to_part(dev); - struct gendisk *disk = dev_to_disk(dev); - unsigned int alignment = 0; - - if (disk->queue) - alignment = queue_limit_discard_alignment(&disk->queue->limits, - p->start_sect); - return sprintf(buf, "%u\n", alignment); + return sprintf(buf, "%u\n", p->discard_alignment); } ssize_t part_stat_show(struct device *dev, @@ -455,6 +449,8 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno, p->start_sect = start; p->alignment_offset = queue_limit_alignment_offset(&disk->queue->limits, start); + p->discard_alignment = + queue_limit_discard_alignment(&disk->queue->limits, start); p->nr_sects = len; p->partno = partno; p->policy = get_disk_ro(disk); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index b78956b3c2e7..300d7582006e 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -100,6 +100,7 @@ struct hd_struct { sector_t start_sect; sector_t nr_sects; sector_t alignment_offset; + unsigned int discard_alignment; struct device __dev; struct kobject *holder_dir; int policy, partno; -- cgit v1.2.3 From f21e0e81d81b649ad309cedc7226f1bed72982e0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 May 2011 08:12:40 +0800 Subject: regulator: Do bulk enables of regulators in parallel In order to reduce the impact of ramp times rather than enabling the regulators for a device in series use async tasks to run the actual enables. This means that the delays which the enables implement can all run in parallel, though it does mean that the order in which the supplies come on may be unstable. For super bonus fun points if any of the regulators are shared between multiple supplies on the same device (as is rather likely) then this will test our locking. Note that in this case we only delay once for each physical regulator so the threads shouldn't block each other while delaying. It'd be even nicer if we could coalesce writes to a shared enable registers in PMICs but that's definitely future work, and it may also be useful and is certainly more achievable to optimise out the parallelism if none of the regulators implement ramp delays. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/core.c | 32 ++++++++++++++++++++++++++------ include/linux/regulator/consumer.h | 3 +++ 2 files changed, 29 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d3e38790906e..7b38af90a012 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -2264,6 +2265,13 @@ err: } EXPORT_SYMBOL_GPL(regulator_bulk_get); +static void regulator_bulk_enable_async(void *data, async_cookie_t cookie) +{ + struct regulator_bulk_data *bulk = data; + + bulk->ret = regulator_enable(bulk->consumer); +} + /** * regulator_bulk_enable - enable multiple regulator consumers * @@ -2279,21 +2287,33 @@ EXPORT_SYMBOL_GPL(regulator_bulk_get); int regulator_bulk_enable(int num_consumers, struct regulator_bulk_data *consumers) { + LIST_HEAD(async_domain); int i; - int ret; + int ret = 0; + + for (i = 0; i < num_consumers; i++) + async_schedule_domain(regulator_bulk_enable_async, + &consumers[i], &async_domain); + + async_synchronize_full_domain(&async_domain); + /* If any consumer failed we need to unwind any that succeeded */ for (i = 0; i < num_consumers; i++) { - ret = regulator_enable(consumers[i].consumer); - if (ret != 0) + if (consumers[i].ret != 0) { + ret = consumers[i].ret; goto err; + } } return 0; err: - pr_err("Failed to enable %s: %d\n", consumers[i].supply, ret); - for (--i; i >= 0; --i) - regulator_disable(consumers[i].consumer); + for (i = 0; i < num_consumers; i++) + if (consumers[i].ret == 0) + regulator_disable(consumers[i].consumer); + else + pr_err("Failed to enable %s: %d\n", + consumers[i].supply, consumers[i].ret); return ret; } diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 9e87c1cb7270..26f6ea4444e3 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -122,6 +122,9 @@ struct regulator; struct regulator_bulk_data { const char *supply; struct regulator *consumer; + + /* Internal use */ + int ret; }; #if defined(CONFIG_REGULATOR) -- cgit v1.2.3 From ea9d6553b3b3044e7374774cc33bb1b2eee19dd3 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 31 May 2011 13:45:53 +0200 Subject: block: remove unwanted semicolons Since those defined functions require additional semicolon from the caller, they could cause potential syntax errors when used in if-else statements. Signed-off-by: Namhyung Kim Acked-by: Martin K. Petersen Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index ae9091a68480..1a23722e8878 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1282,8 +1282,8 @@ queue_max_integrity_segments(struct request_queue *q) #define blk_get_integrity(a) (0) #define blk_integrity_compare(a, b) (0) #define blk_integrity_register(a, b) (0) -#define blk_integrity_unregister(a) do { } while (0); -#define blk_queue_max_integrity_segments(a, b) do { } while (0); +#define blk_integrity_unregister(a) do { } while (0) +#define blk_queue_max_integrity_segments(a, b) do { } while (0) #define queue_max_integrity_segments(a) (0) #define blk_integrity_merge_rq(a, b, c) (0) #define blk_integrity_merge_bio(a, b, c) (0) -- cgit v1.2.3 From f339b9dc1f03591761d5d930800db24bc0eda1e1 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 31 May 2011 10:49:20 +0200 Subject: sched: Fix schedstat.nr_wakeups_migrate While looking over the code I found that with the ttwu rework the nr_wakeups_migrate test broke since we now switch cpus prior to calling ttwu_stat(), hence the test is always true. Cure this by passing the migration state in wake_flags. Also move the whole test under CONFIG_SMP, its hard to migrate tasks on UP :-) Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-pwwxl7gdqs5676f1d4cx6pj7@git.kernel.org Signed-off-by: Ingo Molnar --- include/linux/sched.h | 1 + kernel/sched.c | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 8da84b7bc1b8..483c1ed5bc4d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1063,6 +1063,7 @@ struct sched_domain; */ #define WF_SYNC 0x01 /* waker goes to sleep after wakup */ #define WF_FORK 0x02 /* child wakeup after fork */ +#define WF_MIGRATED 0x04 /* internal use, task got migrated */ #define ENQUEUE_WAKEUP 1 #define ENQUEUE_HEAD 2 diff --git a/kernel/sched.c b/kernel/sched.c index 49cc70b152cf..2fe98ed474da 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2447,6 +2447,10 @@ ttwu_stat(struct task_struct *p, int cpu, int wake_flags) } rcu_read_unlock(); } + + if (wake_flags & WF_MIGRATED) + schedstat_inc(p, se.statistics.nr_wakeups_migrate); + #endif /* CONFIG_SMP */ schedstat_inc(rq, ttwu_count); @@ -2455,9 +2459,6 @@ ttwu_stat(struct task_struct *p, int cpu, int wake_flags) if (wake_flags & WF_SYNC) schedstat_inc(p, se.statistics.nr_wakeups_sync); - if (cpu != task_cpu(p)) - schedstat_inc(p, se.statistics.nr_wakeups_migrate); - #endif /* CONFIG_SCHEDSTATS */ } @@ -2675,8 +2676,10 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) p->sched_class->task_waking(p); cpu = select_task_rq(p, SD_BALANCE_WAKE, wake_flags); - if (task_cpu(p) != cpu) + if (task_cpu(p) != cpu) { + wake_flags |= WF_MIGRATED; set_task_cpu(p, cpu); + } #endif /* CONFIG_SMP */ ttwu_queue(p, cpu); -- cgit v1.2.3 From a000c01e60e40e15304ffe48fff051d17a7bea91 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 29 May 2011 23:23:36 +0000 Subject: sctp: stop pending timers and purge queues when peer restart asoc If the peer restart the asoc, we should not only fail any unsent/unacked data, but also stop the T3-rtx, SACK, T4-rto timers, and teardown ASCONF queues. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- include/net/sctp/command.h | 1 + include/net/sctp/structs.h | 2 +- net/sctp/associola.c | 23 ++++++++++++++--------- net/sctp/sm_sideeffect.c | 3 +++ net/sctp/sm_statefuns.c | 14 ++++++++++++-- 5 files changed, 31 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 2b447646ce4b..dd6847e5d6e4 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -107,6 +107,7 @@ typedef enum { SCTP_CMD_UPDATE_INITTAG, /* Update peer inittag */ SCTP_CMD_SEND_MSG, /* Send the whole use message */ SCTP_CMD_SEND_NEXT_ASCONF, /* Send the next ASCONF after ACK */ + SCTP_CMD_PURGE_ASCONF_QUEUE, /* Purge all asconf queues.*/ SCTP_CMD_LAST } sctp_verb_t; diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 795f4886e111..7df327a6d564 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1993,7 +1993,7 @@ void sctp_assoc_clean_asconf_ack_cache(const struct sctp_association *asoc); struct sctp_chunk *sctp_assoc_lookup_asconf_ack( const struct sctp_association *asoc, __be32 serial); - +void sctp_asconf_queue_teardown(struct sctp_association *asoc); int sctp_cmp_addr_exact(const union sctp_addr *ss1, const union sctp_addr *ss2); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 525f97c467e9..4a62888f2e43 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -444,15 +444,7 @@ void sctp_association_free(struct sctp_association *asoc) asoc->peer.transport_count = 0; - /* Free any cached ASCONF_ACK chunk. */ - sctp_assoc_free_asconf_acks(asoc); - - /* Free the ASCONF queue. */ - sctp_assoc_free_asconf_queue(asoc); - - /* Free any cached ASCONF chunk. */ - if (asoc->addip_last_asconf) - sctp_chunk_free(asoc->addip_last_asconf); + sctp_asconf_queue_teardown(asoc); /* AUTH - Free the endpoint shared keys */ sctp_auth_destroy_keys(&asoc->endpoint_shared_keys); @@ -1646,3 +1638,16 @@ struct sctp_chunk *sctp_assoc_lookup_asconf_ack( return NULL; } + +void sctp_asconf_queue_teardown(struct sctp_association *asoc) +{ + /* Free any cached ASCONF_ACK chunk. */ + sctp_assoc_free_asconf_acks(asoc); + + /* Free the ASCONF queue. */ + sctp_assoc_free_asconf_queue(asoc); + + /* Free any cached ASCONF chunk. */ + if (asoc->addip_last_asconf) + sctp_chunk_free(asoc->addip_last_asconf); +} diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index d612ca1ca6c0..534c2e5feb05 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1670,6 +1670,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_SEND_NEXT_ASCONF: sctp_cmd_send_asconf(asoc); break; + case SCTP_CMD_PURGE_ASCONF_QUEUE: + sctp_asconf_queue_teardown(asoc); + break; default: pr_warn("Impossible command: %u, %p\n", cmd->verb, cmd->obj.ptr); diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 7f4a4f8368ee..a297283154d5 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -1718,11 +1718,21 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep, return SCTP_DISPOSITION_CONSUME; } - /* For now, fail any unsent/unacked data. Consider the optional - * choice of resending of this data. + /* For now, stop pending T3-rtx and SACK timers, fail any unsent/unacked + * data. Consider the optional choice of resending of this data. */ + sctp_add_cmd_sf(commands, SCTP_CMD_T3_RTX_TIMERS_STOP, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_SACK)); sctp_add_cmd_sf(commands, SCTP_CMD_PURGE_OUTQUEUE, SCTP_NULL()); + /* Stop pending T4-rto timer, teardown ASCONF queue, ASCONF-ACK queue + * and ASCONF-ACK cache. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); + sctp_add_cmd_sf(commands, SCTP_CMD_PURGE_ASCONF_QUEUE, SCTP_NULL()); + repl = sctp_make_cookie_ack(new_asoc, chunk); if (!repl) goto nomem; -- cgit v1.2.3 From 63da029015b5255915cd6d61f19ffc276ad4635d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 23 May 2011 11:37:09 -0700 Subject: mtd: fix physmap.h warnings Fix build warnings in physmap.h: include/linux/mtd/physmap.h:25: warning: 'struct platform_device' declared inside parameter list include/linux/mtd/physmap.h:25: warning: its scope is only this definition or declaration, which is probably not what you want include/linux/mtd/physmap.h:26: warning: 'struct platform_device' declared inside parameter list include/linux/mtd/physmap.h:27: warning: 'struct platform_device' declared inside parameter list Signed-off-by: Randy Dunlap Signed-off-by: David Woodhouse --- include/linux/mtd/physmap.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h index d40bfa1d9c91..e5f21d293c70 100644 --- a/include/linux/mtd/physmap.h +++ b/include/linux/mtd/physmap.h @@ -19,6 +19,7 @@ #include struct map_info; +struct platform_device; struct physmap_flash_data { unsigned int width; -- cgit v1.2.3 From 6dd9a7c73761a8a5f5475d5cfdc15368a0f4c06d Mon Sep 17 00:00:00 2001 From: Youquan Song Date: Wed, 25 May 2011 19:13:49 +0100 Subject: intel-iommu: Enable super page (2MiB, 1GiB, etc.) support There are no externally-visible changes with this. In the loop in the internal __domain_mapping() function, we simply detect if we are mapping: - size >= 2MiB, and - virtual address aligned to 2MiB, and - physical address aligned to 2MiB, and - on hardware that supports superpages. (and likewise for larger superpages). We automatically use a superpage for such mappings. We never have to worry about *breaking* superpages, since we trust that we will always *unmap* the same range that was mapped. So all we need to do is ensure that dma_pte_clear_range() will also cope with superpages. Adjust pfn_to_dma_pte() to take a superpage 'level' as an argument, so it can return a PTE at the appropriate level rather than always extending the page tables all the way down to level 1. Again, this is simplified by the fact that we should never encounter existing small pages when we're creating a mapping; any old mapping that used the same virtual range will have been entirely removed and its obsolete page tables freed. Provide an 'intel_iommu=sp_off' argument on the command line as a chicken bit. Not that it should ever be required. == The original commit seen in the iommu-2.6.git was Youquan's implementation (and completion) of my own half-baked code which I'd typed into an email. Followed by half a dozen subsequent 'fixes'. I've taken the unusual step of rewriting history and collapsing the original commits in order to keep the main history simpler, and make life easier for the people who are going to have to backport this to older kernels. And also so I can give it a more coherent commit comment which (hopefully) gives a better explanation of what's going on. The original sequence of commits leading to identical code was: Youquan Song (3): intel-iommu: super page support intel-iommu: Fix superpage alignment calculation error intel-iommu: Fix superpage level calculation error in dma_pfn_level_pte() David Woodhouse (4): intel-iommu: Precalculate superpage support for dmar_domain intel-iommu: Fix hardware_largepage_caps() intel-iommu: Fix inappropriate use of superpages in __domain_mapping() intel-iommu: Fix phys_pfn in __domain_mapping for sglist pages Signed-off-by: Youquan Song Signed-off-by: David Woodhouse --- Documentation/kernel-parameters.txt | 5 +- drivers/pci/intel-iommu.c | 157 +++++++++++++++++++++++++++++++----- include/linux/dma_remapping.h | 4 + 3 files changed, 147 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index cc85a9278190..d005487c1a22 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -999,7 +999,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. With this option on every unmap_single operation will result in a hardware IOTLB flush operation as opposed to batching them for performance. - + sp_off [Default Off] + By default, super page will be supported if Intel IOMMU + has the capability. With this option, super page will + not be supported. intremap= [X86-64, Intel-IOMMU] Format: { on (default) | off | nosid } on enable Interrupt Remapping (default) diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 395f253c0494..e6fe1994f9d3 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -115,6 +115,11 @@ static inline unsigned long align_to_level(unsigned long pfn, int level) return (pfn + level_size(level) - 1) & level_mask(level); } +static inline unsigned long lvl_to_nr_pages(unsigned int lvl) +{ + return 1 << ((lvl - 1) * LEVEL_STRIDE); +} + /* VT-d pages must always be _smaller_ than MM pages. Otherwise things are never going to work. */ static inline unsigned long dma_to_mm_pfn(unsigned long dma_pfn) @@ -343,6 +348,9 @@ struct dmar_domain { int iommu_coherency;/* indicate coherency of iommu access */ int iommu_snooping; /* indicate snooping control feature*/ int iommu_count; /* reference count of iommu */ + int iommu_superpage;/* Level of superpages supported: + 0 == 4KiB (no superpages), 1 == 2MiB, + 2 == 1GiB, 3 == 512GiB, 4 == 1TiB */ spinlock_t iommu_lock; /* protect iommu set in domain */ u64 max_addr; /* maximum mapped address */ }; @@ -392,6 +400,7 @@ int dmar_disabled = 1; static int dmar_map_gfx = 1; static int dmar_forcedac; static int intel_iommu_strict; +static int intel_iommu_superpage = 1; #define DUMMY_DEVICE_DOMAIN_INFO ((struct device_domain_info *)(-1)) static DEFINE_SPINLOCK(device_domain_lock); @@ -422,6 +431,10 @@ static int __init intel_iommu_setup(char *str) printk(KERN_INFO "Intel-IOMMU: disable batched IOTLB flush\n"); intel_iommu_strict = 1; + } else if (!strncmp(str, "sp_off", 6)) { + printk(KERN_INFO + "Intel-IOMMU: disable supported super page\n"); + intel_iommu_superpage = 0; } str += strcspn(str, ","); @@ -560,11 +573,32 @@ static void domain_update_iommu_snooping(struct dmar_domain *domain) } } +static void domain_update_iommu_superpage(struct dmar_domain *domain) +{ + int i, mask = 0xf; + + if (!intel_iommu_superpage) { + domain->iommu_superpage = 0; + return; + } + + domain->iommu_superpage = 4; /* 1TiB */ + + for_each_set_bit(i, &domain->iommu_bmp, g_num_of_iommus) { + mask |= cap_super_page_val(g_iommus[i]->cap); + if (!mask) { + break; + } + } + domain->iommu_superpage = fls(mask); +} + /* Some capabilities may be different across iommus */ static void domain_update_iommu_cap(struct dmar_domain *domain) { domain_update_iommu_coherency(domain); domain_update_iommu_snooping(domain); + domain_update_iommu_superpage(domain); } static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn) @@ -694,23 +728,31 @@ out: } static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain, - unsigned long pfn) + unsigned long pfn, int large_level) { int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; struct dma_pte *parent, *pte = NULL; int level = agaw_to_level(domain->agaw); - int offset; + int offset, target_level; BUG_ON(!domain->pgd); BUG_ON(addr_width < BITS_PER_LONG && pfn >> addr_width); parent = domain->pgd; + /* Search pte */ + if (!large_level) + target_level = 1; + else + target_level = large_level; + while (level > 0) { void *tmp_page; offset = pfn_level_offset(pfn, level); pte = &parent[offset]; - if (level == 1) + if (!large_level && (pte->val & DMA_PTE_LARGE_PAGE)) + break; + if (level == target_level) break; if (!dma_pte_present(pte)) { @@ -738,10 +780,11 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain, return pte; } + /* return address's pte at specific level */ static struct dma_pte *dma_pfn_level_pte(struct dmar_domain *domain, unsigned long pfn, - int level) + int level, int *large_page) { struct dma_pte *parent, *pte = NULL; int total = agaw_to_level(domain->agaw); @@ -754,8 +797,16 @@ static struct dma_pte *dma_pfn_level_pte(struct dmar_domain *domain, if (level == total) return pte; - if (!dma_pte_present(pte)) + if (!dma_pte_present(pte)) { + *large_page = total; break; + } + + if (pte->val & DMA_PTE_LARGE_PAGE) { + *large_page = total; + return pte; + } + parent = phys_to_virt(dma_pte_addr(pte)); total--; } @@ -768,6 +819,7 @@ static void dma_pte_clear_range(struct dmar_domain *domain, unsigned long last_pfn) { int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; + unsigned int large_page = 1; struct dma_pte *first_pte, *pte; BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width); @@ -776,14 +828,15 @@ static void dma_pte_clear_range(struct dmar_domain *domain, /* we don't need lock here; nobody else touches the iova range */ do { - first_pte = pte = dma_pfn_level_pte(domain, start_pfn, 1); + large_page = 1; + first_pte = pte = dma_pfn_level_pte(domain, start_pfn, 1, &large_page); if (!pte) { - start_pfn = align_to_level(start_pfn + 1, 2); + start_pfn = align_to_level(start_pfn + 1, large_page + 1); continue; } - do { + do { dma_clear_pte(pte); - start_pfn++; + start_pfn += lvl_to_nr_pages(large_page); pte++; } while (start_pfn <= last_pfn && !first_pte_in_page(pte)); @@ -803,6 +856,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, int total = agaw_to_level(domain->agaw); int level; unsigned long tmp; + int large_page = 2; BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width); BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width); @@ -818,7 +872,10 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, return; do { - first_pte = pte = dma_pfn_level_pte(domain, tmp, level); + large_page = level; + first_pte = pte = dma_pfn_level_pte(domain, tmp, level, &large_page); + if (large_page > level) + level = large_page + 1; if (!pte) { tmp = align_to_level(tmp + 1, level + 1); continue; @@ -1402,6 +1459,7 @@ static int domain_init(struct dmar_domain *domain, int guest_width) else domain->iommu_snooping = 0; + domain->iommu_superpage = fls(cap_super_page_val(iommu->cap)); domain->iommu_count = 1; domain->nid = iommu->node; @@ -1657,6 +1715,34 @@ static inline unsigned long aligned_nrpages(unsigned long host_addr, return PAGE_ALIGN(host_addr + size) >> VTD_PAGE_SHIFT; } +/* Return largest possible superpage level for a given mapping */ +static inline int hardware_largepage_caps(struct dmar_domain *domain, + unsigned long iov_pfn, + unsigned long phy_pfn, + unsigned long pages) +{ + int support, level = 1; + unsigned long pfnmerge; + + support = domain->iommu_superpage; + + /* To use a large page, the virtual *and* physical addresses + must be aligned to 2MiB/1GiB/etc. Lower bits set in either + of them will mean we have to use smaller pages. So just + merge them and check both at once. */ + pfnmerge = iov_pfn | phy_pfn; + + while (support && !(pfnmerge & ~VTD_STRIDE_MASK)) { + pages >>= VTD_STRIDE_SHIFT; + if (!pages) + break; + pfnmerge >>= VTD_STRIDE_SHIFT; + level++; + support--; + } + return level; +} + static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, struct scatterlist *sg, unsigned long phys_pfn, unsigned long nr_pages, int prot) @@ -1665,6 +1751,8 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, phys_addr_t uninitialized_var(pteval); int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; unsigned long sg_res; + unsigned int largepage_lvl = 0; + unsigned long lvl_pages = 0; BUG_ON(addr_width < BITS_PER_LONG && (iov_pfn + nr_pages - 1) >> addr_width); @@ -1680,7 +1768,7 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, pteval = ((phys_addr_t)phys_pfn << VTD_PAGE_SHIFT) | prot; } - while (nr_pages--) { + while (nr_pages > 0) { uint64_t tmp; if (!sg_res) { @@ -1688,11 +1776,21 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, sg->dma_address = ((dma_addr_t)iov_pfn << VTD_PAGE_SHIFT) + sg->offset; sg->dma_length = sg->length; pteval = page_to_phys(sg_page(sg)) | prot; + phys_pfn = pteval >> VTD_PAGE_SHIFT; } + if (!pte) { - first_pte = pte = pfn_to_dma_pte(domain, iov_pfn); + largepage_lvl = hardware_largepage_caps(domain, iov_pfn, phys_pfn, sg_res); + + first_pte = pte = pfn_to_dma_pte(domain, iov_pfn, largepage_lvl); if (!pte) return -ENOMEM; + /* It is large page*/ + if (largepage_lvl > 1) + pteval |= DMA_PTE_LARGE_PAGE; + else + pteval &= ~(uint64_t)DMA_PTE_LARGE_PAGE; + } /* We don't need lock here, nobody else * touches the iova range @@ -1708,16 +1806,38 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, } WARN_ON(1); } + + lvl_pages = lvl_to_nr_pages(largepage_lvl); + + BUG_ON(nr_pages < lvl_pages); + BUG_ON(sg_res < lvl_pages); + + nr_pages -= lvl_pages; + iov_pfn += lvl_pages; + phys_pfn += lvl_pages; + pteval += lvl_pages * VTD_PAGE_SIZE; + sg_res -= lvl_pages; + + /* If the next PTE would be the first in a new page, then we + need to flush the cache on the entries we've just written. + And then we'll need to recalculate 'pte', so clear it and + let it get set again in the if (!pte) block above. + + If we're done (!nr_pages) we need to flush the cache too. + + Also if we've been setting superpages, we may need to + recalculate 'pte' and switch back to smaller pages for the + end of the mapping, if the trailing size is not enough to + use another superpage (i.e. sg_res < lvl_pages). */ pte++; - if (!nr_pages || first_pte_in_page(pte)) { + if (!nr_pages || first_pte_in_page(pte) || + (largepage_lvl > 1 && sg_res < lvl_pages)) { domain_flush_cache(domain, first_pte, (void *)pte - (void *)first_pte); pte = NULL; } - iov_pfn++; - pteval += VTD_PAGE_SIZE; - sg_res--; - if (!sg_res) + + if (!sg_res && nr_pages) sg = sg_next(sg); } return 0; @@ -3527,6 +3647,7 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width) domain->iommu_count = 0; domain->iommu_coherency = 0; domain->iommu_snooping = 0; + domain->iommu_superpage = 0; domain->max_addr = 0; domain->nid = -1; @@ -3742,7 +3863,7 @@ static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, struct dma_pte *pte; u64 phys = 0; - pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT); + pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, 0); if (pte) phys = dma_pte_addr(pte); diff --git a/include/linux/dma_remapping.h b/include/linux/dma_remapping.h index 5619f8522738..bbd8661b3473 100644 --- a/include/linux/dma_remapping.h +++ b/include/linux/dma_remapping.h @@ -9,8 +9,12 @@ #define VTD_PAGE_MASK (((u64)-1) << VTD_PAGE_SHIFT) #define VTD_PAGE_ALIGN(addr) (((addr) + VTD_PAGE_SIZE - 1) & VTD_PAGE_MASK) +#define VTD_STRIDE_SHIFT (9) +#define VTD_STRIDE_MASK (((u64)-1) << VTD_STRIDE_SHIFT) + #define DMA_PTE_READ (1) #define DMA_PTE_WRITE (2) +#define DMA_PTE_LARGE_PAGE (1 << 7) #define DMA_PTE_SNP (1 << 11) #define CONTEXT_TT_MULTI_LEVEL 0 -- cgit v1.2.3 From 6e3ea0e711b1c4dca3c4f87ef0ab5c896c940c81 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 2 May 2011 16:21:03 -0300 Subject: [media] v4l: Fix media_entity_to_video_device macro argument name The name 'entity' is used twice in the macro body, once as the macro argument, and once as a structure field name. This breaks compilation if the macro is called with its argument not named 'entity'. Fix this by renaming the macro argument '__e'. This should avoid namespace clashes. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-dev.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 93e96fb93452..c7c40f1d2624 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -128,8 +128,8 @@ struct video_device struct mutex *lock; }; -#define media_entity_to_video_device(entity) \ - container_of(entity, struct video_device, entity) +#define media_entity_to_video_device(__e) \ + container_of(__e, struct video_device, entity) /* dev to video-device */ #define to_video_device(cd) container_of(cd, struct video_device, dev) -- cgit v1.2.3 From 333ba7325213f0a09dfa5ceeddb056d6ad74b3b5 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 29 May 2011 15:53:20 +0300 Subject: cfg80211: don't drop p2p probe responses Commit 0a35d36 ("cfg80211: Use capability info to detect mesh beacons") assumed that probe response with both ESS and IBSS bits cleared means that the frame was sent by a mesh sta. However, these capabilities are also being used in the p2p_find phase, and the mesh-validation broke it. Rename the WLAN_CAPABILITY_IS_MBSS macro, and verify that mesh ies exist before assuming this frame was sent by a mesh sta. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 8 ++++++-- net/wireless/scan.c | 43 ++++++++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b2eee5879883..bf56b6f78270 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1003,8 +1003,12 @@ struct ieee80211_ht_info { #define WLAN_CAPABILITY_ESS (1<<0) #define WLAN_CAPABILITY_IBSS (1<<1) -/* A mesh STA sets the ESS and IBSS capability bits to zero */ -#define WLAN_CAPABILITY_IS_MBSS(cap) \ +/* + * A mesh STA sets the ESS and IBSS capability bits to zero. + * however, this holds true for p2p probe responses (in the p2p_find + * phase) as well. + */ +#define WLAN_CAPABILITY_IS_STA_BSS(cap) \ (!((cap) & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS))) #define WLAN_CAPABILITY_CF_POLLABLE (1<<2) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 73a441d237b5..7a6c67667d70 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -267,13 +267,35 @@ static bool is_bss(struct cfg80211_bss *a, return memcmp(ssidie + 2, ssid, ssid_len) == 0; } +static bool is_mesh_bss(struct cfg80211_bss *a) +{ + const u8 *ie; + + if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) + return false; + + ie = cfg80211_find_ie(WLAN_EID_MESH_ID, + a->information_elements, + a->len_information_elements); + if (!ie) + return false; + + ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, + a->information_elements, + a->len_information_elements); + if (!ie) + return false; + + return true; +} + static bool is_mesh(struct cfg80211_bss *a, const u8 *meshid, size_t meshidlen, const u8 *meshcfg) { const u8 *ie; - if (!WLAN_CAPABILITY_IS_MBSS(a->capability)) + if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_ID, @@ -311,7 +333,7 @@ static int cmp_bss(struct cfg80211_bss *a, if (a->channel != b->channel) return b->channel->center_freq - a->channel->center_freq; - if (WLAN_CAPABILITY_IS_MBSS(a->capability | b->capability)) { + if (is_mesh_bss(a) && is_mesh_bss(b)) { r = cmp_ies(WLAN_EID_MESH_ID, a->information_elements, a->len_information_elements, @@ -457,7 +479,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *res) { struct cfg80211_internal_bss *found = NULL; - const u8 *meshid, *meshcfg; /* * The reference to "res" is donated to this function. @@ -470,22 +491,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, res->ts = jiffies; - if (WLAN_CAPABILITY_IS_MBSS(res->pub.capability)) { - /* must be mesh, verify */ - meshid = cfg80211_find_ie(WLAN_EID_MESH_ID, - res->pub.information_elements, - res->pub.len_information_elements); - meshcfg = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, - res->pub.information_elements, - res->pub.len_information_elements); - if (!meshid || !meshcfg || - meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) { - /* bogus mesh */ - kref_put(&res->ref, bss_release); - return NULL; - } - } - spin_lock_bh(&dev->bss_lock); found = rb_find_bss(dev, res); -- cgit v1.2.3 From 1bdcd095e39a789135f8638a2ff76f74e3071d46 Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Wed, 18 May 2011 11:40:22 +0200 Subject: bcma: add IRQ number and pointer to DMA dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: John W. Linville --- drivers/bcma/main.c | 2 ++ include/linux/bcma/bcma.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index be52344ed19d..a2f6b1879273 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -89,6 +89,8 @@ static int bcma_register_cores(struct bcma_bus *bus) switch (bus->hosttype) { case BCMA_HOSTTYPE_PCI: core->dev.parent = &bus->host_pci->dev; + core->dma_dev = &bus->host_pci->dev; + core->irq = bus->host_pci->irq; break; case BCMA_HOSTTYPE_NONE: case BCMA_HOSTTYPE_SDIO: diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 08763e4e848f..8b0cef9fd692 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -117,6 +117,8 @@ struct bcma_device { struct bcma_device_id id; struct device dev; + struct device *dma_dev; + unsigned int irq; bool dev_registered; u8 core_index; -- cgit v1.2.3 From 9d75ef0f8f6d2e31ed940b3057a42a25f07076fb Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Fri, 20 May 2011 03:27:06 +0200 Subject: bcma: host pci: implement block R/W operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: John W. Linville --- drivers/bcma/Kconfig | 5 +++++ drivers/bcma/host_pci.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/bcma/bcma.h | 18 ++++++++++++++++ 3 files changed, 75 insertions(+) (limited to 'include') diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig index 353781b5b78b..83e9adf46441 100644 --- a/drivers/bcma/Kconfig +++ b/drivers/bcma/Kconfig @@ -13,6 +13,11 @@ config BCMA Bus driver for Broadcom specific Advanced Microcontroller Bus Architecture. +# Support for Block-I/O. SELECT this from the driver that needs it. +config BCMA_BLOCKIO + bool + depends on BCMA + config BCMA_HOST_PCI_POSSIBLE bool depends on BCMA && PCI = y diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index ffd8797faf4f..279bf50f6d8e 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -64,6 +64,54 @@ static void bcma_host_pci_write32(struct bcma_device *core, u16 offset, iowrite32(value, core->bus->mmio + offset); } +#ifdef CONFIG_BCMA_BLOCKIO +void bcma_host_pci_block_read(struct bcma_device *core, void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + void __iomem *addr = core->bus->mmio + offset; + if (core->bus->mapped_core != core) + bcma_host_pci_switch_core(core); + switch (reg_width) { + case sizeof(u8): + ioread8_rep(addr, buffer, count); + break; + case sizeof(u16): + WARN_ON(count & 1); + ioread16_rep(addr, buffer, count >> 1); + break; + case sizeof(u32): + WARN_ON(count & 3); + ioread32_rep(addr, buffer, count >> 2); + break; + default: + WARN_ON(1); + } +} + +void bcma_host_pci_block_write(struct bcma_device *core, const void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + void __iomem *addr = core->bus->mmio + offset; + if (core->bus->mapped_core != core) + bcma_host_pci_switch_core(core); + switch (reg_width) { + case sizeof(u8): + iowrite8_rep(addr, buffer, count); + break; + case sizeof(u16): + WARN_ON(count & 1); + iowrite16_rep(addr, buffer, count >> 1); + break; + case sizeof(u32): + WARN_ON(count & 3); + iowrite32_rep(addr, buffer, count >> 2); + break; + default: + WARN_ON(1); + } +} +#endif + static u32 bcma_host_pci_aread32(struct bcma_device *core, u16 offset) { if (core->bus->mapped_core != core) @@ -86,6 +134,10 @@ const struct bcma_host_ops bcma_host_pci_ops = { .write8 = bcma_host_pci_write8, .write16 = bcma_host_pci_write16, .write32 = bcma_host_pci_write32, +#ifdef CONFIG_BCMA_BLOCKIO + .block_read = bcma_host_pci_block_read, + .block_write = bcma_host_pci_block_write, +#endif .aread32 = bcma_host_pci_aread32, .awrite32 = bcma_host_pci_awrite32, }; diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 8b0cef9fd692..27a27a79bea3 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -31,6 +31,12 @@ struct bcma_host_ops { void (*write8)(struct bcma_device *core, u16 offset, u8 value); void (*write16)(struct bcma_device *core, u16 offset, u16 value); void (*write32)(struct bcma_device *core, u16 offset, u32 value); +#ifdef CONFIG_BCMA_BLOCKIO + void (*block_read)(struct bcma_device *core, void *buffer, + size_t count, u16 offset, u8 reg_width); + void (*block_write)(struct bcma_device *core, const void *buffer, + size_t count, u16 offset, u8 reg_width); +#endif /* Agent ops */ u32 (*aread32)(struct bcma_device *core, u16 offset); void (*awrite32)(struct bcma_device *core, u16 offset, u32 value); @@ -210,6 +216,18 @@ void bcma_write32(struct bcma_device *core, u16 offset, u32 value) { core->bus->ops->write32(core, offset, value); } +#ifdef CONFIG_BCMA_BLOCKIO +extern inline void bcma_block_read(struct bcma_device *core, void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + core->bus->ops->block_read(core, buffer, count, offset, reg_width); +} +extern inline void bcma_block_write(struct bcma_device *core, const void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + core->bus->ops->block_write(core, buffer, count, offset, reg_width); +} +#endif extern inline u32 bcma_aread32(struct bcma_device *core, u16 offset) { return core->bus->ops->aread32(core, offset); -- cgit v1.2.3 From 71063f0e8939b5b6ea5121faed47987e094ef018 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Fri, 20 May 2011 09:05:54 -0700 Subject: nl80211: add testmode dump support This adds dump support to testmode. The testmode dump support in nl80211 requires using two of the six cb->args, the rest can be used by the driver to figure out where the dump position is at or to store other data across invocations. Signed-off-by: Wey-Yi Guy Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 11 +++++++ include/net/mac80211.h | 4 +++ net/mac80211/cfg.c | 14 ++++++++ net/wireless/nl80211.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0589f554788a..6cb2543a2ee1 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1284,6 +1284,12 @@ struct cfg80211_wowlan { * frame on another channel * * @testmode_cmd: run a test mode command + * @testmode_dump: Implement a test mode dump. The cb->args[2] and up may be + * used by the function, but 0 and 1 must not be touched. Additionally, + * return error codes other than -ENOBUFS and -ENOENT will terminate the + * dump and return to userspace with an error, so be careful. If any data + * was passed in from userspace then the data/len arguments will be present + * and point to the data contained in %NL80211_ATTR_TESTDATA. * * @set_bitrate_mask: set the bitrate mask configuration * @@ -1433,6 +1439,9 @@ struct cfg80211_ops { #ifdef CONFIG_NL80211_TESTMODE int (*testmode_cmd)(struct wiphy *wiphy, void *data, int len); + int (*testmode_dump)(struct wiphy *wiphy, struct sk_buff *skb, + struct netlink_callback *cb, + void *data, int len); #endif int (*set_bitrate_mask)(struct wiphy *wiphy, @@ -2849,8 +2858,10 @@ struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp); #define CFG80211_TESTMODE_CMD(cmd) .testmode_cmd = (cmd), +#define CFG80211_TESTMODE_DUMP(cmd) .testmode_dump = (cmd), #else #define CFG80211_TESTMODE_CMD(cmd) +#define CFG80211_TESTMODE_DUMP(cmd) #endif /** diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e6d6a66a8f71..b250c6303d6f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1816,6 +1816,7 @@ enum ieee80211_ampdu_mlme_action { * * @testmode_cmd: Implement a cfg80211 test mode command. * The callback can sleep. + * @testmode_dump: Implement a cfg80211 test mode dump. The callback can sleep. * * @flush: Flush all pending frames from the hardware queue, making sure * that the hardware queues are empty. If the parameter @drop is set @@ -1936,6 +1937,9 @@ struct ieee80211_ops { void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class); #ifdef CONFIG_NL80211_TESTMODE int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len); + int (*testmode_dump)(struct ieee80211_hw *hw, struct sk_buff *skb, + struct netlink_callback *cb, + void *data, int len); #endif void (*flush)(struct ieee80211_hw *hw, bool drop); void (*channel_switch)(struct ieee80211_hw *hw, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index be70c70d3f5b..6e56c6ee7ccd 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1554,6 +1554,19 @@ static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) return local->ops->testmode_cmd(&local->hw, data, len); } + +static int ieee80211_testmode_dump(struct wiphy *wiphy, + struct sk_buff *skb, + struct netlink_callback *cb, + void *data, int len) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + + if (!local->ops->testmode_dump) + return -EOPNOTSUPP; + + return local->ops->testmode_dump(&local->hw, skb, cb, data, len); +} #endif int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, @@ -2134,6 +2147,7 @@ struct cfg80211_ops mac80211_config_ops = { .set_wds_peer = ieee80211_set_wds_peer, .rfkill_poll = ieee80211_rfkill_poll, CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) + CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump) .set_power_mgmt = ieee80211_set_power_mgmt, .set_bitrate_mask = ieee80211_set_bitrate_mask, .remain_on_channel = ieee80211_remain_on_channel, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 88a565f130a5..6d0d44b19ee6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4361,6 +4361,93 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) return err; } +static int nl80211_testmode_dump(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct cfg80211_registered_device *dev; + int err; + long phy_idx; + void *data = NULL; + int data_len = 0; + + if (cb->args[0]) { + /* + * 0 is a valid index, but not valid for args[0], + * so we need to offset by 1. + */ + phy_idx = cb->args[0] - 1; + } else { + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, + nl80211_fam.attrbuf, nl80211_fam.maxattr, + nl80211_policy); + if (err) + return err; + if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]) + return -EINVAL; + phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]); + if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]) + cb->args[1] = + (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]; + } + + if (cb->args[1]) { + data = nla_data((void *)cb->args[1]); + data_len = nla_len((void *)cb->args[1]); + } + + mutex_lock(&cfg80211_mutex); + dev = cfg80211_rdev_by_wiphy_idx(phy_idx); + if (!dev) { + mutex_unlock(&cfg80211_mutex); + return -ENOENT; + } + cfg80211_lock_rdev(dev); + mutex_unlock(&cfg80211_mutex); + + if (!dev->ops->testmode_dump) { + err = -EOPNOTSUPP; + goto out_err; + } + + while (1) { + void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + NL80211_CMD_TESTMODE); + struct nlattr *tmdata; + + if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) { + genlmsg_cancel(skb, hdr); + break; + } + + tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA); + if (!tmdata) { + genlmsg_cancel(skb, hdr); + break; + } + err = dev->ops->testmode_dump(&dev->wiphy, skb, cb, + data, data_len); + nla_nest_end(skb, tmdata); + + if (err == -ENOBUFS || err == -ENOENT) { + genlmsg_cancel(skb, hdr); + break; + } else if (err) { + genlmsg_cancel(skb, hdr); + goto out_err; + } + + genlmsg_end(skb, hdr); + } + + err = skb->len; + /* see above */ + cb->args[0] = phy_idx + 1; + out_err: + cfg80211_unlock_rdev(dev); + return err; +} + static struct sk_buff * __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, int approxlen, u32 pid, u32 seq, gfp_t gfp) @@ -5658,6 +5745,7 @@ static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_TESTMODE, .doit = nl80211_testmode_do, + .dumpit = nl80211_testmode_dump, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WIPHY | -- cgit v1.2.3 From 3b68a26ec058fda2d6b470cdd8fedc5a3c854916 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 31 May 2011 15:42:49 -0400 Subject: drm/radeon/kms: add llano pci ids Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- include/drm/drm_pciids.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index f04b2a3b0f49..e08f344c6cff 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -467,6 +467,17 @@ {0x1002, 0x9614, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9615, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9616, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9642, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9643, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9644, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9645, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\ + {0x1002, 0x9648, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\ + {0x1002, 0x964a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x964e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\ + {0x1002, 0x964f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\ {0x1002, 0x9710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ -- cgit v1.2.3 From 40d69043fce579378eb185d31445b6ff66abbd93 Mon Sep 17 00:00:00 2001 From: "Dmitry.Tarnyagin" Date: Wed, 1 Jun 2011 03:29:18 +0000 Subject: caif: Add CAIF HSI Link layer driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces the CAIF HSI Protocol Driver for the CAIF Link Layer. This driver implements a platform driver to accommodate for a platform specific HSI devices. A general platform driver is not possible as there are no HSI side Kernel API defined. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/Kconfig | 9 + drivers/net/caif/Makefile | 3 + drivers/net/caif/caif_hsi.c | 1220 +++++++++++++++++++++++++++++++++++++++++++ include/net/caif/caif_hsi.h | 145 +++++ 4 files changed, 1377 insertions(+) create mode 100644 drivers/net/caif/caif_hsi.c create mode 100644 include/net/caif/caif_hsi.h (limited to 'include') diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig index 09ed3f42d673..abf4d7a9dcce 100644 --- a/drivers/net/caif/Kconfig +++ b/drivers/net/caif/Kconfig @@ -38,3 +38,12 @@ config CAIF_SHM default n ---help--- The CAIF shared memory protocol driver for the STE UX5500 platform. + +config CAIF_HSI + tristate "CAIF HSI transport driver" + depends on CAIF + default n + ---help--- + The caif low level driver for CAIF over HSI. + Be aware that if you enable this then you also need to + enable a low-level HSI driver. diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile index 9560b9d624bd..91dff861560f 100644 --- a/drivers/net/caif/Makefile +++ b/drivers/net/caif/Makefile @@ -10,3 +10,6 @@ obj-$(CONFIG_CAIF_SPI_SLAVE) += cfspi_slave.o # Shared memory caif_shm-objs := caif_shmcore.o caif_shm_u5500.o obj-$(CONFIG_CAIF_SHM) += caif_shm.o + +# HSI interface +obj-$(CONFIG_CAIF_HSI) += caif_hsi.o diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c new file mode 100644 index 000000000000..7a8ce61234a7 --- /dev/null +++ b/drivers/net/caif/caif_hsi.c @@ -0,0 +1,1220 @@ +/* + * Copyright (C) ST-Ericsson AB 2010 + * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com + * Author: Daniel Martensson / daniel.martensson@stericsson.com + * Dmitry.Tarnyagin / dmitry.tarnyagin@stericsson.com + * License terms: GNU General Public License (GPL) version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Daniel Martensson"); +MODULE_DESCRIPTION("CAIF HSI driver"); + +/* Returns the number of padding bytes for alignment. */ +#define PAD_POW2(x, pow) ((((x)&((pow)-1)) == 0) ? 0 :\ + (((pow)-((x)&((pow)-1))))) + +/* + * HSI padding options. + * Warning: must be a base of 2 (& operation used) and can not be zero ! + */ +static int hsi_head_align = 4; +module_param(hsi_head_align, int, S_IRUGO); +MODULE_PARM_DESC(hsi_head_align, "HSI head alignment."); + +static int hsi_tail_align = 4; +module_param(hsi_tail_align, int, S_IRUGO); +MODULE_PARM_DESC(hsi_tail_align, "HSI tail alignment."); + +/* + * HSI link layer flowcontrol thresholds. + * Warning: A high threshold value migth increase throughput but it will at + * the same time prevent channel prioritization and increase the risk of + * flooding the modem. The high threshold should be above the low. + */ +static int hsi_high_threshold = 100; +module_param(hsi_high_threshold, int, S_IRUGO); +MODULE_PARM_DESC(hsi_high_threshold, "HSI high threshold (FLOW OFF)."); + +static int hsi_low_threshold = 50; +module_param(hsi_low_threshold, int, S_IRUGO); +MODULE_PARM_DESC(hsi_low_threshold, "HSI high threshold (FLOW ON)."); + +#define ON 1 +#define OFF 0 + +/* + * Threshold values for the HSI packet queue. Flowcontrol will be asserted + * when the number of packets exceeds HIGH_WATER_MARK. It will not be + * de-asserted before the number of packets drops below LOW_WATER_MARK. + */ +#define LOW_WATER_MARK hsi_low_threshold +#define HIGH_WATER_MARK hsi_high_threshold + +static LIST_HEAD(cfhsi_list); +static spinlock_t cfhsi_list_lock; + +static void cfhsi_inactivity_tout(unsigned long arg) +{ + struct cfhsi *cfhsi = (struct cfhsi *)arg; + + dev_dbg(&cfhsi->ndev->dev, "%s.\n", + __func__); + + /* Schedule power down work queue. */ + if (!test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + queue_work(cfhsi->wq, &cfhsi->wake_down_work); +} + +static void cfhsi_abort_tx(struct cfhsi *cfhsi) +{ + struct sk_buff *skb; + + for (;;) { + spin_lock_bh(&cfhsi->lock); + skb = skb_dequeue(&cfhsi->qhead); + if (!skb) + break; + + cfhsi->ndev->stats.tx_errors++; + cfhsi->ndev->stats.tx_dropped++; + spin_unlock_bh(&cfhsi->lock); + kfree_skb(skb); + } + cfhsi->tx_state = CFHSI_TX_STATE_IDLE; + if (!test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + mod_timer(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT); + spin_unlock_bh(&cfhsi->lock); +} + +static int cfhsi_flush_fifo(struct cfhsi *cfhsi) +{ + char buffer[32]; /* Any reasonable value */ + size_t fifo_occupancy; + int ret; + + dev_dbg(&cfhsi->ndev->dev, "%s.\n", + __func__); + + + ret = cfhsi->dev->cfhsi_wake_up(cfhsi->dev); + if (ret) { + dev_warn(&cfhsi->ndev->dev, + "%s: can't wake up HSI interface: %d.\n", + __func__, ret); + return ret; + } + + do { + ret = cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, + &fifo_occupancy); + if (ret) { + dev_warn(&cfhsi->ndev->dev, + "%s: can't get FIFO occupancy: %d.\n", + __func__, ret); + break; + } else if (!fifo_occupancy) + /* No more data, exitting normally */ + break; + + fifo_occupancy = min(sizeof(buffer), fifo_occupancy); + set_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits); + ret = cfhsi->dev->cfhsi_rx(buffer, fifo_occupancy, + cfhsi->dev); + if (ret) { + clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits); + dev_warn(&cfhsi->ndev->dev, + "%s: can't read data: %d.\n", + __func__, ret); + break; + } + + ret = 5 * HZ; + wait_event_interruptible_timeout(cfhsi->flush_fifo_wait, + !test_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits), ret); + + if (ret < 0) { + dev_warn(&cfhsi->ndev->dev, + "%s: can't wait for flush complete: %d.\n", + __func__, ret); + break; + } else if (!ret) { + ret = -ETIMEDOUT; + dev_warn(&cfhsi->ndev->dev, + "%s: timeout waiting for flush complete.\n", + __func__); + break; + } + } while (1); + + cfhsi->dev->cfhsi_wake_down(cfhsi->dev); + + return ret; +} + +static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi) +{ + int nfrms = 0; + int pld_len = 0; + struct sk_buff *skb; + u8 *pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ; + + skb = skb_dequeue(&cfhsi->qhead); + if (!skb) + return 0; + + /* Check if we can embed a CAIF frame. */ + if (skb->len < CFHSI_MAX_EMB_FRM_SZ) { + struct caif_payload_info *info; + int hpad = 0; + int tpad = 0; + + /* Calculate needed head alignment and tail alignment. */ + info = (struct caif_payload_info *)&skb->cb; + + hpad = 1 + PAD_POW2((info->hdr_len + 1), hsi_head_align); + tpad = PAD_POW2((skb->len + hpad), hsi_tail_align); + + /* Check if frame still fits with added alignment. */ + if ((skb->len + hpad + tpad) <= CFHSI_MAX_EMB_FRM_SZ) { + u8 *pemb = desc->emb_frm; + desc->offset = CFHSI_DESC_SHORT_SZ; + *pemb = (u8)(hpad - 1); + pemb += hpad; + + /* Update network statistics. */ + cfhsi->ndev->stats.tx_packets++; + cfhsi->ndev->stats.tx_bytes += skb->len; + + /* Copy in embedded CAIF frame. */ + skb_copy_bits(skb, 0, pemb, skb->len); + consume_skb(skb); + skb = NULL; + } + } else + /* Clear offset. */ + desc->offset = 0; + + /* Create payload CAIF frames. */ + pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ; + while (nfrms < CFHSI_MAX_PKTS) { + struct caif_payload_info *info; + int hpad = 0; + int tpad = 0; + + if (!skb) + skb = skb_dequeue(&cfhsi->qhead); + + if (!skb) + break; + + /* Calculate needed head alignment and tail alignment. */ + info = (struct caif_payload_info *)&skb->cb; + + hpad = 1 + PAD_POW2((info->hdr_len + 1), hsi_head_align); + tpad = PAD_POW2((skb->len + hpad), hsi_tail_align); + + /* Fill in CAIF frame length in descriptor. */ + desc->cffrm_len[nfrms] = hpad + skb->len + tpad; + + /* Fill head padding information. */ + *pfrm = (u8)(hpad - 1); + pfrm += hpad; + + /* Update network statistics. */ + cfhsi->ndev->stats.tx_packets++; + cfhsi->ndev->stats.tx_bytes += skb->len; + + /* Copy in CAIF frame. */ + skb_copy_bits(skb, 0, pfrm, skb->len); + + /* Update payload length. */ + pld_len += desc->cffrm_len[nfrms]; + + /* Update frame pointer. */ + pfrm += skb->len + tpad; + consume_skb(skb); + skb = NULL; + + /* Update number of frames. */ + nfrms++; + } + + /* Unused length fields should be zero-filled (according to SPEC). */ + while (nfrms < CFHSI_MAX_PKTS) { + desc->cffrm_len[nfrms] = 0x0000; + nfrms++; + } + + /* Check if we can piggy-back another descriptor. */ + skb = skb_peek(&cfhsi->qhead); + if (skb) + desc->header |= CFHSI_PIGGY_DESC; + else + desc->header &= ~CFHSI_PIGGY_DESC; + + return CFHSI_DESC_SZ + pld_len; +} + +static void cfhsi_tx_done_work(struct work_struct *work) +{ + struct cfhsi *cfhsi = NULL; + struct cfhsi_desc *desc = NULL; + int len = 0; + int res; + + cfhsi = container_of(work, struct cfhsi, tx_done_work); + dev_dbg(&cfhsi->ndev->dev, "%s.\n", + __func__); + + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + + desc = (struct cfhsi_desc *)cfhsi->tx_buf; + + do { + /* + * Send flow on if flow off has been previously signalled + * and number of packets is below low water mark. + */ + spin_lock_bh(&cfhsi->lock); + if (cfhsi->flow_off_sent && + cfhsi->qhead.qlen <= cfhsi->q_low_mark && + cfhsi->cfdev.flowctrl) { + + cfhsi->flow_off_sent = 0; + cfhsi->cfdev.flowctrl(cfhsi->ndev, ON); + } + spin_unlock_bh(&cfhsi->lock); + + /* Create HSI frame. */ + len = cfhsi_tx_frm(desc, cfhsi); + if (!len) { + cfhsi->tx_state = CFHSI_TX_STATE_IDLE; + /* Start inactivity timer. */ + mod_timer(&cfhsi->timer, + jiffies + CFHSI_INACTIVITY_TOUT); + break; + } + + /* Set up new transfer. */ + res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); + if (WARN_ON(res < 0)) { + dev_err(&cfhsi->ndev->dev, "%s: TX error %d.\n", + __func__, res); + } + } while (res < 0); +} + +static void cfhsi_tx_done_cb(struct cfhsi_drv *drv) +{ + struct cfhsi *cfhsi; + + cfhsi = container_of(drv, struct cfhsi, drv); + dev_dbg(&cfhsi->ndev->dev, "%s.\n", + __func__); + + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + + queue_work(cfhsi->wq, &cfhsi->tx_done_work); +} + +static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) +{ + int xfer_sz = 0; + int nfrms = 0; + u16 *plen = NULL; + u8 *pfrm = NULL; + + if ((desc->header & ~CFHSI_PIGGY_DESC) || + (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) { + dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", + __func__); + return 0; + } + + /* Check for embedded CAIF frame. */ + if (desc->offset) { + struct sk_buff *skb; + u8 *dst = NULL; + int len = 0, retries = 0; + pfrm = ((u8 *)desc) + desc->offset; + + /* Remove offset padding. */ + pfrm += *pfrm + 1; + + /* Read length of CAIF frame (little endian). */ + len = *pfrm; + len |= ((*(pfrm+1)) << 8) & 0xFF00; + len += 2; /* Add FCS fields. */ + + + /* Allocate SKB (OK even in IRQ context). */ + skb = alloc_skb(len + 1, GFP_KERNEL); + while (!skb) { + retries++; + schedule_timeout(1); + skb = alloc_skb(len + 1, GFP_KERNEL); + if (skb) { + printk(KERN_WARNING "%s: slept for %u " + "before getting memory\n", + __func__, retries); + break; + } + if (retries > HZ) { + printk(KERN_ERR "%s: slept for 1HZ and " + "did not get memory\n", + __func__); + cfhsi->ndev->stats.rx_dropped++; + goto drop_frame; + } + } + caif_assert(skb != NULL); + + dst = skb_put(skb, len); + memcpy(dst, pfrm, len); + + skb->protocol = htons(ETH_P_CAIF); + skb_reset_mac_header(skb); + skb->dev = cfhsi->ndev; + + /* + * We are called from a arch specific platform device. + * Unfortunately we don't know what context we're + * running in. + */ + if (in_interrupt()) + netif_rx(skb); + else + netif_rx_ni(skb); + + /* Update network statistics. */ + cfhsi->ndev->stats.rx_packets++; + cfhsi->ndev->stats.rx_bytes += len; + } + +drop_frame: + /* Calculate transfer length. */ + plen = desc->cffrm_len; + while (nfrms < CFHSI_MAX_PKTS && *plen) { + xfer_sz += *plen; + plen++; + nfrms++; + } + + /* Check for piggy-backed descriptor. */ + if (desc->header & CFHSI_PIGGY_DESC) + xfer_sz += CFHSI_DESC_SZ; + + if (xfer_sz % 4) { + dev_err(&cfhsi->ndev->dev, + "%s: Invalid payload len: %d, ignored.\n", + __func__, xfer_sz); + xfer_sz = 0; + } + + return xfer_sz; +} + +static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) +{ + int rx_sz = 0; + int nfrms = 0; + u16 *plen = NULL; + u8 *pfrm = NULL; + + /* Sanity check header and offset. */ + if (WARN_ON((desc->header & ~CFHSI_PIGGY_DESC) || + (desc->offset > CFHSI_MAX_EMB_FRM_SZ))) { + dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", + __func__); + return -EINVAL; + } + + /* Set frame pointer to start of payload. */ + pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ; + plen = desc->cffrm_len; + while (nfrms < CFHSI_MAX_PKTS && *plen) { + struct sk_buff *skb; + u8 *dst = NULL; + u8 *pcffrm = NULL; + int len = 0, retries = 0; + + if (WARN_ON(desc->cffrm_len[nfrms] > CFHSI_MAX_PAYLOAD_SZ)) { + dev_err(&cfhsi->ndev->dev, "%s: Invalid payload.\n", + __func__); + return -EINVAL; + } + + /* CAIF frame starts after head padding. */ + pcffrm = pfrm + *pfrm + 1; + + /* Read length of CAIF frame (little endian). */ + len = *pcffrm; + len |= ((*(pcffrm + 1)) << 8) & 0xFF00; + len += 2; /* Add FCS fields. */ + + /* Allocate SKB (OK even in IRQ context). */ + skb = alloc_skb(len + 1, GFP_KERNEL); + while (!skb) { + retries++; + schedule_timeout(1); + skb = alloc_skb(len + 1, GFP_KERNEL); + if (skb) { + printk(KERN_WARNING "%s: slept for %u " + "before getting memory\n", + __func__, retries); + break; + } + if (retries > HZ) { + printk(KERN_ERR "%s: slept for 1HZ " + "and did not get memory\n", + __func__); + cfhsi->ndev->stats.rx_dropped++; + goto drop_frame; + } + } + caif_assert(skb != NULL); + + dst = skb_put(skb, len); + memcpy(dst, pcffrm, len); + + skb->protocol = htons(ETH_P_CAIF); + skb_reset_mac_header(skb); + skb->dev = cfhsi->ndev; + + /* + * We're called from a platform device, + * and don't know the context we're running in. + */ + if (in_interrupt()) + netif_rx(skb); + else + netif_rx_ni(skb); + + /* Update network statistics. */ + cfhsi->ndev->stats.rx_packets++; + cfhsi->ndev->stats.rx_bytes += len; + +drop_frame: + pfrm += *plen; + rx_sz += *plen; + plen++; + nfrms++; + } + + return rx_sz; +} + +static void cfhsi_rx_done_work(struct work_struct *work) +{ + int res; + int desc_pld_len = 0; + struct cfhsi *cfhsi = NULL; + struct cfhsi_desc *desc = NULL; + + cfhsi = container_of(work, struct cfhsi, rx_done_work); + desc = (struct cfhsi_desc *)cfhsi->rx_buf; + + dev_dbg(&cfhsi->ndev->dev, "%s: Kick timer if pending.\n", + __func__); + + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + + /* Update inactivity timer if pending. */ + mod_timer_pending(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT); + + if (cfhsi->rx_state == CFHSI_RX_STATE_DESC) { + desc_pld_len = cfhsi_rx_desc(desc, cfhsi); + } else { + int pld_len; + + pld_len = cfhsi_rx_pld(desc, cfhsi); + + 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); + + /* Extract piggy-backed descriptor. */ + desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi); + + /* + * Copy needed information from the piggy-backed + * descriptor to the descriptor in the start. + */ + memcpy((u8 *)desc, (u8 *)piggy_desc, + CFHSI_DESC_SHORT_SZ); + } + } + + if (desc_pld_len) { + cfhsi->rx_state = CFHSI_RX_STATE_PAYLOAD; + cfhsi->rx_ptr = cfhsi->rx_buf + CFHSI_DESC_SZ; + cfhsi->rx_len = desc_pld_len; + } else { + cfhsi->rx_state = CFHSI_RX_STATE_DESC; + cfhsi->rx_ptr = cfhsi->rx_buf; + cfhsi->rx_len = CFHSI_DESC_SZ; + } + clear_bit(CFHSI_PENDING_RX, &cfhsi->bits); + + 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, + cfhsi->dev); + if (WARN_ON(res < 0)) { + dev_err(&cfhsi->ndev->dev, "%s: RX error %d.\n", + __func__, res); + cfhsi->ndev->stats.rx_errors++; + cfhsi->ndev->stats.rx_dropped++; + } + } +} + +static void cfhsi_rx_done_cb(struct cfhsi_drv *drv) +{ + struct cfhsi *cfhsi; + + cfhsi = container_of(drv, struct cfhsi, drv); + dev_dbg(&cfhsi->ndev->dev, "%s.\n", + __func__); + + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + + set_bit(CFHSI_PENDING_RX, &cfhsi->bits); + + if (test_and_clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits)) + wake_up_interruptible(&cfhsi->flush_fifo_wait); + else + queue_work(cfhsi->wq, &cfhsi->rx_done_work); +} + +static void cfhsi_wake_up(struct work_struct *work) +{ + struct cfhsi *cfhsi = NULL; + int res; + int len; + long ret; + + cfhsi = container_of(work, struct cfhsi, wake_up_work); + + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + + if (unlikely(test_bit(CFHSI_AWAKE, &cfhsi->bits))) { + /* It happenes when wakeup is requested by + * both ends at the same time. */ + clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); + return; + } + + /* Activate wake line. */ + cfhsi->dev->cfhsi_wake_up(cfhsi->dev); + + dev_dbg(&cfhsi->ndev->dev, "%s: Start waiting.\n", + __func__); + + /* Wait for acknowledge. */ + ret = CFHSI_WAKEUP_TOUT; + wait_event_interruptible_timeout(cfhsi->wake_up_wait, + test_bit(CFHSI_WAKE_UP_ACK, + &cfhsi->bits), ret); + if (unlikely(ret < 0)) { + /* Interrupted by signal. */ + dev_info(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n", + __func__, ret); + clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); + cfhsi->dev->cfhsi_wake_down(cfhsi->dev); + return; + } else if (!ret) { + /* Wakeup timeout */ + dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n", + __func__); + clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); + cfhsi->dev->cfhsi_wake_down(cfhsi->dev); + return; + } + dev_dbg(&cfhsi->ndev->dev, "%s: Woken.\n", + __func__); + + /* Clear power up bit. */ + set_bit(CFHSI_AWAKE, &cfhsi->bits); + clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); + + /* Resume read operation. */ + if (!test_bit(CFHSI_PENDING_RX, &cfhsi->bits)) { + dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n", + __func__); + res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, + cfhsi->rx_len, cfhsi->dev); + if (WARN_ON(res < 0)) { + dev_err(&cfhsi->ndev->dev, "%s: RX error %d.\n", + __func__, res); + } + } + + /* Clear power up acknowledment. */ + clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); + + spin_lock_bh(&cfhsi->lock); + + /* Resume transmit if queue is not empty. */ + if (!skb_peek(&cfhsi->qhead)) { + dev_dbg(&cfhsi->ndev->dev, "%s: Peer wake, start timer.\n", + __func__); + /* Start inactivity timer. */ + mod_timer(&cfhsi->timer, + jiffies + CFHSI_INACTIVITY_TOUT); + spin_unlock_bh(&cfhsi->lock); + return; + } + + dev_dbg(&cfhsi->ndev->dev, "%s: Host wake.\n", + __func__); + + spin_unlock_bh(&cfhsi->lock); + + /* Create HSI frame. */ + len = cfhsi_tx_frm((struct cfhsi_desc *)cfhsi->tx_buf, cfhsi); + + if (likely(len > 0)) { + /* Set up new transfer. */ + res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); + if (WARN_ON(res < 0)) { + dev_err(&cfhsi->ndev->dev, "%s: TX error %d.\n", + __func__, res); + cfhsi_abort_tx(cfhsi); + } + } else { + dev_err(&cfhsi->ndev->dev, + "%s: Failed to create HSI frame: %d.\n", + __func__, len); + } + +} + +static void cfhsi_wake_down(struct work_struct *work) +{ + long ret; + struct cfhsi *cfhsi = NULL; + size_t fifo_occupancy; + + cfhsi = container_of(work, struct cfhsi, wake_down_work); + dev_dbg(&cfhsi->ndev->dev, "%s.\n", + __func__); + + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + + /* Check if there is something in FIFO. */ + if (WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, + &fifo_occupancy))) + fifo_occupancy = 0; + + if (fifo_occupancy) { + dev_dbg(&cfhsi->ndev->dev, + "%s: %u words in RX FIFO, restart timer.\n", + __func__, (unsigned) fifo_occupancy); + spin_lock_bh(&cfhsi->lock); + mod_timer(&cfhsi->timer, + jiffies + CFHSI_INACTIVITY_TOUT); + spin_unlock_bh(&cfhsi->lock); + return; + } + + /* Cancel pending RX requests */ + cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev); + + /* Deactivate wake line. */ + cfhsi->dev->cfhsi_wake_down(cfhsi->dev); + + /* Wait for acknowledge. */ + ret = CFHSI_WAKEUP_TOUT; + ret = wait_event_interruptible_timeout(cfhsi->wake_down_wait, + test_bit(CFHSI_WAKE_DOWN_ACK, + &cfhsi->bits), + ret); + if (ret < 0) { + /* Interrupted by signal. */ + dev_info(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n", + __func__, ret); + return; + } else if (!ret) { + /* Timeout */ + dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n", + __func__); + } + + /* Clear power down acknowledment. */ + clear_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits); + clear_bit(CFHSI_AWAKE, &cfhsi->bits); + + /* Check if there is something in FIFO. */ + if (WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, + &fifo_occupancy))) + fifo_occupancy = 0; + + if (fifo_occupancy) { + dev_dbg(&cfhsi->ndev->dev, + "%s: %u words in RX FIFO, wakeup forced.\n", + __func__, (unsigned) fifo_occupancy); + if (!test_and_set_bit(CFHSI_WAKE_UP, &cfhsi->bits)) + queue_work(cfhsi->wq, &cfhsi->wake_up_work); + } else + dev_dbg(&cfhsi->ndev->dev, "%s: Done.\n", + __func__); +} + +static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) +{ + struct cfhsi *cfhsi = NULL; + + cfhsi = container_of(drv, struct cfhsi, drv); + dev_dbg(&cfhsi->ndev->dev, "%s.\n", + __func__); + + set_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); + wake_up_interruptible(&cfhsi->wake_up_wait); + + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + + /* Schedule wake up work queue if the peer initiates. */ + if (!test_and_set_bit(CFHSI_WAKE_UP, &cfhsi->bits)) + queue_work(cfhsi->wq, &cfhsi->wake_up_work); +} + +static void cfhsi_wake_down_cb(struct cfhsi_drv *drv) +{ + struct cfhsi *cfhsi = NULL; + + cfhsi = container_of(drv, struct cfhsi, drv); + dev_dbg(&cfhsi->ndev->dev, "%s.\n", + __func__); + + /* Initiating low power is only permitted by the host (us). */ + set_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits); + wake_up_interruptible(&cfhsi->wake_down_wait); +} + +static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct cfhsi *cfhsi = NULL; + int start_xfer = 0; + int timer_active; + + if (!dev) + return -EINVAL; + + cfhsi = netdev_priv(dev); + + spin_lock_bh(&cfhsi->lock); + + skb_queue_tail(&cfhsi->qhead, skb); + + /* Sanity check; xmit should not be called after unregister_netdev */ + if (WARN_ON(test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))) { + spin_unlock_bh(&cfhsi->lock); + cfhsi_abort_tx(cfhsi); + return -EINVAL; + } + + /* Send flow off if number of packets is above high water mark. */ + if (!cfhsi->flow_off_sent && + cfhsi->qhead.qlen > cfhsi->q_high_mark && + cfhsi->cfdev.flowctrl) { + cfhsi->flow_off_sent = 1; + cfhsi->cfdev.flowctrl(cfhsi->ndev, OFF); + } + + if (cfhsi->tx_state == CFHSI_TX_STATE_IDLE) { + cfhsi->tx_state = CFHSI_TX_STATE_XFER; + start_xfer = 1; + } + + spin_unlock_bh(&cfhsi->lock); + + if (!start_xfer) + return 0; + + /* Delete inactivity timer if started. */ +#ifdef CONFIG_SMP + timer_active = del_timer_sync(&cfhsi->timer); +#else + timer_active = del_timer(&cfhsi->timer); +#endif /* CONFIG_SMP */ + + if (timer_active) { + struct cfhsi_desc *desc = (struct cfhsi_desc *)cfhsi->tx_buf; + int len; + int res; + + /* Create HSI frame. */ + len = cfhsi_tx_frm(desc, cfhsi); + BUG_ON(!len); + + /* Set up new transfer. */ + res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); + if (WARN_ON(res < 0)) { + dev_err(&cfhsi->ndev->dev, "%s: TX error %d.\n", + __func__, res); + cfhsi_abort_tx(cfhsi); + } + } else { + /* Schedule wake up work queue if the we initiate. */ + if (!test_and_set_bit(CFHSI_WAKE_UP, &cfhsi->bits)) + queue_work(cfhsi->wq, &cfhsi->wake_up_work); + } + + return 0; +} + +static int cfhsi_open(struct net_device *dev) +{ + netif_wake_queue(dev); + + return 0; +} + +static int cfhsi_close(struct net_device *dev) +{ + netif_stop_queue(dev); + + return 0; +} + +static const struct net_device_ops cfhsi_ops = { + .ndo_open = cfhsi_open, + .ndo_stop = cfhsi_close, + .ndo_start_xmit = cfhsi_xmit +}; + +static void cfhsi_setup(struct net_device *dev) +{ + struct cfhsi *cfhsi = netdev_priv(dev); + dev->features = 0; + dev->netdev_ops = &cfhsi_ops; + dev->type = ARPHRD_CAIF; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->mtu = CFHSI_MAX_PAYLOAD_SZ; + dev->tx_queue_len = 0; + dev->destructor = free_netdev; + skb_queue_head_init(&cfhsi->qhead); + cfhsi->cfdev.link_select = CAIF_LINK_HIGH_BANDW; + cfhsi->cfdev.use_frag = false; + cfhsi->cfdev.use_stx = false; + cfhsi->cfdev.use_fcs = false; + cfhsi->ndev = dev; +} + +int cfhsi_probe(struct platform_device *pdev) +{ + struct cfhsi *cfhsi = NULL; + struct net_device *ndev; + struct cfhsi_dev *dev; + int res; + + ndev = alloc_netdev(sizeof(struct cfhsi), "cfhsi%d", cfhsi_setup); + if (!ndev) { + dev_err(&pdev->dev, "%s: alloc_netdev failed.\n", + __func__); + return -ENODEV; + } + + cfhsi = netdev_priv(ndev); + cfhsi->ndev = ndev; + cfhsi->pdev = pdev; + + /* Initialize state vaiables. */ + cfhsi->tx_state = CFHSI_TX_STATE_IDLE; + cfhsi->rx_state = CFHSI_RX_STATE_DESC; + + /* Set flow info */ + cfhsi->flow_off_sent = 0; + cfhsi->q_low_mark = LOW_WATER_MARK; + cfhsi->q_high_mark = HIGH_WATER_MARK; + + /* Assign the HSI device. */ + dev = (struct cfhsi_dev *)pdev->dev.platform_data; + cfhsi->dev = dev; + + /* Assign the driver to this HSI device. */ + dev->drv = &cfhsi->drv; + + /* + * Allocate a TX buffer with the size of a HSI packet descriptors + * and the necessary room for CAIF payload frames. + */ + cfhsi->tx_buf = kzalloc(CFHSI_BUF_SZ_TX, GFP_KERNEL); + if (!cfhsi->tx_buf) { + dev_err(&ndev->dev, "%s: Failed to allocate TX buffer.\n", + __func__); + res = -ENODEV; + goto err_alloc_tx; + } + + /* + * Allocate a RX buffer with the size of two HSI packet descriptors and + * the necessary room for CAIF payload frames. + */ + cfhsi->rx_buf = kzalloc(CFHSI_BUF_SZ_RX, GFP_KERNEL); + if (!cfhsi->rx_buf) { + dev_err(&ndev->dev, "%s: Failed to allocate RX buffer.\n", + __func__); + res = -ENODEV; + goto err_alloc_rx; + } + + /* Initialize recieve vaiables. */ + cfhsi->rx_ptr = cfhsi->rx_buf; + cfhsi->rx_len = CFHSI_DESC_SZ; + + /* Initialize spin locks. */ + spin_lock_init(&cfhsi->lock); + + /* Set up the driver. */ + cfhsi->drv.tx_done_cb = cfhsi_tx_done_cb; + cfhsi->drv.rx_done_cb = cfhsi_rx_done_cb; + + /* Initialize the work queues. */ + INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up); + INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down); + INIT_WORK(&cfhsi->rx_done_work, cfhsi_rx_done_work); + INIT_WORK(&cfhsi->tx_done_work, cfhsi_tx_done_work); + + /* Clear all bit fields. */ + clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); + clear_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits); + clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); + clear_bit(CFHSI_AWAKE, &cfhsi->bits); + clear_bit(CFHSI_PENDING_RX, &cfhsi->bits); + + /* Create work thread. */ + cfhsi->wq = create_singlethread_workqueue(pdev->name); + if (!cfhsi->wq) { + dev_err(&ndev->dev, "%s: Failed to create work queue.\n", + __func__); + res = -ENODEV; + goto err_create_wq; + } + + /* Initialize wait queues. */ + init_waitqueue_head(&cfhsi->wake_up_wait); + init_waitqueue_head(&cfhsi->wake_down_wait); + init_waitqueue_head(&cfhsi->flush_fifo_wait); + + /* Setup the inactivity timer. */ + init_timer(&cfhsi->timer); + cfhsi->timer.data = (unsigned long)cfhsi; + cfhsi->timer.function = cfhsi_inactivity_tout; + + /* Add CAIF HSI device to list. */ + spin_lock(&cfhsi_list_lock); + list_add_tail(&cfhsi->list, &cfhsi_list); + spin_unlock(&cfhsi_list_lock); + + /* Activate HSI interface. */ + res = cfhsi->dev->cfhsi_up(cfhsi->dev); + if (res) { + dev_err(&cfhsi->ndev->dev, + "%s: can't activate HSI interface: %d.\n", + __func__, res); + goto err_activate; + } + + /* Flush FIFO */ + res = cfhsi_flush_fifo(cfhsi); + if (res) { + dev_err(&ndev->dev, "%s: Can't flush FIFO: %d.\n", + __func__, res); + goto err_net_reg; + } + + cfhsi->drv.wake_up_cb = cfhsi_wake_up_cb; + cfhsi->drv.wake_down_cb = cfhsi_wake_down_cb; + + /* Register network device. */ + res = register_netdev(ndev); + if (res) { + dev_err(&ndev->dev, "%s: Registration error: %d.\n", + __func__, res); + goto err_net_reg; + } + + netif_stop_queue(ndev); + + return res; + + err_net_reg: + cfhsi->dev->cfhsi_down(cfhsi->dev); + err_activate: + destroy_workqueue(cfhsi->wq); + err_create_wq: + kfree(cfhsi->rx_buf); + err_alloc_rx: + kfree(cfhsi->tx_buf); + err_alloc_tx: + free_netdev(ndev); + + return res; +} + +static void cfhsi_shutdown(struct cfhsi *cfhsi, bool remove_platform_dev) +{ + u8 *tx_buf, *rx_buf; + + /* Stop TXing */ + netif_tx_stop_all_queues(cfhsi->ndev); + + /* going to shutdown driver */ + set_bit(CFHSI_SHUTDOWN, &cfhsi->bits); + + if (remove_platform_dev) { + /* Flush workqueue */ + flush_workqueue(cfhsi->wq); + + /* Notify device. */ + platform_device_unregister(cfhsi->pdev); + } + + /* Flush workqueue */ + flush_workqueue(cfhsi->wq); + + /* Delete timer if pending */ +#ifdef CONFIG_SMP + del_timer_sync(&cfhsi->timer); +#else + del_timer(&cfhsi->timer); +#endif /* CONFIG_SMP */ + + /* Cancel pending RX request (if any) */ + cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev); + + /* Flush again and destroy workqueue */ + destroy_workqueue(cfhsi->wq); + + /* Store bufferes: will be freed later. */ + tx_buf = cfhsi->tx_buf; + rx_buf = cfhsi->rx_buf; + + /* Flush transmit queues. */ + cfhsi_abort_tx(cfhsi); + + /* Deactivate interface */ + cfhsi->dev->cfhsi_down(cfhsi->dev); + + /* Finally unregister the network device. */ + unregister_netdev(cfhsi->ndev); + + /* Free buffers. */ + kfree(tx_buf); + kfree(rx_buf); +} + +int cfhsi_remove(struct platform_device *pdev) +{ + struct list_head *list_node; + struct list_head *n; + struct cfhsi *cfhsi = NULL; + struct cfhsi_dev *dev; + + dev = (struct cfhsi_dev *)pdev->dev.platform_data; + spin_lock(&cfhsi_list_lock); + list_for_each_safe(list_node, n, &cfhsi_list) { + cfhsi = list_entry(list_node, struct cfhsi, list); + /* Find the corresponding device. */ + if (cfhsi->dev == dev) { + /* Remove from list. */ + list_del(list_node); + spin_unlock(&cfhsi_list_lock); + + /* Shutdown driver. */ + cfhsi_shutdown(cfhsi, false); + + return 0; + } + } + spin_unlock(&cfhsi_list_lock); + return -ENODEV; +} + +struct platform_driver cfhsi_plat_drv = { + .probe = cfhsi_probe, + .remove = cfhsi_remove, + .driver = { + .name = "cfhsi", + .owner = THIS_MODULE, + }, +}; + +static void __exit cfhsi_exit_module(void) +{ + struct list_head *list_node; + struct list_head *n; + struct cfhsi *cfhsi = NULL; + + spin_lock(&cfhsi_list_lock); + list_for_each_safe(list_node, n, &cfhsi_list) { + cfhsi = list_entry(list_node, struct cfhsi, list); + + /* Remove from list. */ + list_del(list_node); + spin_unlock(&cfhsi_list_lock); + + /* Shutdown driver. */ + cfhsi_shutdown(cfhsi, true); + + spin_lock(&cfhsi_list_lock); + } + spin_unlock(&cfhsi_list_lock); + + /* Unregister platform driver. */ + platform_driver_unregister(&cfhsi_plat_drv); +} + +static int __init cfhsi_init_module(void) +{ + int result; + + /* Initialize spin lock. */ + spin_lock_init(&cfhsi_list_lock); + + /* Register platform driver. */ + result = platform_driver_register(&cfhsi_plat_drv); + if (result) { + printk(KERN_ERR "Could not register platform HSI driver: %d.\n", + result); + goto err_dev_register; + } + + return result; + + err_dev_register: + return result; +} + +module_init(cfhsi_init_module); +module_exit(cfhsi_exit_module); diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h new file mode 100644 index 000000000000..c5dedd87b4cb --- /dev/null +++ b/include/net/caif/caif_hsi.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) ST-Ericsson AB 2010 + * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com + * Author: Daniel Martensson / daniel.martensson@stericsson.com + * Dmitry.Tarnyagin / dmitry.tarnyagin@stericsson.com + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef CAIF_HSI_H_ +#define CAIF_HSI_H_ + +#include +#include +#include + +/* + * Maximum number of CAIF frames that can reside in the same HSI frame. + */ +#define CFHSI_MAX_PKTS 15 + +/* + * Maximum number of bytes used for the frame that can be embedded in the + * HSI descriptor. + */ +#define CFHSI_MAX_EMB_FRM_SZ 96 + +/* + * Decides if HSI buffers should be prefilled with 0xFF pattern for easier + * debugging. Both TX and RX buffers will be filled before the transfer. + */ +#define CFHSI_DBG_PREFILL 0 + +/* Structure describing a HSI packet descriptor. */ +#pragma pack(1) /* Byte alignment. */ +struct cfhsi_desc { + u8 header; + u8 offset; + u16 cffrm_len[CFHSI_MAX_PKTS]; + u8 emb_frm[CFHSI_MAX_EMB_FRM_SZ]; +}; +#pragma pack() /* Default alignment. */ + +/* Size of the complete HSI packet descriptor. */ +#define CFHSI_DESC_SZ (sizeof(struct cfhsi_desc)) + +/* + * Size of the complete HSI packet descriptor excluding the optional embedded + * CAIF frame. + */ +#define CFHSI_DESC_SHORT_SZ (CFHSI_DESC_SZ - CFHSI_MAX_EMB_FRM_SZ) + +/* + * Maximum bytes transferred in one transfer. + */ +/* TODO: 4096 is temporary... */ +#define CFHSI_MAX_PAYLOAD_SZ (CFHSI_MAX_PKTS * 4096) + +/* Size of the complete HSI TX buffer. */ +#define CFHSI_BUF_SZ_TX (CFHSI_DESC_SZ + CFHSI_MAX_PAYLOAD_SZ) + +/* Size of the complete HSI RX buffer. */ +#define CFHSI_BUF_SZ_RX ((2 * CFHSI_DESC_SZ) + CFHSI_MAX_PAYLOAD_SZ) + +/* Bitmasks for the HSI descriptor. */ +#define CFHSI_PIGGY_DESC (0x01 << 7) + +#define CFHSI_TX_STATE_IDLE 0 +#define CFHSI_TX_STATE_XFER 1 + +#define CFHSI_RX_STATE_DESC 0 +#define CFHSI_RX_STATE_PAYLOAD 1 + +/* Bitmasks for power management. */ +#define CFHSI_WAKE_UP 0 +#define CFHSI_WAKE_UP_ACK 1 +#define CFHSI_WAKE_DOWN_ACK 2 +#define CFHSI_AWAKE 3 +#define CFHSI_PENDING_RX 4 +#define CFHSI_SHUTDOWN 6 +#define CFHSI_FLUSH_FIFO 7 + +#ifndef CFHSI_INACTIVITY_TOUT +#define CFHSI_INACTIVITY_TOUT (1 * HZ) +#endif /* CFHSI_INACTIVITY_TOUT */ + +#ifndef CFHSI_WAKEUP_TOUT +#define CFHSI_WAKEUP_TOUT (3 * HZ) +#endif /* CFHSI_WAKEUP_TOUT */ + + +/* Structure implemented by the CAIF HSI driver. */ +struct cfhsi_drv { + void (*tx_done_cb) (struct cfhsi_drv *drv); + void (*rx_done_cb) (struct cfhsi_drv *drv); + void (*wake_up_cb) (struct cfhsi_drv *drv); + void (*wake_down_cb) (struct cfhsi_drv *drv); +}; + +/* Structure implemented by HSI device. */ +struct cfhsi_dev { + int (*cfhsi_up) (struct cfhsi_dev *dev); + int (*cfhsi_down) (struct cfhsi_dev *dev); + int (*cfhsi_tx) (u8 *ptr, int len, struct cfhsi_dev *dev); + int (*cfhsi_rx) (u8 *ptr, int len, struct cfhsi_dev *dev); + int (*cfhsi_wake_up) (struct cfhsi_dev *dev); + int (*cfhsi_wake_down) (struct cfhsi_dev *dev); + int (*cfhsi_fifo_occupancy)(struct cfhsi_dev *dev, size_t *occupancy); + int (*cfhsi_rx_cancel)(struct cfhsi_dev *dev); + struct cfhsi_drv *drv; +}; + +/* Structure implemented by CAIF HSI drivers. */ +struct cfhsi { + struct caif_dev_common cfdev; + struct net_device *ndev; + struct platform_device *pdev; + struct sk_buff_head qhead; + struct cfhsi_drv drv; + struct cfhsi_dev *dev; + int tx_state; + int rx_state; + int rx_len; + u8 *rx_ptr; + u8 *tx_buf; + u8 *rx_buf; + spinlock_t lock; + int flow_off_sent; + u32 q_low_mark; + u32 q_high_mark; + struct list_head list; + struct work_struct wake_up_work; + struct work_struct wake_down_work; + struct work_struct rx_done_work; + struct work_struct tx_done_work; + struct workqueue_struct *wq; + wait_queue_head_t wake_up_wait; + wait_queue_head_t wake_down_wait; + wait_queue_head_t flush_fifo_wait; + struct timer_list timer; + unsigned long bits; +}; + +extern struct platform_driver cfhsi_driver; + +#endif /* CAIF_HSI_H_ */ -- cgit v1.2.3 From a3bcc23e890a6d49d6763d9eb073d711de2e0469 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Wed, 1 Jun 2011 06:49:10 +0000 Subject: af-packet: Add flag to distinguish VID 0 from no-vlan. Currently, user-space cannot determine if a 0 tcp_vlan_tci means there is no VLAN tag or the VLAN ID was zero. Add flag to make this explicit. User-space can check for TP_STATUS_VLAN_VALID || tp_vlan_tci > 0, which will be backwards compatible. Older could would have just checked for tp_vlan_tci, so it will work no worse than before. Signed-off-by: Ben Greear Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/if_packet.h | 1 + net/packet/af_packet.c | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h index 72bfa5a034dd..6d66ce1791a9 100644 --- a/include/linux/if_packet.h +++ b/include/linux/if_packet.h @@ -70,6 +70,7 @@ struct tpacket_auxdata { #define TP_STATUS_COPY 0x2 #define TP_STATUS_LOSING 0x4 #define TP_STATUS_CSUMNOTREADY 0x8 +#define TP_STATUS_VLAN_VALID 0x10 /* auxdata has valid tp_vlan_tci */ /* Tx ring - header status */ #define TP_STATUS_AVAILABLE 0x0 diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 925f715686a5..ba248d93399a 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -798,7 +798,12 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, getnstimeofday(&ts); h.h2->tp_sec = ts.tv_sec; h.h2->tp_nsec = ts.tv_nsec; - h.h2->tp_vlan_tci = vlan_tx_tag_get(skb); + if (vlan_tx_tag_present(skb)) { + h.h2->tp_vlan_tci = vlan_tx_tag_get(skb); + status |= TP_STATUS_VLAN_VALID; + } else { + h.h2->tp_vlan_tci = 0; + } hdrlen = sizeof(*h.h2); break; default: @@ -1725,8 +1730,12 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, aux.tp_snaplen = skb->len; aux.tp_mac = 0; aux.tp_net = skb_network_offset(skb); - aux.tp_vlan_tci = vlan_tx_tag_get(skb); - + if (vlan_tx_tag_present(skb)) { + aux.tp_vlan_tci = vlan_tx_tag_get(skb); + aux.tp_status |= TP_STATUS_VLAN_VALID; + } else { + aux.tp_vlan_tci = 0; + } put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux); } -- cgit v1.2.3 From 3ced2dddf10f26f0aaff96f3345a3d876cea62f8 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki Date: Tue, 26 Apr 2011 19:23:24 +0900 Subject: sctp: Allow regular C expression in 4th argument for SCTP_DEBUG_PRINTK_IPADDR macro. Signed-off-by: YOSHIFUJI Hideaki Acked-by: Wei Yongjun Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index b2c2366676a7..0037e28e80e5 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -285,20 +285,21 @@ do { \ pr_cont(fmt, ##args); \ } while (0) #define SCTP_DEBUG_PRINTK_IPADDR(fmt_lead, fmt_trail, \ - args_lead, saddr, args_trail...) \ + args_lead, addr, args_trail...) \ do { \ + const union sctp_addr *_addr = (addr); \ if (sctp_debug_flag) { \ - if (saddr->sa.sa_family == AF_INET6) { \ + if (_addr->sa.sa_family == AF_INET6) { \ printk(KERN_DEBUG \ pr_fmt(fmt_lead "%pI6" fmt_trail), \ args_lead, \ - &saddr->v6.sin6_addr, \ + &_addr->v6.sin6_addr, \ args_trail); \ } else { \ printk(KERN_DEBUG \ pr_fmt(fmt_lead "%pI4" fmt_trail), \ args_lead, \ - &saddr->v4.sin_addr.s_addr, \ + &_addr->v4.sin_addr.s_addr, \ args_trail); \ } \ } \ -- cgit v1.2.3 From 9f7d653b67aed2d92540fbb0a8adaf32fcf352ae Mon Sep 17 00:00:00 2001 From: Michio Honda Date: Tue, 26 Apr 2011 19:32:51 +0900 Subject: sctp: Add Auto-ASCONF support (core). SCTP reconfigure the IP addresses in the association by using ASCONF chunks as mentioned in RFC5061. For example, we can start to use the newly configured IP address in the existing association. This patch implements automatic ASCONF operation in the SCTP stack with address events in the host computer, which is called auto_asconf. Signed-off-by: Michio Honda Signed-off-by: YOSHIFUJI Hideaki Acked-by: Wei Yongjun Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 2 + include/net/sctp/structs.h | 15 +++++ net/sctp/bind_addr.c | 15 +++++ net/sctp/ipv6.c | 2 + net/sctp/protocol.c | 147 +++++++++++++++++++++++++++++++++++++++++++++ net/sctp/socket.c | 46 ++++++++++++-- 6 files changed, 223 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 0037e28e80e5..f9fe374ac9f4 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -120,6 +120,7 @@ extern int sctp_copy_local_addr_list(struct sctp_bind_addr *, int flags); extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family); extern int sctp_register_pf(struct sctp_pf *, sa_family_t); +extern void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *, int); /* * sctp/socket.c @@ -134,6 +135,7 @@ void sctp_sock_rfree(struct sk_buff *skb); void sctp_copy_sock(struct sock *newsk, struct sock *sk, struct sctp_association *asoc); extern struct percpu_counter sctp_sockets_allocated; +extern int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *); /* * sctp/primitive.c diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 7df327a6d564..cd8c929e6b0d 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -205,6 +205,11 @@ extern struct sctp_globals { * It is a list of sctp_sockaddr_entry. */ struct list_head local_addr_list; + int default_auto_asconf; + struct list_head addr_waitq; + struct timer_list addr_wq_timer; + struct list_head auto_asconf_splist; + spinlock_t addr_wq_lock; /* Lock that protects the local_addr_list writers */ spinlock_t addr_list_lock; @@ -264,6 +269,11 @@ extern struct sctp_globals { #define sctp_port_hashtable (sctp_globals.port_hashtable) #define sctp_local_addr_list (sctp_globals.local_addr_list) #define sctp_local_addr_lock (sctp_globals.addr_list_lock) +#define sctp_auto_asconf_splist (sctp_globals.auto_asconf_splist) +#define sctp_addr_waitq (sctp_globals.addr_waitq) +#define sctp_addr_wq_timer (sctp_globals.addr_wq_timer) +#define sctp_addr_wq_lock (sctp_globals.addr_wq_lock) +#define sctp_default_auto_asconf (sctp_globals.default_auto_asconf) #define sctp_scope_policy (sctp_globals.ipv4_scope_policy) #define sctp_addip_enable (sctp_globals.addip_enable) #define sctp_addip_noauth (sctp_globals.addip_noauth_enable) @@ -341,6 +351,8 @@ struct sctp_sock { atomic_t pd_mode; /* Receive to here while partial delivery is in effect. */ struct sk_buff_head pd_lobby; + struct list_head auto_asconf_list; + int do_auto_asconf; }; static inline struct sctp_sock *sctp_sk(const struct sock *sk) @@ -792,6 +804,8 @@ struct sctp_sockaddr_entry { __u8 valid; }; +#define SCTP_ADDRESS_TICK_DELAY 500 + typedef struct sctp_chunk *(sctp_packet_phandler_t)(struct sctp_association *); /* This structure holds lists of chunks as we are assembling for @@ -1236,6 +1250,7 @@ sctp_scope_t sctp_scope(const union sctp_addr *); int sctp_in_scope(const union sctp_addr *addr, const sctp_scope_t scope); int sctp_is_any(struct sock *sk, const union sctp_addr *addr); int sctp_addr_is_valid(const union sctp_addr *addr); +int sctp_is_ep_boundall(struct sock *sk); /* What type of endpoint? */ diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 83e3011c19ca..17d157325b66 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -534,6 +534,21 @@ int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope) return 0; } +int sctp_is_ep_boundall(struct sock *sk) +{ + struct sctp_bind_addr *bp; + struct sctp_sockaddr_entry *addr; + + bp = &sctp_sk(sk)->ep->base.bind_addr; + if (sctp_list_single_entry(&bp->address_list)) { + addr = list_entry(bp->address_list.next, + struct sctp_sockaddr_entry, list); + if (sctp_is_any(sk, &addr->a)) + return 1; + } + return 0; +} + /******************************************************************** * 3rd Level Abstractions ********************************************************************/ diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 0bb0d7cb9f10..aabaee41dd3e 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -112,6 +112,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, addr->valid = 1; spin_lock_bh(&sctp_local_addr_lock); list_add_tail_rcu(&addr->list, &sctp_local_addr_list); + sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW); spin_unlock_bh(&sctp_local_addr_lock); } break; @@ -122,6 +123,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, if (addr->a.sa.sa_family == AF_INET6 && ipv6_addr_equal(&addr->a.v6.sin6_addr, &ifa->addr)) { + sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL); found = 1; addr->valid = 0; list_del_rcu(&addr->list); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 67380a29e2e9..013c6136c546 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -623,6 +623,142 @@ static void sctp_v4_ecn_capable(struct sock *sk) INET_ECN_xmit(sk); } +void sctp_addr_wq_timeout_handler(unsigned long arg) +{ + struct sctp_sockaddr_entry *addrw, *temp; + struct sctp_sock *sp; + + spin_lock_bh(&sctp_addr_wq_lock); + + list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) { + SCTP_DEBUG_PRINTK_IPADDR("sctp_addrwq_timo_handler: the first ent in wq %p is ", + " for cmd %d at entry %p\n", &sctp_addr_waitq, &addrw->a, addrw->state, + addrw); + + /* Now we send an ASCONF for each association */ + /* Note. we currently don't handle link local IPv6 addressees */ + if (addrw->a.sa.sa_family == AF_INET6) { + struct in6_addr *in6; + + if (ipv6_addr_type(&addrw->a.v6.sin6_addr) & + IPV6_ADDR_LINKLOCAL) + goto free_next; + + in6 = (struct in6_addr *)&addrw->a.v6.sin6_addr; + if (ipv6_chk_addr(&init_net, in6, NULL, 0) == 0 && + addrw->state == SCTP_ADDR_NEW) { + unsigned long timeo_val; + + SCTP_DEBUG_PRINTK("sctp_timo_handler: this is on DAD, trying %d sec later\n", + SCTP_ADDRESS_TICK_DELAY); + timeo_val = jiffies; + timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY); + mod_timer(&sctp_addr_wq_timer, timeo_val); + break; + } + } + + list_for_each_entry(sp, &sctp_auto_asconf_splist, auto_asconf_list) { + struct sock *sk; + + sk = sctp_opt2sk(sp); + /* ignore bound-specific endpoints */ + if (!sctp_is_ep_boundall(sk)) + continue; + sctp_bh_lock_sock(sk); + if (sctp_asconf_mgmt(sp, addrw) < 0) + SCTP_DEBUG_PRINTK("sctp_addrwq_timo_handler: sctp_asconf_mgmt failed\n"); + sctp_bh_unlock_sock(sk); + } +free_next: + list_del(&addrw->list); + kfree(addrw); + } + spin_unlock_bh(&sctp_addr_wq_lock); +} + +static void sctp_free_addr_wq(void) +{ + struct sctp_sockaddr_entry *addrw; + struct sctp_sockaddr_entry *temp; + + spin_lock_bh(&sctp_addr_wq_lock); + del_timer(&sctp_addr_wq_timer); + list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) { + list_del(&addrw->list); + kfree(addrw); + } + spin_unlock_bh(&sctp_addr_wq_lock); +} + +/* lookup the entry for the same address in the addr_waitq + * sctp_addr_wq MUST be locked + */ +static struct sctp_sockaddr_entry *sctp_addr_wq_lookup(struct sctp_sockaddr_entry *addr) +{ + struct sctp_sockaddr_entry *addrw; + + list_for_each_entry(addrw, &sctp_addr_waitq, list) { + if (addrw->a.sa.sa_family != addr->a.sa.sa_family) + continue; + if (addrw->a.sa.sa_family == AF_INET) { + if (addrw->a.v4.sin_addr.s_addr == + addr->a.v4.sin_addr.s_addr) + return addrw; + } else if (addrw->a.sa.sa_family == AF_INET6) { + if (ipv6_addr_equal(&addrw->a.v6.sin6_addr, + &addr->a.v6.sin6_addr)) + return addrw; + } + } + return NULL; +} + +void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *addr, int cmd) +{ + struct sctp_sockaddr_entry *addrw; + unsigned long timeo_val; + + /* first, we check if an opposite message already exist in the queue. + * If we found such message, it is removed. + * This operation is a bit stupid, but the DHCP client attaches the + * new address after a couple of addition and deletion of that address + */ + + spin_lock_bh(&sctp_addr_wq_lock); + /* Offsets existing events in addr_wq */ + addrw = sctp_addr_wq_lookup(addr); + if (addrw) { + if (addrw->state != cmd) { + SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt offsets existing entry for %d ", + " in wq %p\n", addrw->state, &addrw->a, + &sctp_addr_waitq); + list_del(&addrw->list); + kfree(addrw); + } + spin_unlock_bh(&sctp_addr_wq_lock); + return; + } + + /* OK, we have to add the new address to the wait queue */ + addrw = kmemdup(addr, sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC); + if (addrw == NULL) { + spin_unlock_bh(&sctp_addr_wq_lock); + return; + } + addrw->state = cmd; + list_add_tail(&addrw->list, &sctp_addr_waitq); + SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt add new entry for cmd:%d ", + " in wq %p\n", addrw->state, &addrw->a, &sctp_addr_waitq); + + if (!timer_pending(&sctp_addr_wq_timer)) { + timeo_val = jiffies; + timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY); + mod_timer(&sctp_addr_wq_timer, timeo_val); + } + spin_unlock_bh(&sctp_addr_wq_lock); +} + /* Event handler for inet address addition/deletion events. * The sctp_local_addr_list needs to be protocted by a spin lock since * multiple notifiers (say IPv4 and IPv6) may be running at the same @@ -650,6 +786,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, addr->valid = 1; spin_lock_bh(&sctp_local_addr_lock); list_add_tail_rcu(&addr->list, &sctp_local_addr_list); + sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW); spin_unlock_bh(&sctp_local_addr_lock); } break; @@ -660,6 +797,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, if (addr->a.sa.sa_family == AF_INET && addr->a.v4.sin_addr.s_addr == ifa->ifa_local) { + sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL); found = 1; addr->valid = 0; list_del_rcu(&addr->list); @@ -1242,6 +1380,7 @@ SCTP_STATIC __init int sctp_init(void) /* Disable ADDIP by default. */ sctp_addip_enable = 0; sctp_addip_noauth = 0; + sctp_default_auto_asconf = 0; /* Enable PR-SCTP by default. */ sctp_prsctp_enable = 1; @@ -1266,6 +1405,13 @@ SCTP_STATIC __init int sctp_init(void) spin_lock_init(&sctp_local_addr_lock); sctp_get_local_addr_list(); + /* Initialize the address event list */ + INIT_LIST_HEAD(&sctp_addr_waitq); + INIT_LIST_HEAD(&sctp_auto_asconf_splist); + spin_lock_init(&sctp_addr_wq_lock); + sctp_addr_wq_timer.expires = 0; + setup_timer(&sctp_addr_wq_timer, sctp_addr_wq_timeout_handler, 0); + status = sctp_v4_protosw_init(); if (status) @@ -1337,6 +1483,7 @@ SCTP_STATIC __exit void sctp_exit(void) /* Unregister with inet6/inet layers. */ sctp_v6_del_protocol(); sctp_v4_del_protocol(); + sctp_free_addr_wq(); /* Free the control endpoint. */ inet_ctl_sock_destroy(sctp_ctl_sock); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 6766913a53e6..7eb1f1a736fb 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -811,6 +811,28 @@ out: return retval; } +/* set addr events to assocs in the endpoint. ep and addr_wq must be locked */ +int sctp_asconf_mgmt(struct sctp_sock *sp, struct sctp_sockaddr_entry *addrw) +{ + struct sock *sk = sctp_opt2sk(sp); + union sctp_addr *addr; + struct sctp_af *af; + + /* It is safe to write port space in caller. */ + addr = &addrw->a; + addr->v4.sin_port = htons(sp->ep->base.bind_addr.port); + af = sctp_get_af_specific(addr->sa.sa_family); + if (!af) + return -EINVAL; + if (sctp_verify_addr(sk, addr, af->sockaddr_len)) + return -EINVAL; + + if (addrw->state == SCTP_ADDR_NEW) + return sctp_send_asconf_add_ip(sk, (struct sockaddr *)addr, 1); + else + return sctp_send_asconf_del_ip(sk, (struct sockaddr *)addr, 1); +} + /* Helper for tunneling sctp_bindx() requests through sctp_setsockopt() * * API 8.1 @@ -3763,6 +3785,12 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) local_bh_disable(); percpu_counter_inc(&sctp_sockets_allocated); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); + if (sctp_default_auto_asconf) { + list_add_tail(&sp->auto_asconf_list, + &sctp_auto_asconf_splist); + sp->do_auto_asconf = 1; + } else + sp->do_auto_asconf = 0; local_bh_enable(); return 0; @@ -3771,13 +3799,17 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) /* Cleanup any SCTP per socket resources. */ SCTP_STATIC void sctp_destroy_sock(struct sock *sk) { - struct sctp_endpoint *ep; + struct sctp_sock *sp; SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk); /* Release our hold on the endpoint. */ - ep = sctp_sk(sk)->ep; - sctp_endpoint_free(ep); + sp = sctp_sk(sk); + if (sp->do_auto_asconf) { + sp->do_auto_asconf = 0; + list_del(&sp->auto_asconf_list); + } + sctp_endpoint_free(sp->ep); local_bh_disable(); percpu_counter_dec(&sctp_sockets_allocated); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); @@ -6512,6 +6544,7 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, struct sk_buff *skb, *tmp; struct sctp_ulpevent *event; struct sctp_bind_hashbucket *head; + struct list_head tmplist; /* Migrate socket buffer sizes and all the socket level options to the * new socket. @@ -6519,7 +6552,12 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, newsk->sk_sndbuf = oldsk->sk_sndbuf; newsk->sk_rcvbuf = oldsk->sk_rcvbuf; /* Brute force copy old sctp opt. */ - inet_sk_copy_descendant(newsk, oldsk); + if (oldsp->do_auto_asconf) { + memcpy(&tmplist, &newsp->auto_asconf_list, sizeof(tmplist)); + inet_sk_copy_descendant(newsk, oldsk); + memcpy(&newsp->auto_asconf_list, &tmplist, sizeof(tmplist)); + } else + inet_sk_copy_descendant(newsk, oldsk); /* Restore the ep value that was overwritten with the above structure * copy. -- cgit v1.2.3 From 7dc04d712203eecdc1435a4cd135935c4a297be5 Mon Sep 17 00:00:00 2001 From: Michio Honda Date: Tue, 26 Apr 2011 20:16:31 +0900 Subject: sctp: Add socket option operation for Auto-ASCONF. This patch allows the application to operate Auto-ASCONF on/off behavior via setsockopt() and getsockopt(). Signed-off-by: Michio Honda Signed-off-by: YOSHIFUJI Hideaki Acked-by: Wei Yongjun Signed-off-by: David S. Miller --- include/net/sctp/user.h | 1 + net/sctp/socket.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h index 32fd51274037..0842ef00b2fe 100644 --- a/include/net/sctp/user.h +++ b/include/net/sctp/user.h @@ -92,6 +92,7 @@ typedef __s32 sctp_assoc_t; #define SCTP_LOCAL_AUTH_CHUNKS 27 /* Read only */ #define SCTP_GET_ASSOC_NUMBER 28 /* Read only */ #define SCTP_GET_ASSOC_ID_LIST 29 /* Read only */ +#define SCTP_AUTO_ASCONF 30 /* Internal Socket Options. Some of the sctp library functions are * implemented using these socket options. diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 7eb1f1a736fb..cc06198dc444 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -3356,6 +3356,46 @@ static int sctp_setsockopt_del_key(struct sock *sk, } +/* + * 8.1.23 SCTP_AUTO_ASCONF + * + * This option will enable or disable the use of the automatic generation of + * ASCONF chunks to add and delete addresses to an existing association. Note + * that this option has two caveats namely: a) it only affects sockets that + * are bound to all addresses available to the SCTP stack, and b) the system + * administrator may have an overriding control that turns the ASCONF feature + * off no matter what setting the socket option may have. + * This option expects an integer boolean flag, where a non-zero value turns on + * the option, and a zero value turns off the option. + * Note. In this implementation, socket operation overrides default parameter + * being set by sysctl as well as FreeBSD implementation + */ +static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval, + unsigned int optlen) +{ + int val; + struct sctp_sock *sp = sctp_sk(sk); + + if (optlen < sizeof(int)) + return -EINVAL; + if (get_user(val, (int __user *)optval)) + return -EFAULT; + if (!sctp_is_ep_boundall(sk) && val) + return -EINVAL; + if ((val && sp->do_auto_asconf) || (!val && !sp->do_auto_asconf)) + return 0; + + if (val == 0 && sp->do_auto_asconf) { + list_del(&sp->auto_asconf_list); + sp->do_auto_asconf = 0; + } else if (val && !sp->do_auto_asconf) { + list_add_tail(&sp->auto_asconf_list, + &sctp_auto_asconf_splist); + sp->do_auto_asconf = 1; + } + return 0; +} + /* API 6.2 setsockopt(), getsockopt() * @@ -3503,6 +3543,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, case SCTP_AUTH_DELETE_KEY: retval = sctp_setsockopt_del_key(sk, optval, optlen); break; + case SCTP_AUTO_ASCONF: + retval = sctp_setsockopt_auto_asconf(sk, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; @@ -5308,6 +5351,28 @@ static int sctp_getsockopt_assoc_number(struct sock *sk, int len, return 0; } +/* + * 8.1.23 SCTP_AUTO_ASCONF + * See the corresponding setsockopt entry as description + */ +static int sctp_getsockopt_auto_asconf(struct sock *sk, int len, + char __user *optval, int __user *optlen) +{ + int val = 0; + + if (len < sizeof(int)) + return -EINVAL; + + len = sizeof(int); + if (sctp_sk(sk)->do_auto_asconf && sctp_is_ep_boundall(sk)) + val = 1; + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + return 0; +} + /* * 8.2.6. Get the Current Identifiers of Associations * (SCTP_GET_ASSOC_ID_LIST) @@ -5492,6 +5557,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, case SCTP_GET_ASSOC_ID_LIST: retval = sctp_getsockopt_assoc_ids(sk, len, optval, optlen); break; + case SCTP_AUTO_ASCONF: + retval = sctp_getsockopt_auto_asconf(sk, len, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; -- cgit v1.2.3 From 8a07eb0a50aebc8c95478d49c28c7f8419a26cef Mon Sep 17 00:00:00 2001 From: Michio Honda Date: Tue, 26 Apr 2011 20:19:36 +0900 Subject: sctp: Add ASCONF operation on the single-homed host In this case, the SCTP association transmits an ASCONF packet including addition of the new IP address and deletion of the old address. This patch implements this functionality. In this case, the ASCONF chunk is added to the beginning of the queue, because the other chunks cannot be transmitted in this state. Signed-off-by: Michio Honda Signed-off-by: YOSHIFUJI Hideaki Acked-by: Wei Yongjun Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 2 ++ net/sctp/associola.c | 6 +++++ net/sctp/outqueue.c | 13 +++++++++++ net/sctp/protocol.c | 4 +++- net/sctp/sm_make_chunk.c | 27 ++++++++++++++++++++++ net/sctp/socket.c | 56 +++++++++++++++++++++++++++++++++++++++++----- 6 files changed, 101 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index cd8c929e6b0d..31d7ea2e1d2a 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1913,6 +1913,8 @@ struct sctp_association { * after reaching 4294967295. */ __u32 addip_serial; + union sctp_addr *asconf_addr_del_pending; + int src_out_of_asoc_ok; /* SCTP AUTH: list of the endpoint shared keys. These * keys are provided out of band by the user applicaton diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 4a62888f2e43..dc16b90ddb6f 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -280,6 +280,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a asoc->peer.asconf_capable = 0; if (sctp_addip_noauth) asoc->peer.asconf_capable = 1; + asoc->asconf_addr_del_pending = NULL; + asoc->src_out_of_asoc_ok = 0; /* Create an input queue. */ sctp_inq_init(&asoc->base.inqueue); @@ -446,6 +448,10 @@ void sctp_association_free(struct sctp_association *asoc) sctp_asconf_queue_teardown(asoc); + /* Free pending address space being deleted */ + if (asoc->asconf_addr_del_pending != NULL) + kfree(asoc->asconf_addr_del_pending); + /* AUTH - Free the endpoint shared keys */ sctp_auth_destroy_keys(&asoc->endpoint_shared_keys); diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 1c88c8911dc5..edc753297a49 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -754,6 +754,16 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) */ list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) { + /* RFC 5061, 5.3 + * F1) This means that until such time as the ASCONF + * containing the add is acknowledged, the sender MUST + * NOT use the new IP address as a source for ANY SCTP + * packet except on carrying an ASCONF Chunk. + */ + if (asoc->src_out_of_asoc_ok && + chunk->chunk_hdr->type != SCTP_CID_ASCONF) + continue; + list_del_init(&chunk->list); /* Pick the right transport to use. */ @@ -881,6 +891,9 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) } } + if (q->asoc->src_out_of_asoc_ok) + goto sctp_flush_out; + /* Is it OK to send data chunks? */ switch (asoc->state) { case SCTP_STATE_COOKIE_ECHOED: diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 013c6136c546..af0a6b0fc9b6 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -503,7 +503,9 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr, sctp_v4_dst_saddr(&dst_saddr, fl4, htons(bp->port)); rcu_read_lock(); list_for_each_entry_rcu(laddr, &bp->address_list, list) { - if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC)) + if (!laddr->valid || (laddr->state == SCTP_ADDR_DEL) || + (laddr->state != SCTP_ADDR_SRC && + !asoc->src_out_of_asoc_ok)) continue; if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a)) goto out_unlock; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 37406039820f..3363d3788259 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2768,6 +2768,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, int addr_param_len = 0; int totallen = 0; int i; + int del_pickup = 0; /* Get total length of all the address parameters. */ addr_buf = addrs; @@ -2780,6 +2781,13 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, totallen += addr_param_len; addr_buf += af->sockaddr_len; + if (asoc->asconf_addr_del_pending && !del_pickup) { + /* reuse the parameter length from the same scope one */ + totallen += paramlen; + totallen += addr_param_len; + del_pickup = 1; + SCTP_DEBUG_PRINTK("mkasconf_update_ip: picked same-scope del_pending addr, totallen for all addresses is %d\n", totallen); + } } /* Create an asconf chunk with the required length. */ @@ -2802,6 +2810,17 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, addr_buf += af->sockaddr_len; } + if (flags == SCTP_PARAM_ADD_IP && del_pickup) { + addr = asoc->asconf_addr_del_pending; + af = sctp_get_af_specific(addr->v4.sin_family); + addr_param_len = af->to_addr_param(addr, &addr_param); + param.param_hdr.type = SCTP_PARAM_DEL_IP; + param.param_hdr.length = htons(paramlen + addr_param_len); + param.crr_id = i; + + sctp_addto_chunk(retval, paramlen, ¶m); + sctp_addto_chunk(retval, addr_param_len, &addr_param); + } return retval; } @@ -3224,6 +3243,11 @@ static void sctp_asconf_param_success(struct sctp_association *asoc, case SCTP_PARAM_DEL_IP: local_bh_disable(); sctp_del_bind_addr(bp, &addr); + if (asoc->asconf_addr_del_pending != NULL && + sctp_cmp_addr_exact(asoc->asconf_addr_del_pending, &addr)) { + kfree(asoc->asconf_addr_del_pending); + asoc->asconf_addr_del_pending = NULL; + } local_bh_enable(); list_for_each_entry(transport, &asoc->peer.transport_addr_list, transports) { @@ -3381,6 +3405,9 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, asconf_len -= length; } + if (no_err && asoc->src_out_of_asoc_ok) + asoc->src_out_of_asoc_ok = 0; + /* Free the cached last sent asconf chunk. */ list_del_init(&asconf->transmitted_list); sctp_chunk_free(asconf); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index cc06198dc444..e7e1b142875c 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -583,10 +583,6 @@ static int sctp_send_asconf_add_ip(struct sock *sk, goto out; } - retval = sctp_send_asconf(asoc, chunk); - if (retval) - goto out; - /* Add the new addresses to the bind address list with * use_as_src set to 0. */ @@ -599,6 +595,23 @@ static int sctp_send_asconf_add_ip(struct sock *sk, SCTP_ADDR_NEW, GFP_ATOMIC); addr_buf += af->sockaddr_len; } + if (asoc->src_out_of_asoc_ok) { + struct sctp_transport *trans; + + list_for_each_entry(trans, + &asoc->peer.transport_addr_list, transports) { + /* Clear the source and route cache */ + dst_release(trans->dst); + trans->cwnd = min(4*asoc->pathmtu, max_t(__u32, + 2*asoc->pathmtu, 4380)); + trans->ssthresh = asoc->peer.i.a_rwnd; + trans->rto = asoc->rto_initial; + trans->rtt = trans->srtt = trans->rttvar = 0; + sctp_transport_route(trans, NULL, + sctp_sk(asoc->base.sk)); + } + } + retval = sctp_send_asconf(asoc, chunk); } out: @@ -715,7 +728,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk, struct sctp_sockaddr_entry *saddr; int i; int retval = 0; + int stored = 0; + chunk = NULL; if (!sctp_addip_enable) return retval; @@ -766,8 +781,33 @@ static int sctp_send_asconf_del_ip(struct sock *sk, bp = &asoc->base.bind_addr; laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs, addrcnt, sp); - if (!laddr) - continue; + if ((laddr == NULL) && (addrcnt == 1)) { + if (asoc->asconf_addr_del_pending) + continue; + asoc->asconf_addr_del_pending = + kzalloc(sizeof(union sctp_addr), GFP_ATOMIC); + asoc->asconf_addr_del_pending->sa.sa_family = + addrs->sa_family; + asoc->asconf_addr_del_pending->v4.sin_port = + htons(bp->port); + if (addrs->sa_family == AF_INET) { + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)addrs; + asoc->asconf_addr_del_pending->v4.sin_addr.s_addr = sin->sin_addr.s_addr; + } else if (addrs->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)addrs; + ipv6_addr_copy(&asoc->asconf_addr_del_pending->v6.sin6_addr, &sin6->sin6_addr); + } + SCTP_DEBUG_PRINTK_IPADDR("send_asconf_del_ip: keep the last address asoc: %p ", + " at %p\n", asoc, asoc->asconf_addr_del_pending, + asoc->asconf_addr_del_pending); + asoc->src_out_of_asoc_ok = 1; + stored = 1; + goto skip_mkasconf; + } /* We do not need RCU protection throughout this loop * because this is done under a socket lock from the @@ -780,6 +820,7 @@ static int sctp_send_asconf_del_ip(struct sock *sk, goto out; } +skip_mkasconf: /* Reset use_as_src flag for the addresses in the bind address * list that are to be deleted. */ @@ -805,6 +846,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk, sctp_sk(asoc->base.sk)); } + if (stored) + /* We don't need to transmit ASCONF */ + continue; retval = sctp_send_asconf(asoc, chunk); } out: -- cgit v1.2.3 From b36a968927b789b2dd8de0aaf7a72ef7c1f0d012 Mon Sep 17 00:00:00 2001 From: Chris Metcalf Date: Thu, 2 Jun 2011 14:21:45 -0400 Subject: asm-generic/unistd.h: support sendmmsg syscall Signed-off-by: Chris Metcalf Acked-by: Arnd Bergmann --- include/asm-generic/unistd.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-generic/unistd.h b/include/asm-generic/unistd.h index ae90e0f63995..4f76959397fa 100644 --- a/include/asm-generic/unistd.h +++ b/include/asm-generic/unistd.h @@ -683,9 +683,11 @@ __SC_COMP(__NR_clock_adjtime, sys_clock_adjtime, compat_sys_clock_adjtime) __SYSCALL(__NR_syncfs, sys_syncfs) #define __NR_setns 268 __SYSCALL(__NR_setns, sys_setns) +#define __NR_sendmmsg 269 +__SC_COMP(__NR_sendmmsg, sys_sendmmsg, compat_sys_sendmmsg) #undef __NR_syscalls -#define __NR_syscalls 269 +#define __NR_syscalls 270 /* * All syscalls below here should go away really, -- cgit v1.2.3 From 796d5116c407690b14fd5bda136aa67a39e7061a Mon Sep 17 00:00:00 2001 From: Jeff Moyer Date: Thu, 2 Jun 2011 21:19:05 +0200 Subject: iosched: prevent aliased requests from starving other I/O Hi, Jens, If you recall, I posted an RFC patch for this back in July of last year: http://lkml.org/lkml/2010/7/13/279 The basic problem is that a process can issue a never-ending stream of async direct I/Os to the same sector on a device, thus starving out other I/O in the system (due to the way the alias handling works in both cfq and deadline). The solution I proposed back then was to start dispatching from the fifo after a certain number of aliases had been dispatched. Vivek asked why we had to treat aliases differently at all, and I never had a good answer. So, I put together a simple patch which allows aliases to be added to the rb tree (it adds them to the right, though that doesn't matter as the order isn't guaranteed anyway). I think this is the preferred solution, as it doesn't break up time slices in CFQ or batches in deadline. I've tested it, and it does solve the starvation issue. Let me know what you think. Cheers, Jeff Signed-off-by: Jeff Moyer Signed-off-by: Jens Axboe --- block/cfq-iosched.c | 9 ++------- block/deadline-iosched.c | 4 +--- block/elevator.c | 7 ++----- include/linux/elevator.h | 2 +- 4 files changed, 6 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 7c52d6888924..a2fb14bb1402 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -1501,16 +1501,11 @@ static void cfq_add_rq_rb(struct request *rq) { struct cfq_queue *cfqq = RQ_CFQQ(rq); struct cfq_data *cfqd = cfqq->cfqd; - struct request *__alias, *prev; + struct request *prev; cfqq->queued[rq_is_sync(rq)]++; - /* - * looks a little odd, but the first insert might return an alias. - * if that happens, put the alias on the dispatch list - */ - while ((__alias = elv_rb_add(&cfqq->sort_list, rq)) != NULL) - cfq_dispatch_insert(cfqd->queue, __alias); + elv_rb_add(&cfqq->sort_list, rq); if (!cfq_cfqq_on_rr(cfqq)) cfq_add_cfqq_rr(cfqd, cfqq); diff --git a/block/deadline-iosched.c b/block/deadline-iosched.c index 5139c0ea1864..c644137d9cd6 100644 --- a/block/deadline-iosched.c +++ b/block/deadline-iosched.c @@ -77,10 +77,8 @@ static void deadline_add_rq_rb(struct deadline_data *dd, struct request *rq) { struct rb_root *root = deadline_rb_root(dd, rq); - struct request *__alias; - while (unlikely(__alias = elv_rb_add(root, rq))) - deadline_move_request(dd, __alias); + elv_rb_add(root, rq); } static inline void diff --git a/block/elevator.c b/block/elevator.c index b0b38ce0dcb6..a3b64bc71d88 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -353,7 +353,7 @@ static struct request *elv_rqhash_find(struct request_queue *q, sector_t offset) * RB-tree support functions for inserting/lookup/removal of requests * in a sorted RB tree. */ -struct request *elv_rb_add(struct rb_root *root, struct request *rq) +void elv_rb_add(struct rb_root *root, struct request *rq) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; @@ -365,15 +365,12 @@ struct request *elv_rb_add(struct rb_root *root, struct request *rq) if (blk_rq_pos(rq) < blk_rq_pos(__rq)) p = &(*p)->rb_left; - else if (blk_rq_pos(rq) > blk_rq_pos(__rq)) + else if (blk_rq_pos(rq) >= blk_rq_pos(__rq)) p = &(*p)->rb_right; - else - return __rq; } rb_link_node(&rq->rb_node, parent, p); rb_insert_color(&rq->rb_node, root); - return NULL; } EXPORT_SYMBOL(elv_rb_add); diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 21a8ebf2dc3a..d800d5142184 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -146,7 +146,7 @@ extern struct request *elv_rb_latter_request(struct request_queue *, struct requ /* * rb support functions. */ -extern struct request *elv_rb_add(struct rb_root *, struct request *); +extern void elv_rb_add(struct rb_root *, struct request *); extern void elv_rb_del(struct rb_root *, struct request *); extern struct request *elv_rb_find(struct rb_root *, sector_t); -- cgit v1.2.3 From ec764bf083a6ff396234351b51fd236f53c903bf Mon Sep 17 00:00:00 2001 From: Koki Sanagi Date: Mon, 30 May 2011 21:48:34 +0000 Subject: net: tracepoint of net_dev_xmit sees freed skb and causes panic Because there is a possibility that skb is kfree_skb()ed and zero cleared after ndo_start_xmit, we should not see the contents of skb like skb->len and skb->dev->name after ndo_start_xmit. But trace_net_dev_xmit does that and causes panic by NULL pointer dereference. This patch fixes trace_net_dev_xmit not to see the contents of skb directly. If you want to reproduce this panic, 1. Get tracepoint of net_dev_xmit on 2. Create 2 guests on KVM 2. Make 2 guests use virtio_net 4. Execute netperf from one to another for a long time as a network burden 5. host will panic(It takes about 30 minutes) Signed-off-by: Koki Sanagi Signed-off-by: David S. Miller --- include/trace/events/net.h | 12 +++++++----- net/core/dev.c | 7 +++++-- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/trace/events/net.h b/include/trace/events/net.h index 5f247f5ffc56..f99645d05a8f 100644 --- a/include/trace/events/net.h +++ b/include/trace/events/net.h @@ -12,22 +12,24 @@ TRACE_EVENT(net_dev_xmit, TP_PROTO(struct sk_buff *skb, - int rc), + int rc, + struct net_device *dev, + unsigned int skb_len), - TP_ARGS(skb, rc), + TP_ARGS(skb, rc, dev, skb_len), TP_STRUCT__entry( __field( void *, skbaddr ) __field( unsigned int, len ) __field( int, rc ) - __string( name, skb->dev->name ) + __string( name, dev->name ) ), TP_fast_assign( __entry->skbaddr = skb; - __entry->len = skb->len; + __entry->len = skb_len; __entry->rc = rc; - __assign_str(name, skb->dev->name); + __assign_str(name, dev->name); ), TP_printk("dev=%s skbaddr=%p len=%u rc=%d", diff --git a/net/core/dev.c b/net/core/dev.c index c7e305d13b71..939307891e71 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2096,6 +2096,7 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, { const struct net_device_ops *ops = dev->netdev_ops; int rc = NETDEV_TX_OK; + unsigned int skb_len; if (likely(!skb->next)) { u32 features; @@ -2146,8 +2147,9 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, } } + skb_len = skb->len; rc = ops->ndo_start_xmit(skb, dev); - trace_net_dev_xmit(skb, rc); + trace_net_dev_xmit(skb, rc, dev, skb_len); if (rc == NETDEV_TX_OK) txq_trans_update(txq); return rc; @@ -2167,8 +2169,9 @@ gso: if (dev->priv_flags & IFF_XMIT_DST_RELEASE) skb_dst_drop(nskb); + skb_len = nskb->len; rc = ops->ndo_start_xmit(nskb, dev); - trace_net_dev_xmit(nskb, rc); + trace_net_dev_xmit(nskb, rc, dev, skb_len); if (unlikely(rc != NETDEV_TX_OK)) { if (rc & ~NETDEV_TX_MASK) goto out_kfree_gso_skb; -- cgit v1.2.3 From 3a43e05f4d0600e906fa09f4a65d749288c44592 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 31 May 2011 08:56:11 +0200 Subject: irq: Handle spurios irq detection for threaded irqs The detection of spurios interrupts is currently limited to first level handler. In force-threaded mode we never notice if the threaded irq does not feel responsible. This patch catches the return value of the threaded handler and forwards it to the spurious detector. If the primary handler returns only IRQ_WAKE_THREAD then the spourious detector ignores it because it gets called again from the threaded handler. [ tglx: Report the erroneous return value early and bail out ] Signed-off-by: Sebastian Andrzej Siewior Link: http://lkml.kernel.org/r/1306824972-27067-2-git-send-email-sebastian@breakpoint.cc Signed-off-by: Thomas Gleixner --- include/linux/irqreturn.h | 6 +++--- kernel/irq/handle.c | 6 ------ kernel/irq/manage.c | 24 ++++++++++++++++++------ kernel/irq/spurious.c | 22 ++++++++++++++++++---- 4 files changed, 39 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/irqreturn.h b/include/linux/irqreturn.h index 819acaaac3f5..714ba08dc092 100644 --- a/include/linux/irqreturn.h +++ b/include/linux/irqreturn.h @@ -8,9 +8,9 @@ * @IRQ_WAKE_THREAD handler requests to wake the handler thread */ enum irqreturn { - IRQ_NONE, - IRQ_HANDLED, - IRQ_WAKE_THREAD, + IRQ_NONE = (0 << 0), + IRQ_HANDLED = (1 << 0), + IRQ_WAKE_THREAD = (1 << 1), }; typedef enum irqreturn irqreturn_t; diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index 90cb55f6d7eb..470d08c82bbe 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c @@ -132,12 +132,6 @@ handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action) switch (res) { case IRQ_WAKE_THREAD: - /* - * Set result to handled so the spurious check - * does not trigger. - */ - res = IRQ_HANDLED; - /* * Catch drivers which return WAKE_THREAD but * did not set up a thread function diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index f7ce0021e1c4..d64bafb1afd0 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -723,13 +723,16 @@ irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action) { } * context. So we need to disable bh here to avoid deadlocks and other * side effects. */ -static void +static irqreturn_t irq_forced_thread_fn(struct irq_desc *desc, struct irqaction *action) { + irqreturn_t ret; + local_bh_disable(); - action->thread_fn(action->irq, action->dev_id); + ret = action->thread_fn(action->irq, action->dev_id); irq_finalize_oneshot(desc, action, false); local_bh_enable(); + return ret; } /* @@ -737,10 +740,14 @@ irq_forced_thread_fn(struct irq_desc *desc, struct irqaction *action) * preemtible - many of them need to sleep and wait for slow busses to * complete. */ -static void irq_thread_fn(struct irq_desc *desc, struct irqaction *action) +static irqreturn_t irq_thread_fn(struct irq_desc *desc, + struct irqaction *action) { - action->thread_fn(action->irq, action->dev_id); + irqreturn_t ret; + + ret = action->thread_fn(action->irq, action->dev_id); irq_finalize_oneshot(desc, action, false); + return ret; } /* @@ -753,7 +760,8 @@ static int irq_thread(void *data) }; struct irqaction *action = data; struct irq_desc *desc = irq_to_desc(action->irq); - void (*handler_fn)(struct irq_desc *desc, struct irqaction *action); + irqreturn_t (*handler_fn)(struct irq_desc *desc, + struct irqaction *action); int wake; if (force_irqthreads & test_bit(IRQTF_FORCED_THREAD, @@ -783,8 +791,12 @@ static int irq_thread(void *data) desc->istate |= IRQS_PENDING; raw_spin_unlock_irq(&desc->lock); } else { + irqreturn_t action_ret; + raw_spin_unlock_irq(&desc->lock); - handler_fn(desc, action); + action_ret = handler_fn(desc, action); + if (!noirqdebug) + note_interrupt(action->irq, desc, action_ret); } wake = atomic_dec_and_test(&desc->threads_active); diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index c9a78ba30b6f..aa57d5da18c1 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -167,6 +167,13 @@ out: jiffies + POLL_SPURIOUS_IRQ_INTERVAL); } +static inline int bad_action_ret(irqreturn_t action_ret) +{ + if (likely(action_ret <= (IRQ_HANDLED | IRQ_WAKE_THREAD))) + return 0; + return 1; +} + /* * If 99,900 of the previous 100,000 interrupts have not been handled * then assume that the IRQ is stuck in some manner. Drop a diagnostic @@ -182,7 +189,7 @@ __report_bad_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *action; unsigned long flags; - if (action_ret != IRQ_HANDLED && action_ret != IRQ_NONE) { + if (bad_action_ret(action_ret)) { printk(KERN_ERR "irq event %d: bogus return value %x\n", irq, action_ret); } else { @@ -263,7 +270,16 @@ void note_interrupt(unsigned int irq, struct irq_desc *desc, if (desc->istate & IRQS_POLL_INPROGRESS) return; - if (unlikely(action_ret != IRQ_HANDLED)) { + /* we get here again via the threaded handler */ + if (action_ret == IRQ_WAKE_THREAD) + return; + + if (bad_action_ret(action_ret)) { + report_bad_irq(irq, desc, action_ret); + return; + } + + if (unlikely(action_ret == IRQ_NONE)) { /* * If we are seeing only the odd spurious IRQ caused by * bus asynchronicity then don't eventually trigger an error, @@ -275,8 +291,6 @@ void note_interrupt(unsigned int irq, struct irq_desc *desc, else desc->irqs_unhandled++; desc->last_unhandled = jiffies; - if (unlikely(action_ret != IRQ_NONE)) - report_bad_irq(irq, desc, action_ret); } if (unlikely(try_misrouted_irq(irq, desc, action_ret))) { -- cgit v1.2.3 From d4d84fef6d0366b585b7de13527a0faeca84d9ce Mon Sep 17 00:00:00 2001 From: Chris Metcalf Date: Thu, 2 Jun 2011 10:19:41 -0400 Subject: slub: always align cpu_slab to honor cmpxchg_double requirement On an architecture without CMPXCHG_LOCAL but with DEBUG_VM enabled, the VM_BUG_ON() in __pcpu_double_call_return_bool() will cause an early panic during boot unless we always align cpu_slab properly. In principle we could remove the alignment-testing VM_BUG_ON() for architectures that don't have CMPXCHG_LOCAL, but leaving it in means that new code will tend not to break x86 even if it is introduced on another platform, and it's low cost to require alignment. Acked-by: David Rientjes Acked-by: Christoph Lameter Signed-off-by: Chris Metcalf Signed-off-by: Pekka Enberg --- include/linux/percpu.h | 3 +++ mm/slub.c | 12 ++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/percpu.h b/include/linux/percpu.h index 8b97308e65df..9ca008f0c542 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -259,6 +259,9 @@ extern void __bad_size_call_parameter(void); * Special handling for cmpxchg_double. cmpxchg_double is passed two * percpu variables. The first has to be aligned to a double word * boundary and the second has to follow directly thereafter. + * We enforce this on all architectures even if they don't support + * a double cmpxchg instruction, since it's a cheap requirement, and it + * avoids breaking the requirement for architectures with the instruction. */ #define __pcpu_double_call_return_bool(stem, pcp1, pcp2, ...) \ ({ \ diff --git a/mm/slub.c b/mm/slub.c index 7be0223531b0..35f351f26193 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2320,16 +2320,12 @@ static inline int alloc_kmem_cache_cpus(struct kmem_cache *s) BUILD_BUG_ON(PERCPU_DYNAMIC_EARLY_SIZE < SLUB_PAGE_SHIFT * sizeof(struct kmem_cache_cpu)); -#ifdef CONFIG_CMPXCHG_LOCAL /* - * Must align to double word boundary for the double cmpxchg instructions - * to work. + * Must align to double word boundary for the double cmpxchg + * instructions to work; see __pcpu_double_call_return_bool(). */ - s->cpu_slab = __alloc_percpu(sizeof(struct kmem_cache_cpu), 2 * sizeof(void *)); -#else - /* Regular alignment is sufficient */ - s->cpu_slab = alloc_percpu(struct kmem_cache_cpu); -#endif + s->cpu_slab = __alloc_percpu(sizeof(struct kmem_cache_cpu), + 2 * sizeof(void *)); if (!s->cpu_slab) return 0; -- cgit v1.2.3 From a3cc68c37897f0656489a0d853d6e342fc6f076b Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 27 May 2011 16:35:59 -0700 Subject: gpio/74x164: remove unnecessary defines and prototype Remove the #define GEN_74X164_GPIO_COUNT since it's only used in one place and it's meaning is obvious. Also remove the #define GEN_74X164_DRIVER_NAME and use spi->modalias to set the gpio chip's label and the string "74x164" for the driver name. Reorder the code slightly to remove the need to prototype gen_74x164_set_value. Signed-off-by: H Hartley Sweeten Signed-off-by: Grant Likely --- drivers/gpio/74x164.c | 27 +++++++++++---------------- include/linux/spi/74x164.h | 2 -- 2 files changed, 11 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/gpio/74x164.c b/drivers/gpio/74x164.c index 84e070219839..7fb60b6bf523 100644 --- a/drivers/gpio/74x164.c +++ b/drivers/gpio/74x164.c @@ -16,9 +16,6 @@ #include #include -#define GEN_74X164_GPIO_COUNT 8 - - struct gen_74x164_chip { struct spi_device *spi; struct gpio_chip gpio_chip; @@ -26,8 +23,6 @@ struct gen_74x164_chip { u8 port_config; }; -static void gen_74x164_set_value(struct gpio_chip *, unsigned, int); - static struct gen_74x164_chip *gpio_to_chip(struct gpio_chip *gc) { return container_of(gc, struct gen_74x164_chip, gpio_chip); @@ -39,13 +34,6 @@ static int __gen_74x164_write_config(struct gen_74x164_chip *chip) &chip->port_config, sizeof(chip->port_config)); } -static int gen_74x164_direction_output(struct gpio_chip *gc, - unsigned offset, int val) -{ - gen_74x164_set_value(gc, offset, val); - return 0; -} - static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) { struct gen_74x164_chip *chip = gpio_to_chip(gc); @@ -73,6 +61,13 @@ static void gen_74x164_set_value(struct gpio_chip *gc, mutex_unlock(&chip->lock); } +static int gen_74x164_direction_output(struct gpio_chip *gc, + unsigned offset, int val) +{ + gen_74x164_set_value(gc, offset, val); + return 0; +} + static int __devinit gen_74x164_probe(struct spi_device *spi) { struct gen_74x164_chip *chip; @@ -104,12 +99,12 @@ static int __devinit gen_74x164_probe(struct spi_device *spi) chip->spi = spi; - chip->gpio_chip.label = GEN_74X164_DRIVER_NAME, - chip->gpio_chip.direction_output = gen_74x164_direction_output; + chip->gpio_chip.label = spi->modalias; + chip->gpio_chip.direction_output = gen_74x164_direction_output; chip->gpio_chip.get = gen_74x164_get_value; chip->gpio_chip.set = gen_74x164_set_value; chip->gpio_chip.base = pdata->base; - chip->gpio_chip.ngpio = GEN_74X164_GPIO_COUNT; + chip->gpio_chip.ngpio = 8; chip->gpio_chip.can_sleep = 1; chip->gpio_chip.dev = &spi->dev; chip->gpio_chip.owner = THIS_MODULE; @@ -157,7 +152,7 @@ static int __devexit gen_74x164_remove(struct spi_device *spi) static struct spi_driver gen_74x164_driver = { .driver = { - .name = GEN_74X164_DRIVER_NAME, + .name = "74x164", .owner = THIS_MODULE, }, .probe = gen_74x164_probe, diff --git a/include/linux/spi/74x164.h b/include/linux/spi/74x164.h index d85c52f294a0..0aa6acc73317 100644 --- a/include/linux/spi/74x164.h +++ b/include/linux/spi/74x164.h @@ -1,8 +1,6 @@ #ifndef LINUX_SPI_74X164_H #define LINUX_SPI_74X164_H -#define GEN_74X164_DRIVER_NAME "74x164" - struct gen_74x164_chip_platform_data { /* number assigned to the first GPIO */ unsigned base; -- cgit v1.2.3 From 7150962d637cf38617924f7f72ea00612283eb89 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 31 May 2011 11:22:15 +0200 Subject: lib: crc8: add new library module providing crc8 algorithm The brcm80211 driver in staging tree uses a crc8 function. Based on feedback from John Linville to move this to lib directory, the linux source has been searched. Although there is currently only one other kernel driver using this algorithm (ie. drivers/ssb) we are providing this as a library function for others to use. Cc: linux-kernel@vger.kernel.org Cc: linux-wireless@vger.kernel.org Cc: Dan Carpenter Cc: George Spelvin Cc: Randy Dunlap Reviewed-by: Henry Ptasinski Reviewed-by: Roland Vossen Reviewed-by: "Franky (Zhenhui) Lin" Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- include/linux/crc8.h | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/Kconfig | 7 ++++ lib/Makefile | 1 + lib/crc8.c | 86 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 include/linux/crc8.h create mode 100644 lib/crc8.c (limited to 'include') diff --git a/include/linux/crc8.h b/include/linux/crc8.h new file mode 100644 index 000000000000..13c8dabb0441 --- /dev/null +++ b/include/linux/crc8.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2011 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, 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. + */ +#ifndef __CRC8_H_ +#define __CRC8_H_ + +#include + +/* see usage of this value in crc8() description */ +#define CRC8_INIT_VALUE 0xFF + +/* + * Return value of crc8() indicating valid message+crc. This is true + * if a CRC is inverted before transmission. The CRC computed over the + * whole received bitstream is _table[x], where x is the bit pattern + * of the modification (almost always 0xff). + */ +#define CRC8_GOOD_VALUE(_table) (_table[0xFF]) + +/* required table size for crc8 algorithm */ +#define CRC8_TABLE_SIZE 256 + +/* helper macro assuring right table size is used */ +#define DECLARE_CRC8_TABLE(_table) \ + static u8 _table[CRC8_TABLE_SIZE] + +/** + * crc8_populate_lsb - fill crc table for given polynomial in regular bit order. + * + * @table: table to be filled. + * @polynomial: polynomial for which table is to be filled. + * + * This function fills the provided table according the polynomial provided for + * regular bit order (lsb first). Polynomials in CRC algorithms are typically + * represented as shown below. + * + * poly = x^8 + x^7 + x^6 + x^4 + x^2 + 1 + * + * For lsb first direction x^7 maps to the lsb. So the polynomial is as below. + * + * - lsb first: poly = 10101011(1) = 0xAB + */ +void crc8_populate_lsb(u8 table[CRC8_TABLE_SIZE], u8 polynomial); + +/** + * crc8_populate_msb - fill crc table for given polynomial in reverse bit order. + * + * @table: table to be filled. + * @polynomial: polynomial for which table is to be filled. + * + * This function fills the provided table according the polynomial provided for + * reverse bit order (msb first). Polynomials in CRC algorithms are typically + * represented as shown below. + * + * poly = x^8 + x^7 + x^6 + x^4 + x^2 + 1 + * + * For msb first direction x^7 maps to the msb. So the polynomial is as below. + * + * - msb first: poly = (1)11010101 = 0xD5 + */ +void crc8_populate_msb(u8 table[CRC8_TABLE_SIZE], u8 polynomial); + +/** + * crc8() - calculate a crc8 over the given input data. + * + * @table: crc table used for calculation. + * @pdata: pointer to data buffer. + * @nbytes: number of bytes in data buffer. + * @crc: previous returned crc8 value. + * + * The CRC8 is calculated using the polynomial given in crc8_populate_msb() + * or crc8_populate_lsb(). + * + * The caller provides the initial value (either %CRC8_INIT_VALUE + * or the previous returned value) to allow for processing of + * discontiguous blocks of data. When generating the CRC the + * caller is responsible for complementing the final return value + * and inserting it into the byte stream. When validating a byte + * stream (including CRC8), a final return value of %CRC8_GOOD_VALUE + * indicates the byte stream data can be considered valid. + * + * Reference: + * "A Painless Guide to CRC Error Detection Algorithms", ver 3, Aug 1993 + * Williams, Ross N., rossross.net + * (see URL http://www.ross.net/crc/download/crc_v3.txt). + */ +u8 crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, size_t nbytes, u8 crc); + +#endif /* __CRC8_H_ */ diff --git a/lib/Kconfig b/lib/Kconfig index 9c10e38fc609..ff9e5a34be76 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -89,6 +89,13 @@ config LIBCRC32C require M here. See Castagnoli93. Module will be libcrc32c. +config CRC8 + tristate "CRC8 function" + help + This option provides CRC8 function. Drivers may select this + when they need to do cyclic redundancy check according CRC8 + algorithm. Module will be called crc8. + config AUDIT_GENERIC bool depends on AUDIT && !AUDIT_ARCH diff --git a/lib/Makefile b/lib/Makefile index 4b49a249064b..704959d2018b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_CRC_ITU_T) += crc-itu-t.o obj-$(CONFIG_CRC32) += crc32.o obj-$(CONFIG_CRC7) += crc7.o obj-$(CONFIG_LIBCRC32C) += libcrc32c.o +obj-$(CONFIG_CRC8) += crc8.o obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate/ diff --git a/lib/crc8.c b/lib/crc8.c new file mode 100644 index 000000000000..87b59cafdb83 --- /dev/null +++ b/lib/crc8.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2011 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +/* + * crc8_populate_msb - fill crc table for given polynomial in reverse bit order. + * + * table: table to be filled. + * polynomial: polynomial for which table is to be filled. + */ +void crc8_populate_msb(u8 table[CRC8_TABLE_SIZE], u8 polynomial) +{ + int i, j; + const u8 msbit = 0x80; + u8 t = msbit; + + table[0] = 0; + + for (i = 1; i < CRC8_TABLE_SIZE; i *= 2) { + t = (t << 1) ^ (t & msbit ? polynomial : 0); + for (j = 0; j < i; j++) + table[i+j] = table[j] ^ t; + } +} +EXPORT_SYMBOL(crc8_populate_msb); + +/* + * crc8_populate_lsb - fill crc table for given polynomial in regular bit order. + * + * table: table to be filled. + * polynomial: polynomial for which table is to be filled. + */ +void crc8_populate_lsb(u8 table[CRC8_TABLE_SIZE], u8 polynomial) +{ + int i, j; + u8 t = 1; + + table[0] = 0; + + for (i = (CRC8_TABLE_SIZE >> 1); i; i >>= 1) { + t = (t >> 1) ^ (t & 1 ? polynomial : 0); + for (j = 0; j < CRC8_TABLE_SIZE; j += 2*i) + table[i+j] = table[j] ^ t; + } +} +EXPORT_SYMBOL(crc8_populate_lsb); + +/* + * crc8 - calculate a crc8 over the given input data. + * + * table: crc table used for calculation. + * pdata: pointer to data buffer. + * nbytes: number of bytes in data buffer. + * crc: previous returned crc8 value. + */ +u8 crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, size_t nbytes, u8 crc) +{ + /* loop over the buffer data */ + while (nbytes-- > 0) + crc = table[(crc ^ *pdata++) & 0xff]; + + return crc; +} +EXPORT_SYMBOL(crc8); + +MODULE_DESCRIPTION("CRC8 (by Williams, Ross N.) function"); +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_LICENSE("Dual BSD/GPL"); -- cgit v1.2.3 From 10f8113ecb76eea72f96c7cfb72d7fed7c282565 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 31 May 2011 11:22:16 +0200 Subject: lib: cordic: add library module providing cordic angle calculation The brcm80211 driver in the staging tree has a cordic function to determine cosine and sine for a given angle. Feedback received from John Linville suggested that these kind of functions should be made available to others as a library function in the kernel tree. The b43 driver also has a cordic angle calculation implemented. Cc: linux-kernel@vger.kernel.org Cc: linux-wireless@vger.kernel.org Cc: "John W. Linville" Cc: Greg Kroah-Hartman Cc: Dan Carpenter Cc: Randy Dunlap Cc: Larry Finger Reviewed-by: Roland Vossen Reviewed-by: Henry Ptasinski Reviewed-by: Franky (Zhenhui) Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- include/linux/cordic.h | 48 +++++++++++++++++++++++ lib/Kconfig | 7 ++++ lib/Makefile | 2 + lib/cordic.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 include/linux/cordic.h create mode 100644 lib/cordic.c (limited to 'include') diff --git a/include/linux/cordic.h b/include/linux/cordic.h new file mode 100644 index 000000000000..f932093e20c2 --- /dev/null +++ b/include/linux/cordic.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, 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. + */ +#ifndef __CORDIC_H_ +#define __CORDIC_H_ + +#include + +/** + * struct cordic_iq - i/q coordinate. + * + * @i: real part of coordinate (in phase). + * @q: imaginary part of coordinate (quadrature). + */ +struct cordic_iq { + s32 i; + s32 q; +}; + +/** + * cordic_calc_iq() - calculates the i/q coordinate for given angle. + * + * @theta: angle in degrees for which i/q coordinate is to be calculated. + * @coord: function output parameter holding the i/q coordinate. + * + * The function calculates the i/q coordinate for a given angle using + * cordic algorithm. The coordinate consists of a real (i) and an + * imaginary (q) part. The real part is essentially the cosine of the + * angle and the imaginary part is the sine of the angle. The returned + * values are scaled by 2^16 for precision. The range for theta is + * for -180 degrees to +180 degrees. Passed values outside this range are + * converted before doing the actual calculation. + */ +struct cordic_iq cordic_calc_iq(s32 theta); + +#endif /* __CORDIC_H_ */ diff --git a/lib/Kconfig b/lib/Kconfig index ff9e5a34be76..5c702047c57f 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -279,4 +279,11 @@ config AVERAGE If unsure, say N. +config CORDIC + tristate "Cordic function" + help + The option provides arithmetic function using cordic algorithm + so its calculations are in fixed point. Modules can select this + when they require this function. Module will be called cordic. + endmenu diff --git a/lib/Makefile b/lib/Makefile index 704959d2018b..9e3c1b0652d1 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -117,6 +117,8 @@ obj-$(CONFIG_AVERAGE) += average.o obj-$(CONFIG_CPU_RMAP) += cpu_rmap.o +obj-$(CONFIG_CORDIC) += cordic.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/cordic.c b/lib/cordic.c new file mode 100644 index 000000000000..aa27a88d7e04 --- /dev/null +++ b/lib/cordic.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2011 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, 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. + */ +#include +#include + +#define CORDIC_ANGLE_GEN 39797 +#define CORDIC_PRECISION_SHIFT 16 +#define CORDIC_NUM_ITER (CORDIC_PRECISION_SHIFT + 2) + +#define FIXED(X) ((s32)((X) << CORDIC_PRECISION_SHIFT)) +#define FLOAT(X) (((X) >= 0) \ + ? ((((X) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1) \ + : -((((-(X)) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1)) + +static const s32 arctan_table[] = { + 2949120, + 1740967, + 919879, + 466945, + 234379, + 117304, + 58666, + 29335, + 14668, + 7334, + 3667, + 1833, + 917, + 458, + 229, + 115, + 57, + 29 +}; + +/* + * cordic_calc_iq() - calculates the i/q coordinate for given angle + * + * theta: angle in degrees for which i/q coordinate is to be calculated + * coord: function output parameter holding the i/q coordinate + */ +struct cordic_iq cordic_calc_iq(s32 theta) +{ + struct cordic_iq coord; + s32 angle, valtmp; + unsigned iter; + int signx = 1; + int signtheta; + + coord.i = CORDIC_ANGLE_GEN; + coord.q = 0; + angle = 0; + + theta = FIXED(theta); + signtheta = (theta < 0) ? -1 : 1; + theta = ((theta + FIXED(180) * signtheta) % FIXED(360)) - + FIXED(180) * signtheta; + + if (FLOAT(theta) > 90) { + theta -= FIXED(180); + signx = -1; + } else if (FLOAT(theta) < -90) { + theta += FIXED(180); + signx = -1; + } + + for (iter = 0; iter < CORDIC_NUM_ITER; iter++) { + if (theta > angle) { + valtmp = coord.i - (coord.q >> iter); + coord.q += (coord.i >> iter); + angle += arctan_table[iter]; + } else { + valtmp = coord.i + (coord.q >> iter); + coord.q -= (coord.i >> iter); + angle -= arctan_table[iter]; + } + coord.i = valtmp; + } + + coord.i *= signx; + coord.q *= signx; + return coord; +} +EXPORT_SYMBOL(cordic_calc_iq); + +MODULE_DESCRIPTION("Cordic functions"); +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_LICENSE("Dual BSD/GPL"); -- cgit v1.2.3 From 27f18dc2dafe66a93c7101fc94201b8c83903597 Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Thu, 2 Jun 2011 02:08:51 +0200 Subject: bcma: read SPROM and extract MAC from it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case of BCMA cards SPROM is located in the ChipCommon core, it is not mapped as separated host window. So far we have met only SPROMs rev 8. SPROM layout seems to be the same as for SSB buses, so we decided to share SPROM struct and some defines. For now we extract MAC address only, this can be improved of course. Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: John W. Linville --- drivers/bcma/Makefile | 2 +- drivers/bcma/bcma_private.h | 3 + drivers/bcma/main.c | 7 ++ drivers/bcma/sprom.c | 162 ++++++++++++++++++++++++++++ include/linux/bcma/bcma.h | 5 + include/linux/bcma/bcma_driver_chipcommon.h | 1 + 6 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 drivers/bcma/sprom.c (limited to 'include') diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile index 0d56245bcb79..cde0182bd1dc 100644 --- a/drivers/bcma/Makefile +++ b/drivers/bcma/Makefile @@ -1,4 +1,4 @@ -bcma-y += main.o scan.o core.o +bcma-y += main.o scan.o core.o sprom.o bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o bcma-y += driver_pci.o bcma-$(CONFIG_BCMA_HOST_PCI) += host_pci.o diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 2f72e9c585fd..12a75ab3dd23 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -19,6 +19,9 @@ extern void bcma_bus_unregister(struct bcma_bus *bus); /* scan.c */ int bcma_bus_scan(struct bcma_bus *bus); +/* sprom.c */ +int bcma_sprom_get(struct bcma_bus *bus); + #ifdef CONFIG_BCMA_HOST_PCI /* host_pci.c */ extern int __init bcma_host_pci_init(void); diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index a2f6b1879273..11e96dc6011a 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -146,6 +146,13 @@ int bcma_bus_register(struct bcma_bus *bus) bcma_core_pci_init(&bus->drv_pci); } + /* Try to get SPROM */ + err = bcma_sprom_get(bus); + if (err) { + pr_err("Failed to get SPROM: %d\n", err); + return -ENOENT; + } + /* Register found cores */ bcma_register_cores(bus); diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c new file mode 100644 index 000000000000..ffbb0e32e921 --- /dev/null +++ b/drivers/bcma/sprom.c @@ -0,0 +1,162 @@ +/* + * Broadcom specific AMBA + * SPROM reading + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include "bcma_private.h" + +#include +#include +#include +#include +#include +#include + +#define SPOFF(offset) ((offset) / sizeof(u16)) + +/************************************************** + * R/W ops. + **************************************************/ + +static void bcma_sprom_read(struct bcma_bus *bus, u16 *sprom) +{ + int i; + for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++) + sprom[i] = bcma_read16(bus->drv_cc.core, + BCMA_CC_SPROM + (i * 2)); +} + +/************************************************** + * Validation. + **************************************************/ + +static inline u8 bcma_crc8(u8 crc, u8 data) +{ + /* Polynomial: x^8 + x^7 + x^6 + x^4 + x^2 + 1 */ + static const u8 t[] = { + 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, + 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, + 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, + 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, + 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, + 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, + 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, + 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, + 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, + 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, + 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, + 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, + 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, + 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, + 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, + 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, + 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, + 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, + 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, + 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, + 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, + 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, + 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, + 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, + 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, + 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, + 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, + 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, + 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, + 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, + 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, + 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F, + }; + return t[crc ^ data]; +} + +static u8 bcma_sprom_crc(const u16 *sprom) +{ + int word; + u8 crc = 0xFF; + + for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) { + crc = bcma_crc8(crc, sprom[word] & 0x00FF); + crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8); + } + crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF); + crc ^= 0xFF; + + return crc; +} + +static int bcma_sprom_check_crc(const u16 *sprom) +{ + u8 crc; + u8 expected_crc; + u16 tmp; + + crc = bcma_sprom_crc(sprom); + tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC; + expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT; + if (crc != expected_crc) + return -EPROTO; + + return 0; +} + +static int bcma_sprom_valid(const u16 *sprom) +{ + u16 revision; + int err; + + err = bcma_sprom_check_crc(sprom); + if (err) + return err; + + revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV; + if (revision != 8) { + pr_err("Unsupported SPROM revision: %d\n", revision); + return -ENOENT; + } + + return 0; +} + +/************************************************** + * SPROM extraction. + **************************************************/ + +static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) +{ + u16 v; + int i; + + for (i = 0; i < 3; i++) { + v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i]; + *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v); + } +} + +int bcma_sprom_get(struct bcma_bus *bus) +{ + u16 *sprom; + int err = 0; + + if (!bus->drv_cc.core) + return -EOPNOTSUPP; + + sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), + GFP_KERNEL); + if (!sprom) + return -ENOMEM; + + bcma_sprom_read(bus, sprom); + + err = bcma_sprom_valid(sprom); + if (err) + goto out; + + bcma_sprom_extract_r8(bus, sprom); + +out: + kfree(sprom); + return err; +} diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 27a27a79bea3..6ff080eac0b2 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -6,6 +6,7 @@ #include #include +#include /* SPROM sharing */ #include "bcma_regs.h" @@ -187,6 +188,10 @@ struct bcma_bus { struct bcma_drv_cc drv_cc; struct bcma_drv_pci drv_pci; + + /* We decided to share SPROM struct with SSB as long as we do not need + * any hacks for BCMA. This simplifies drivers code. */ + struct ssb_sprom sprom; }; extern inline u32 bcma_read8(struct bcma_device *core, u16 offset) diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 083c3b6cd5ce..9c5b69fc985a 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -244,6 +244,7 @@ #define BCMA_CC_REGCTL_DATA 0x065C #define BCMA_CC_PLLCTL_ADDR 0x0660 #define BCMA_CC_PLLCTL_DATA 0x0664 +#define BCMA_CC_SPROM 0x0830 /* SPROM beginning */ /* Data for the PMU, if available. * Check availability with ((struct bcma_chipcommon)->capabilities & BCMA_CC_CAP_PMU) -- cgit v1.2.3 From 55db4c64eddf37e31279ec15fe90314713bc9cfa Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 4 Jun 2011 06:33:24 +0900 Subject: Revert "tty: make receive_buf() return the amout of bytes received" This reverts commit b1c43f82c5aa265442f82dba31ce985ebb7aa71c. It was broken in so many ways, and results in random odd pty issues. It re-introduced the buggy schedule_work() in flush_to_ldisc() that can cause endless work-loops (see commit a5660b41af6a: "tty: fix endless work loop when the buffer fills up"). It also used an "unsigned int" return value fo the ->receive_buf() function, but then made multiple functions return a negative error code, and didn't actually check for the error in the caller. And it didn't actually work at all. BenH bisected down odd tty behavior to it: "It looks like the patch is causing some major malfunctions of the X server for me, possibly related to PTYs. For example, cat'ing a large file in a gnome terminal hangs the kernel for -minutes- in a loop of what looks like flush_to_ldisc/workqueue code, (some ftrace data in the quoted bits further down). ... Some more data: It -looks- like what happens is that the flush_to_ldisc work queue entry constantly re-queues itself (because the PTY is full ?) and the workqueue thread will basically loop forver calling it without ever scheduling, thus starving the consumer process that could have emptied the PTY." which is pretty much exactly the problem we fixed in a5660b41af6a. Milton Miller pointed out the 'unsigned int' issue. Reported-by: Benjamin Herrenschmidt Reported-by: Milton Miller Cc: Stefan Bigler Cc: Toby Gray Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: Alan Cox Signed-off-by: Linus Torvalds --- drivers/bluetooth/hci_ldisc.c | 17 ++++------- drivers/input/serio/serport.c | 10 ++----- drivers/isdn/gigaset/ser-gigaset.c | 8 ++--- drivers/misc/ti-st/st_core.c | 6 ++-- drivers/net/caif/caif_serial.c | 6 ++-- drivers/net/can/slcan.c | 9 ++---- drivers/net/hamradio/6pack.c | 8 ++--- drivers/net/hamradio/mkiss.c | 11 +++---- drivers/net/irda/irtty-sir.c | 16 +++++----- drivers/net/ppp_async.c | 6 ++-- drivers/net/ppp_synctty.c | 6 ++-- drivers/net/slip.c | 11 +++---- drivers/net/wan/x25_asy.c | 7 ++--- drivers/tty/n_gsm.c | 6 ++-- drivers/tty/n_hdlc.c | 18 +++++------ drivers/tty/n_r3964.c | 10 +++---- drivers/tty/n_tty.c | 61 +++++++++++++++++++++++++++++--------- drivers/tty/tty_buffer.c | 15 ++++------ drivers/tty/vt/selection.c | 3 +- include/linux/tty_ldisc.h | 9 +++--- 20 files changed, 115 insertions(+), 128 deletions(-) (limited to 'include') diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index b3f01996318f..48ad2a7ab080 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -355,29 +355,24 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty) * flags pointer to flags for data * count count of received data in bytes * - * Return Value: Number of bytes received + * Return Value: None */ -static unsigned int hci_uart_tty_receive(struct tty_struct *tty, - const u8 *data, char *flags, int count) +static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count) { struct hci_uart *hu = (void *)tty->disc_data; - int received; if (!hu || tty != hu->tty) - return -ENODEV; + return; if (!test_bit(HCI_UART_PROTO_SET, &hu->flags)) - return -EINVAL; + return; spin_lock(&hu->rx_lock); - received = hu->proto->recv(hu, (void *) data, count); - if (received > 0) - hu->hdev->stat.byte_rx += received; + hu->proto->recv(hu, (void *) data, count); + hu->hdev->stat.byte_rx += count; spin_unlock(&hu->rx_lock); tty_unthrottle(tty); - - return received; } static int hci_uart_register_dev(struct hci_uart *hu) diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index f3698967edf6..8755f5f3ad37 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -120,21 +120,17 @@ static void serport_ldisc_close(struct tty_struct *tty) * 'interrupt' routine. */ -static unsigned int serport_ldisc_receive(struct tty_struct *tty, - const unsigned char *cp, char *fp, int count) +static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct serport *serport = (struct serport*) tty->disc_data; unsigned long flags; unsigned int ch_flags; - int ret = 0; int i; spin_lock_irqsave(&serport->lock, flags); - if (!test_bit(SERPORT_ACTIVE, &serport->flags)) { - ret = -EINVAL; + if (!test_bit(SERPORT_ACTIVE, &serport->flags)) goto out; - } for (i = 0; i < count; i++) { switch (fp[i]) { @@ -156,8 +152,6 @@ static unsigned int serport_ldisc_receive(struct tty_struct *tty, out: spin_unlock_irqrestore(&serport->lock, flags); - - return ret == 0 ? count : ret; } /* diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index 1d44d470897c..86a5c4f7775e 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -674,7 +674,7 @@ gigaset_tty_ioctl(struct tty_struct *tty, struct file *file, * cflags buffer containing error flags for received characters (ignored) * count number of received characters */ -static unsigned int +static void gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) { @@ -683,12 +683,12 @@ gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, struct inbuf_t *inbuf; if (!cs) - return -ENODEV; + return; inbuf = cs->inbuf; if (!inbuf) { dev_err(cs->dev, "%s: no inbuf\n", __func__); cs_put(cs); - return -EINVAL; + return; } tail = inbuf->tail; @@ -725,8 +725,6 @@ gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, gig_dbg(DEBUG_INTR, "%s-->BH", __func__); gigaset_schedule_event(cs); cs_put(cs); - - return count; } /* diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 1a05fe08e2cb..f91f82eabda7 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -747,8 +747,8 @@ static void st_tty_close(struct tty_struct *tty) pr_debug("%s: done ", __func__); } -static unsigned int st_tty_receive(struct tty_struct *tty, - const unsigned char *data, char *tty_flags, int count) +static void st_tty_receive(struct tty_struct *tty, const unsigned char *data, + char *tty_flags, int count) { #ifdef VERBOSE print_hex_dump(KERN_DEBUG, ">in>", DUMP_PREFIX_NONE, @@ -761,8 +761,6 @@ static unsigned int st_tty_receive(struct tty_struct *tty, */ st_recv(tty->disc_data, data, count); pr_debug("done %s", __func__); - - return count; } /* wake-up function called in from the TTY layer diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index 73c7e03617ec..3df0c0f8b8bf 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -167,8 +167,8 @@ static inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size) #endif -static unsigned int ldisc_receive(struct tty_struct *tty, - const u8 *data, char *flags, int count) +static void ldisc_receive(struct tty_struct *tty, const u8 *data, + char *flags, int count) { struct sk_buff *skb = NULL; struct ser_device *ser; @@ -215,8 +215,6 @@ static unsigned int ldisc_receive(struct tty_struct *tty, } else ++ser->dev->stats.rx_dropped; update_tty_status(ser); - - return count; } static int handle_tx(struct ser_device *ser) diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index 75622d54581f..1b49df6b2470 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -425,17 +425,16 @@ static void slc_setup(struct net_device *dev) * in parallel */ -static unsigned int slcan_receive_buf(struct tty_struct *tty, +static void slcan_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct slcan *sl = (struct slcan *) tty->disc_data; - int bytes = count; if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev)) - return -ENODEV; + return; /* Read the characters out of the buffer */ - while (bytes--) { + while (count--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->dev->stats.rx_errors++; @@ -444,8 +443,6 @@ static unsigned int slcan_receive_buf(struct tty_struct *tty, } slcan_unesc(sl, *cp++); } - - return count; } /************************************ diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 992089639ea4..3e5d0b6b6516 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -456,7 +456,7 @@ out: * a block of 6pack data has been received, which can now be decapsulated * and sent on to some IP layer for further processing. */ -static unsigned int sixpack_receive_buf(struct tty_struct *tty, +static void sixpack_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct sixpack *sp; @@ -464,11 +464,11 @@ static unsigned int sixpack_receive_buf(struct tty_struct *tty, int count1; if (!count) - return 0; + return; sp = sp_get(tty); if (!sp) - return -ENODEV; + return; memcpy(buf, cp, count < sizeof(buf) ? count : sizeof(buf)); @@ -487,8 +487,6 @@ static unsigned int sixpack_receive_buf(struct tty_struct *tty, sp_put(sp); tty_unthrottle(tty); - - return count1; } /* diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 0e4f23531140..4c628393c8b1 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -923,14 +923,13 @@ static long mkiss_compat_ioctl(struct tty_struct *tty, struct file *file, * a block of data has been received, which can now be decapsulated * and sent on to the AX.25 layer for further processing. */ -static unsigned int mkiss_receive_buf(struct tty_struct *tty, - const unsigned char *cp, char *fp, int count) +static void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) { struct mkiss *ax = mkiss_get(tty); - int bytes = count; if (!ax) - return -ENODEV; + return; /* * Argh! mtu change time! - costs us the packet part received @@ -940,7 +939,7 @@ static unsigned int mkiss_receive_buf(struct tty_struct *tty, ax_changedmtu(ax); /* Read the characters out of the buffer */ - while (bytes--) { + while (count--) { if (fp != NULL && *fp++) { if (!test_and_set_bit(AXF_ERROR, &ax->flags)) ax->dev->stats.rx_errors++; @@ -953,8 +952,6 @@ static unsigned int mkiss_receive_buf(struct tty_struct *tty, mkiss_put(ax); tty_unthrottle(tty); - - return count; } /* diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index 035861d8acb1..3352b2443e58 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -216,23 +216,23 @@ static int irtty_do_write(struct sir_dev *dev, const unsigned char *ptr, size_t * usbserial: urb-complete-interrupt / softint */ -static unsigned int irtty_receive_buf(struct tty_struct *tty, - const unsigned char *cp, char *fp, int count) +static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) { struct sir_dev *dev; struct sirtty_cb *priv = tty->disc_data; int i; - IRDA_ASSERT(priv != NULL, return -ENODEV;); - IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -EINVAL;); + IRDA_ASSERT(priv != NULL, return;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); if (unlikely(count==0)) /* yes, this happens */ - return 0; + return; dev = priv->dev; if (!dev) { IRDA_WARNING("%s(), not ready yet!\n", __func__); - return -ENODEV; + return; } for (i = 0; i < count; i++) { @@ -242,13 +242,11 @@ static unsigned int irtty_receive_buf(struct tty_struct *tty, if (fp && *fp++) { IRDA_DEBUG(0, "Framing or parity error!\n"); sirdev_receive(dev, NULL, 0); /* notify sir_dev (updating stats) */ - return -EINVAL; + return; } } sirdev_receive(dev, cp, count); - - return count; } /* diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index 53872d7d7382..a1b82c9c67d2 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -340,7 +340,7 @@ ppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) } /* May sleep, don't call from interrupt level or with interrupts disabled */ -static unsigned int +static void ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) { @@ -348,7 +348,7 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, unsigned long flags; if (!ap) - return -ENODEV; + return; spin_lock_irqsave(&ap->recv_lock, flags); ppp_async_input(ap, buf, cflags, count); spin_unlock_irqrestore(&ap->recv_lock, flags); @@ -356,8 +356,6 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, tasklet_schedule(&ap->tsk); ap_put(ap); tty_unthrottle(tty); - - return count; } static void diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index 0815790a5cf9..2573f525f11c 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -381,7 +381,7 @@ ppp_sync_poll(struct tty_struct *tty, struct file *file, poll_table *wait) } /* May sleep, don't call from interrupt level or with interrupts disabled */ -static unsigned int +static void ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) { @@ -389,7 +389,7 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, unsigned long flags; if (!ap) - return -ENODEV; + return; spin_lock_irqsave(&ap->recv_lock, flags); ppp_sync_input(ap, buf, cflags, count); spin_unlock_irqrestore(&ap->recv_lock, flags); @@ -397,8 +397,6 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, tasklet_schedule(&ap->tsk); sp_put(ap); tty_unthrottle(tty); - - return count; } static void diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 584809c656d5..8ec1a9a0bb9a 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -670,17 +670,16 @@ static void sl_setup(struct net_device *dev) * in parallel */ -static unsigned int slip_receive_buf(struct tty_struct *tty, - const unsigned char *cp, char *fp, int count) +static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) { struct slip *sl = tty->disc_data; - int bytes = count; if (!sl || sl->magic != SLIP_MAGIC || !netif_running(sl->dev)) - return -ENODEV; + return; /* Read the characters out of the buffer */ - while (bytes--) { + while (count--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->dev->stats.rx_errors++; @@ -694,8 +693,6 @@ static unsigned int slip_receive_buf(struct tty_struct *tty, #endif slip_unesc(sl, *cp++); } - - return count; } /************************************ diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 40398bf7d036..24297b274cd4 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -517,18 +517,17 @@ static int x25_asy_close(struct net_device *dev) * and sent on to some IP layer for further processing. */ -static unsigned int x25_asy_receive_buf(struct tty_struct *tty, +static void x25_asy_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct x25_asy *sl = tty->disc_data; - int bytes = count; if (!sl || sl->magic != X25_ASY_MAGIC || !netif_running(sl->dev)) return; /* Read the characters out of the buffer */ - while (bytes--) { + while (count--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->dev->stats.rx_errors++; @@ -537,8 +536,6 @@ static unsigned int x25_asy_receive_buf(struct tty_struct *tty, } x25_asy_unesc(sl, *cp++); } - - return count; } /* diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index a4c42a75a3bf..09e8c7d53af3 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2128,8 +2128,8 @@ static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) gsm->tty = NULL; } -static unsigned int gsmld_receive_buf(struct tty_struct *tty, - const unsigned char *cp, char *fp, int count) +static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) { struct gsm_mux *gsm = tty->disc_data; const unsigned char *dp; @@ -2162,8 +2162,6 @@ static unsigned int gsmld_receive_buf(struct tty_struct *tty, } /* FASYNC if needed ? */ /* If clogged call tty_throttle(tty); */ - - return count; } /** diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index cac666314aef..cea56033b34c 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -188,8 +188,8 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait); static int n_hdlc_tty_open(struct tty_struct *tty); static void n_hdlc_tty_close(struct tty_struct *tty); -static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, - const __u8 *cp, char *fp, int count); +static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *cp, + char *fp, int count); static void n_hdlc_tty_wakeup(struct tty_struct *tty); #define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) @@ -509,8 +509,8 @@ static void n_hdlc_tty_wakeup(struct tty_struct *tty) * Called by tty low level driver when receive data is available. Data is * interpreted as one HDLC frame. */ -static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, - const __u8 *data, char *flags, int count) +static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, + char *flags, int count) { register struct n_hdlc *n_hdlc = tty2n_hdlc (tty); register struct n_hdlc_buf *buf; @@ -521,20 +521,20 @@ static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, /* This can happen if stuff comes in on the backup tty */ if (!n_hdlc || tty != n_hdlc->tty) - return -ENODEV; + return; /* verify line is using HDLC discipline */ if (n_hdlc->magic != HDLC_MAGIC) { printk("%s(%d) line not using HDLC discipline\n", __FILE__,__LINE__); - return -EINVAL; + return; } if ( count>maxframe ) { if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d) rx count>maxframesize, data discarded\n", __FILE__,__LINE__); - return -EINVAL; + return; } /* get a free HDLC buffer */ @@ -550,7 +550,7 @@ static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d) no more rx buffers, data discarded\n", __FILE__,__LINE__); - return -EINVAL; + return; } /* copy received data to HDLC buffer */ @@ -565,8 +565,6 @@ static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, if (n_hdlc->tty->fasync != NULL) kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN); - return count; - } /* end of n_hdlc_tty_receive() */ /** diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index a4bc39c21a43..5c6c31459a2f 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -139,8 +139,8 @@ static int r3964_ioctl(struct tty_struct *tty, struct file *file, static void r3964_set_termios(struct tty_struct *tty, struct ktermios *old); static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, struct poll_table_struct *wait); -static unsigned int r3964_receive_buf(struct tty_struct *tty, - const unsigned char *cp, char *fp, int count); +static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count); static struct tty_ldisc_ops tty_ldisc_N_R3964 = { .owner = THIS_MODULE, @@ -1239,8 +1239,8 @@ static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, return result; } -static unsigned int r3964_receive_buf(struct tty_struct *tty, - const unsigned char *cp, char *fp, int count) +static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) { struct r3964_info *pInfo = tty->disc_data; const unsigned char *p; @@ -1257,8 +1257,6 @@ static unsigned int r3964_receive_buf(struct tty_struct *tty, } } - - return count; } MODULE_LICENSE("GPL"); diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 95d0a9c2dd13..0ad32888091c 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -81,6 +81,38 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, return put_user(x, ptr); } +/** + * n_tty_set__room - receive space + * @tty: terminal + * + * Called by the driver to find out how much data it is + * permitted to feed to the line discipline without any being lost + * and thus to manage flow control. Not serialized. Answers for the + * "instant". + */ + +static void n_tty_set_room(struct tty_struct *tty) +{ + /* tty->read_cnt is not read locked ? */ + int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; + int old_left; + + /* + * If we are doing input canonicalization, and there are no + * pending newlines, let characters through without limit, so + * that erase characters will be handled. Other excess + * characters will be beeped. + */ + if (left <= 0) + left = tty->icanon && !tty->canon_data; + old_left = tty->receive_room; + tty->receive_room = left; + + /* Did this open up the receive buffer? We may need to flip */ + if (left && !old_left) + schedule_work(&tty->buf.work); +} + static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) { if (tty->read_cnt < N_TTY_BUF_SIZE) { @@ -152,6 +184,7 @@ static void reset_buffer_flags(struct tty_struct *tty) tty->canon_head = tty->canon_data = tty->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); + n_tty_set_room(tty); check_unthrottle(tty); } @@ -1327,19 +1360,17 @@ static void n_tty_write_wakeup(struct tty_struct *tty) * calls one at a time and in order (or using flush_to_ldisc) */ -static unsigned int n_tty_receive_buf(struct tty_struct *tty, - const unsigned char *cp, char *fp, int count) +static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) { const unsigned char *p; char *f, flags = TTY_NORMAL; int i; char buf[64]; unsigned long cpuflags; - int left; - int ret = 0; if (!tty->read_buf) - return 0; + return; if (tty->real_raw) { spin_lock_irqsave(&tty->read_lock, cpuflags); @@ -1349,7 +1380,6 @@ static unsigned int n_tty_receive_buf(struct tty_struct *tty, memcpy(tty->read_buf + tty->read_head, cp, i); tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); tty->read_cnt += i; - ret += i; cp += i; count -= i; @@ -1359,10 +1389,8 @@ static unsigned int n_tty_receive_buf(struct tty_struct *tty, memcpy(tty->read_buf + tty->read_head, cp, i); tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); tty->read_cnt += i; - ret += i; spin_unlock_irqrestore(&tty->read_lock, cpuflags); } else { - ret = count; for (i = count, p = cp, f = fp; i; i--, p++) { if (f) flags = *f++; @@ -1390,6 +1418,8 @@ static unsigned int n_tty_receive_buf(struct tty_struct *tty, tty->ops->flush_chars(tty); } + n_tty_set_room(tty); + if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); @@ -1402,12 +1432,8 @@ static unsigned int n_tty_receive_buf(struct tty_struct *tty, * mode. We don't want to throttle the driver if we're in * canonical mode and don't have a newline yet! */ - left = N_TTY_BUF_SIZE - tty->read_cnt - 1; - - if (left < TTY_THRESHOLD_THROTTLE) + if (tty->receive_room < TTY_THRESHOLD_THROTTLE) tty_throttle(tty); - - return ret; } int is_ignored(int sig) @@ -1451,6 +1477,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { tty->raw = 1; tty->real_raw = 1; + n_tty_set_room(tty); return; } if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || @@ -1503,6 +1530,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) else tty->real_raw = 0; } + n_tty_set_room(tty); /* The termios change make the tty ready for I/O */ wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->read_wait); @@ -1784,6 +1812,8 @@ do_it_again: retval = -ERESTARTSYS; break; } + /* FIXME: does n_tty_set_room need locking ? */ + n_tty_set_room(tty); timeout = schedule_timeout(timeout); continue; } @@ -1855,8 +1885,10 @@ do_it_again: * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, * we won't get any more characters. */ - if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) + if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { + n_tty_set_room(tty); check_unthrottle(tty); + } if (b - buf >= minimum) break; @@ -1878,6 +1910,7 @@ do_it_again: } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again; + n_tty_set_room(tty); return retval; } diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 46de2e075dac..f1a7918d71aa 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -416,7 +416,6 @@ static void flush_to_ldisc(struct work_struct *work) struct tty_buffer *head, *tail = tty->buf.tail; int seen_tail = 0; while ((head = tty->buf.head) != NULL) { - int copied; int count; char *char_buf; unsigned char *flag_buf; @@ -443,19 +442,17 @@ static void flush_to_ldisc(struct work_struct *work) line discipline as we want to empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) break; + if (!tty->receive_room || seen_tail) + break; + if (count > tty->receive_room) + count = tty->receive_room; char_buf = head->char_buf_ptr + head->read; flag_buf = head->flag_buf_ptr + head->read; + head->read += count; spin_unlock_irqrestore(&tty->buf.lock, flags); - copied = disc->ops->receive_buf(tty, char_buf, + disc->ops->receive_buf(tty, char_buf, flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); - - head->read += copied; - - if (copied == 0 || seen_tail) { - schedule_work(&tty->buf.work); - break; - } } clear_bit(TTY_FLUSHING, &tty->flags); } diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 67b1d0d7c8ac..fb864e7fcd13 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -332,7 +332,8 @@ int paste_selection(struct tty_struct *tty) continue; } count = sel_buffer_lth - pasted; - count = tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, + count = min(count, tty->receive_room); + tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, NULL, count); pasted += count; } diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 5b07792ccb46..ff7dc08696a8 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -76,7 +76,7 @@ * tty device. It is solely the responsibility of the line * discipline to handle poll requests. * - * unsigned int (*receive_buf)(struct tty_struct *, const unsigned char *cp, + * void (*receive_buf)(struct tty_struct *, const unsigned char *cp, * char *fp, int count); * * This function is called by the low-level tty driver to send @@ -84,8 +84,7 @@ * processing. is a pointer to the buffer of input * character received by the device. is a pointer to a * pointer of flag bytes which indicate whether a character was - * received with a parity error, etc. Returns the amount of bytes - * received. + * received with a parity error, etc. * * void (*write_wakeup)(struct tty_struct *); * @@ -141,8 +140,8 @@ struct tty_ldisc_ops { /* * The following routines are called from below. */ - unsigned int (*receive_buf)(struct tty_struct *, - const unsigned char *cp, char *fp, int count); + void (*receive_buf)(struct tty_struct *, const unsigned char *cp, + char *fp, int count); void (*write_wakeup)(struct tty_struct *); void (*dcd_change)(struct tty_struct *, unsigned int, struct pps_event_time *); -- cgit v1.2.3 From 9e1f1de02c2275d7172e18dc4e7c2065777611bf Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 3 Jun 2011 18:24:58 -0400 Subject: more conservative S_NOSEC handling Caching "we have already removed suid/caps" was overenthusiastic as merged. On network filesystems we might have had suid/caps set on another client, silently picked by this client on revalidate, all of that *without* clearing the S_NOSEC flag. AFAICS, the only reasonably sane way to deal with that is * new superblock flag; unless set, S_NOSEC is not going to be set. * local block filesystems set it in their ->mount() (more accurately, mount_bdev() does, so does btrfs ->mount(), users of mount_bdev() other than local block ones clear it) * if any network filesystem (or a cluster one) wants to use S_NOSEC, it'll need to set MS_NOSEC in sb->s_flags *AND* take care to clear S_NOSEC when inode attribute changes are picked from other clients. It's not an earth-shattering hole (anybody that can set suid on another client will almost certainly be able to write to the file before doing that anyway), but it's a bug that needs fixing. Signed-off-by: Al Viro --- fs/btrfs/super.c | 2 +- fs/fuse/inode.c | 2 ++ fs/ocfs2/super.c | 2 +- fs/super.c | 2 +- include/linux/fs.h | 3 ++- mm/filemap.c | 2 +- 6 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 9b2e7e5bc3ef..d158b672a2d2 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -819,7 +819,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, } else { char b[BDEVNAME_SIZE]; - s->s_flags = flags; + s->s_flags = flags | MS_NOSEC; strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id)); error = btrfs_fill_super(s, fs_devices, data, flags & MS_SILENT ? 1 : 0); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index cc6ec4b2f0ff..38f84cd48b67 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -921,6 +921,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) if (sb->s_flags & MS_MANDLOCK) goto err; + sb->s_flags &= ~MS_NOSEC; + if (!parse_fuse_opt((char *) data, &d, is_bdev)) goto err; diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index cdbaf5e97308..56f61027236b 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -1072,7 +1072,7 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) sb->s_magic = OCFS2_SUPER_MAGIC; - sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | + sb->s_flags = (sb->s_flags & ~(MS_POSIXACL | MS_NOSEC)) | ((osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); /* Hard readonly mode only if: bdev_read_only, MS_RDONLY, diff --git a/fs/super.c b/fs/super.c index c75593953c52..ab3d672db0de 100644 --- a/fs/super.c +++ b/fs/super.c @@ -822,7 +822,7 @@ struct dentry *mount_bdev(struct file_system_type *fs_type, } else { char b[BDEVNAME_SIZE]; - s->s_flags = flags; + s->s_flags = flags | MS_NOSEC; s->s_mode = mode; strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id)); sb_set_blocksize(s, block_size(bdev)); diff --git a/include/linux/fs.h b/include/linux/fs.h index c55d6b7cd5d6..646a1836152a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -208,6 +208,7 @@ struct inodes_stat_t { #define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */ #define MS_I_VERSION (1<<23) /* Update inode I_version field */ #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ +#define MS_NOSEC (1<<28) #define MS_BORN (1<<29) #define MS_ACTIVE (1<<30) #define MS_NOUSER (1<<31) @@ -2591,7 +2592,7 @@ static inline int is_sxid(mode_t mode) static inline void inode_has_no_xattr(struct inode *inode) { - if (!is_sxid(inode->i_mode)) + if (!is_sxid(inode->i_mode) && (inode->i_sb->s_flags & MS_NOSEC)) inode->i_flags |= S_NOSEC; } diff --git a/mm/filemap.c b/mm/filemap.c index d7b10578a64b..a8251a8d3457 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2000,7 +2000,7 @@ int file_remove_suid(struct file *file) error = security_inode_killpriv(dentry); if (!error && killsuid) error = __remove_suid(dentry, killsuid); - if (!error) + if (!error && (inode->i_sb->s_flags & MS_NOSEC)) inode->i_flags |= S_NOSEC; return error; -- cgit v1.2.3 From d7ebe75b065a7c2d58ffc12f9d2e00d5ea4e71eb Mon Sep 17 00:00:00 2001 From: Vince Weaver Date: Fri, 3 Jun 2011 17:59:51 -0400 Subject: perf: Fix comments in include/linux/perf_event.h Fix include/linux/perf_event.h comments to be consistent with the actual #define names. This is trivial, but it can be a bit confusing when first reading through the file. Signed-off-by: Vince Weaver Cc: Peter Zijlstra Cc: paulus@samba.org Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Steven Rostedt Link: http://lkml.kernel.org/r/alpine.DEB.2.00.1106031757090.29381@cl320.eecs.utk.edu Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 3412684ce5d5..e0786e35f247 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -137,14 +137,14 @@ enum perf_event_sample_format { * * struct read_format { * { u64 value; - * { u64 time_enabled; } && PERF_FORMAT_ENABLED - * { u64 time_running; } && PERF_FORMAT_RUNNING + * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED + * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING * { u64 id; } && PERF_FORMAT_ID * } && !PERF_FORMAT_GROUP * * { u64 nr; - * { u64 time_enabled; } && PERF_FORMAT_ENABLED - * { u64 time_running; } && PERF_FORMAT_RUNNING + * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED + * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING * { u64 value; * { u64 id; } && PERF_FORMAT_ID * } cntr[nr]; -- cgit v1.2.3 From a8f072c1d624a627b67f2ace2f0c25d856ef4e54 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 2 Jun 2011 11:13:59 +0200 Subject: job control: rename signal->group_stop and flags to jobctl and update them signal->group_stop currently hosts mostly group stop related flags; however, it's gonna be used for wider purposes and the GROUP_STOP_ flag prefix becomes confusing. Rename signal->group_stop to signal->jobctl and rename all GROUP_STOP_* flags to JOBCTL_*. Bit position macros JOBCTL_*_BIT are defined and JOBCTL_* flags are defined in terms of them to allow using bitops later. While at it, reassign JOBCTL_TRAPPING to bit 22 to better accomodate future additions. This doesn't cause any functional change. -v2: JOBCTL_*_BIT macros added as suggested by Linus. Signed-off-by: Tejun Heo Cc: Linus Torvalds Signed-off-by: Oleg Nesterov --- fs/exec.c | 2 +- include/linux/sched.h | 22 ++++++++----- kernel/ptrace.c | 12 +++---- kernel/signal.c | 91 ++++++++++++++++++++++++++------------------------- 4 files changed, 67 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index ea5f748906a8..8986bb0f9dc2 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1772,7 +1772,7 @@ static int zap_process(struct task_struct *start, int exit_code) t = start; do { - task_clear_group_stop_pending(t); + task_clear_jobctl_stop_pending(t); if (t != current && t->mm) { sigaddset(&t->pending.signal, SIGKILL); signal_wake_up(t, 1); diff --git a/include/linux/sched.h b/include/linux/sched.h index 2a8621c4be1e..b0dd064eb4fc 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1282,7 +1282,7 @@ struct task_struct { int exit_state; int exit_code, exit_signal; int pdeath_signal; /* The signal sent when the parent dies */ - unsigned int group_stop; /* GROUP_STOP_*, siglock protected */ + unsigned int jobctl; /* JOBCTL_*, siglock protected */ /* ??? */ unsigned int personality; unsigned did_exec:1; @@ -1803,15 +1803,21 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t * #define used_math() tsk_used_math(current) /* - * task->group_stop flags + * task->jobctl flags */ -#define GROUP_STOP_SIGMASK 0xffff /* signr of the last group stop */ -#define GROUP_STOP_PENDING (1 << 16) /* task should stop for group stop */ -#define GROUP_STOP_CONSUME (1 << 17) /* consume group stop count */ -#define GROUP_STOP_TRAPPING (1 << 18) /* switching from STOPPED to TRACED */ -#define GROUP_STOP_DEQUEUED (1 << 19) /* stop signal dequeued */ +#define JOBCTL_STOP_SIGMASK 0xffff /* signr of the last group stop */ -extern void task_clear_group_stop_pending(struct task_struct *task); +#define JOBCTL_STOP_DEQUEUED_BIT 16 /* stop signal dequeued */ +#define JOBCTL_STOP_PENDING_BIT 17 /* task should stop for group stop */ +#define JOBCTL_STOP_CONSUME_BIT 18 /* consume group stop count */ +#define JOBCTL_TRAPPING_BIT 21 /* switching to TRACED */ + +#define JOBCTL_STOP_DEQUEUED (1 << JOBCTL_STOP_DEQUEUED_BIT) +#define JOBCTL_STOP_PENDING (1 << JOBCTL_STOP_PENDING_BIT) +#define JOBCTL_STOP_CONSUME (1 << JOBCTL_STOP_CONSUME_BIT) +#define JOBCTL_TRAPPING (1 << JOBCTL_TRAPPING_BIT) + +extern void task_clear_jobctl_stop_pending(struct task_struct *task); #ifdef CONFIG_PREEMPT_RCU diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 4f689cb739a3..134f34cb142b 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -77,13 +77,13 @@ void __ptrace_unlink(struct task_struct *child) spin_lock(&child->sighand->siglock); /* - * Reinstate GROUP_STOP_PENDING if group stop is in effect and + * Reinstate JOBCTL_STOP_PENDING if group stop is in effect and * @child isn't dead. */ if (!(child->flags & PF_EXITING) && (child->signal->flags & SIGNAL_STOP_STOPPED || child->signal->group_stop_count)) - child->group_stop |= GROUP_STOP_PENDING; + child->jobctl |= JOBCTL_STOP_PENDING; /* * If transition to TASK_STOPPED is pending or in TASK_TRACED, kick @@ -91,7 +91,7 @@ void __ptrace_unlink(struct task_struct *child) * is in TASK_TRACED; otherwise, we might unduly disrupt * TASK_KILLABLE sleeps. */ - if (child->group_stop & GROUP_STOP_PENDING || task_is_traced(child)) + if (child->jobctl & JOBCTL_STOP_PENDING || task_is_traced(child)) signal_wake_up(child, task_is_traced(child)); spin_unlock(&child->sighand->siglock); @@ -226,7 +226,7 @@ static int ptrace_attach(struct task_struct *task) spin_lock(&task->sighand->siglock); /* - * If the task is already STOPPED, set GROUP_STOP_PENDING and + * If the task is already STOPPED, set JOBCTL_STOP_PENDING and * TRAPPING, and kick it so that it transits to TRACED. TRAPPING * will be cleared if the child completes the transition or any * event which clears the group stop states happens. We'll wait @@ -243,7 +243,7 @@ static int ptrace_attach(struct task_struct *task) * in and out of STOPPED are protected by siglock. */ if (task_is_stopped(task)) { - task->group_stop |= GROUP_STOP_PENDING | GROUP_STOP_TRAPPING; + task->jobctl |= JOBCTL_STOP_PENDING | JOBCTL_TRAPPING; signal_wake_up(task, 1); } @@ -257,7 +257,7 @@ unlock_creds: out: if (!retval) wait_event(current->signal->wait_chldexit, - !(task->group_stop & GROUP_STOP_TRAPPING)); + !(task->jobctl & JOBCTL_TRAPPING)); return retval; } diff --git a/kernel/signal.c b/kernel/signal.c index 86c32b884f8e..ab6851c06461 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -124,7 +124,7 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) static int recalc_sigpending_tsk(struct task_struct *t) { - if ((t->group_stop & GROUP_STOP_PENDING) || + if ((t->jobctl & JOBCTL_STOP_PENDING) || PENDING(&t->pending, &t->blocked) || PENDING(&t->signal->shared_pending, &t->blocked)) { set_tsk_thread_flag(t, TIF_SIGPENDING); @@ -224,27 +224,28 @@ static inline void print_dropped_signal(int sig) } /** - * task_clear_group_stop_trapping - clear group stop trapping bit + * task_clear_jobctl_trapping - clear jobctl trapping bit * @task: target task * - * If GROUP_STOP_TRAPPING is set, a ptracer is waiting for us. Clear it - * and wake up the ptracer. Note that we don't need any further locking. - * @task->siglock guarantees that @task->parent points to the ptracer. + * If JOBCTL_TRAPPING is set, a ptracer is waiting for us to enter TRACED. + * Clear it and wake up the ptracer. Note that we don't need any further + * locking. @task->siglock guarantees that @task->parent points to the + * ptracer. * * CONTEXT: * Must be called with @task->sighand->siglock held. */ -static void task_clear_group_stop_trapping(struct task_struct *task) +static void task_clear_jobctl_trapping(struct task_struct *task) { - if (unlikely(task->group_stop & GROUP_STOP_TRAPPING)) { - task->group_stop &= ~GROUP_STOP_TRAPPING; + if (unlikely(task->jobctl & JOBCTL_TRAPPING)) { + task->jobctl &= ~JOBCTL_TRAPPING; __wake_up_sync_key(&task->parent->signal->wait_chldexit, TASK_UNINTERRUPTIBLE, 1, task); } } /** - * task_clear_group_stop_pending - clear pending group stop + * task_clear_jobctl_stop_pending - clear pending group stop * @task: target task * * Clear group stop states for @task. @@ -252,19 +253,19 @@ static void task_clear_group_stop_trapping(struct task_struct *task) * CONTEXT: * Must be called with @task->sighand->siglock held. */ -void task_clear_group_stop_pending(struct task_struct *task) +void task_clear_jobctl_stop_pending(struct task_struct *task) { - task->group_stop &= ~(GROUP_STOP_PENDING | GROUP_STOP_CONSUME | - GROUP_STOP_DEQUEUED); + task->jobctl &= ~(JOBCTL_STOP_PENDING | JOBCTL_STOP_CONSUME | + JOBCTL_STOP_DEQUEUED); } /** * task_participate_group_stop - participate in a group stop * @task: task participating in a group stop * - * @task has GROUP_STOP_PENDING set and is participating in a group stop. + * @task has %JOBCTL_STOP_PENDING set and is participating in a group stop. * Group stop states are cleared and the group stop count is consumed if - * %GROUP_STOP_CONSUME was set. If the consumption completes the group + * %JOBCTL_STOP_CONSUME was set. If the consumption completes the group * stop, the appropriate %SIGNAL_* flags are set. * * CONTEXT: @@ -277,11 +278,11 @@ void task_clear_group_stop_pending(struct task_struct *task) static bool task_participate_group_stop(struct task_struct *task) { struct signal_struct *sig = task->signal; - bool consume = task->group_stop & GROUP_STOP_CONSUME; + bool consume = task->jobctl & JOBCTL_STOP_CONSUME; - WARN_ON_ONCE(!(task->group_stop & GROUP_STOP_PENDING)); + WARN_ON_ONCE(!(task->jobctl & JOBCTL_STOP_PENDING)); - task_clear_group_stop_pending(task); + task_clear_jobctl_stop_pending(task); if (!consume) return false; @@ -604,7 +605,7 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) * is to alert stop-signal processing code when another * processor has come along and cleared the flag. */ - current->group_stop |= GROUP_STOP_DEQUEUED; + current->jobctl |= JOBCTL_STOP_DEQUEUED; } if ((info->si_code & __SI_MASK) == __SI_TIMER && info->si_sys_private) { /* @@ -809,7 +810,7 @@ static int prepare_signal(int sig, struct task_struct *p, int from_ancestor_ns) rm_from_queue(SIG_KERNEL_STOP_MASK, &signal->shared_pending); t = p; do { - task_clear_group_stop_pending(t); + task_clear_jobctl_stop_pending(t); rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending); wake_up_state(t, __TASK_STOPPED); } while_each_thread(p, t); @@ -925,7 +926,7 @@ static void complete_signal(int sig, struct task_struct *p, int group) signal->group_stop_count = 0; t = p; do { - task_clear_group_stop_pending(t); + task_clear_jobctl_stop_pending(t); sigaddset(&t->pending.signal, SIGKILL); signal_wake_up(t, 1); } while_each_thread(p, t); @@ -1160,7 +1161,7 @@ int zap_other_threads(struct task_struct *p) p->signal->group_stop_count = 0; while_each_thread(p, t) { - task_clear_group_stop_pending(t); + task_clear_jobctl_stop_pending(t); count++; /* Don't bother with already dead threads */ @@ -1738,7 +1739,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info) * clear now. We act as if SIGCONT is received after TASK_TRACED * is entered - ignore it. */ - if (why == CLD_STOPPED && (current->group_stop & GROUP_STOP_PENDING)) + if (why == CLD_STOPPED && (current->jobctl & JOBCTL_STOP_PENDING)) gstop_done = task_participate_group_stop(current); current->last_siginfo = info; @@ -1751,12 +1752,12 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info) set_current_state(TASK_TRACED); /* - * We're committing to trapping. Clearing GROUP_STOP_TRAPPING and + * We're committing to trapping. Clearing JOBCTL_TRAPPING and * transition to TASK_TRACED should be atomic with respect to - * siglock. This hsould be done after the arch hook as siglock is + * siglock. This should be done after the arch hook as siglock is * released and regrabbed across it. */ - task_clear_group_stop_trapping(current); + task_clear_jobctl_trapping(current); spin_unlock_irq(¤t->sighand->siglock); read_lock(&tasklist_lock); @@ -1792,9 +1793,9 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info) * * If @gstop_done, the ptracer went away between group stop * completion and here. During detach, it would have set - * GROUP_STOP_PENDING on us and we'll re-enter TASK_STOPPED - * in do_signal_stop() on return, so notifying the real - * parent of the group stop completion is enough. + * JOBCTL_STOP_PENDING on us and we'll re-enter + * TASK_STOPPED in do_signal_stop() on return, so notifying + * the real parent of the group stop completion is enough. */ if (gstop_done) do_notify_parent_cldstop(current, false, why); @@ -1856,14 +1857,14 @@ static int do_signal_stop(int signr) { struct signal_struct *sig = current->signal; - if (!(current->group_stop & GROUP_STOP_PENDING)) { - unsigned int gstop = GROUP_STOP_PENDING | GROUP_STOP_CONSUME; + if (!(current->jobctl & JOBCTL_STOP_PENDING)) { + unsigned int gstop = JOBCTL_STOP_PENDING | JOBCTL_STOP_CONSUME; struct task_struct *t; - /* signr will be recorded in task->group_stop for retries */ - WARN_ON_ONCE(signr & ~GROUP_STOP_SIGMASK); + /* signr will be recorded in task->jobctl for retries */ + WARN_ON_ONCE(signr & ~JOBCTL_STOP_SIGMASK); - if (!likely(current->group_stop & GROUP_STOP_DEQUEUED) || + if (!likely(current->jobctl & JOBCTL_STOP_DEQUEUED) || unlikely(signal_group_exit(sig))) return 0; /* @@ -1890,19 +1891,19 @@ static int do_signal_stop(int signr) else WARN_ON_ONCE(!task_ptrace(current)); - current->group_stop &= ~GROUP_STOP_SIGMASK; - current->group_stop |= signr | gstop; + current->jobctl &= ~JOBCTL_STOP_SIGMASK; + current->jobctl |= signr | gstop; sig->group_stop_count = 1; for (t = next_thread(current); t != current; t = next_thread(t)) { - t->group_stop &= ~GROUP_STOP_SIGMASK; + t->jobctl &= ~JOBCTL_STOP_SIGMASK; /* * Setting state to TASK_STOPPED for a group * stop is always done with the siglock held, * so this check has no races. */ if (!(t->flags & PF_EXITING) && !task_is_stopped(t)) { - t->group_stop |= signr | gstop; + t->jobctl |= signr | gstop; sig->group_stop_count++; signal_wake_up(t, 0); } @@ -1943,23 +1944,23 @@ retry: spin_lock_irq(¤t->sighand->siglock); } else { - ptrace_stop(current->group_stop & GROUP_STOP_SIGMASK, + ptrace_stop(current->jobctl & JOBCTL_STOP_SIGMASK, CLD_STOPPED, 0, NULL); current->exit_code = 0; } /* - * GROUP_STOP_PENDING could be set if another group stop has + * JOBCTL_STOP_PENDING could be set if another group stop has * started since being woken up or ptrace wants us to transit * between TASK_STOPPED and TRACED. Retry group stop. */ - if (current->group_stop & GROUP_STOP_PENDING) { - WARN_ON_ONCE(!(current->group_stop & GROUP_STOP_SIGMASK)); + if (current->jobctl & JOBCTL_STOP_PENDING) { + WARN_ON_ONCE(!(current->jobctl & JOBCTL_STOP_SIGMASK)); goto retry; } /* PTRACE_ATTACH might have raced with task killing, clear trapping */ - task_clear_group_stop_trapping(current); + task_clear_jobctl_trapping(current); spin_unlock_irq(¤t->sighand->siglock); @@ -2078,8 +2079,8 @@ relock: if (unlikely(signr != 0)) ka = return_ka; else { - if (unlikely(current->group_stop & - GROUP_STOP_PENDING) && do_signal_stop(0)) + if (unlikely(current->jobctl & JOBCTL_STOP_PENDING) && + do_signal_stop(0)) goto relock; signr = dequeue_signal(current, ¤t->blocked, @@ -2253,7 +2254,7 @@ void exit_signals(struct task_struct *tsk) signotset(&unblocked); retarget_shared_pending(tsk, &unblocked); - if (unlikely(tsk->group_stop & GROUP_STOP_PENDING) && + if (unlikely(tsk->jobctl & JOBCTL_STOP_PENDING) && task_participate_group_stop(tsk)) group_stop = CLD_STOPPED; out: -- cgit v1.2.3 From 755e276b3326f300585435d2f3876e66e248c476 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 2 Jun 2011 11:13:59 +0200 Subject: ptrace: ptrace_check_attach(): rename @kill to @ignore_state and add comments PTRACE_INTERRUPT is going to be added which should also skip task_is_traced() check in ptrace_check_attach(). Rename @kill to @ignore_state and make it bool. Add function comment while at it. This patch doesn't introduce any behavior difference. Signed-off-by: Tejun Heo Signed-off-by: Oleg Nesterov --- include/linux/ptrace.h | 2 +- kernel/ptrace.c | 24 +++++++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 9178d5cc0b01..e93ef1a54fc7 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -105,7 +105,7 @@ extern long arch_ptrace(struct task_struct *child, long request, extern int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len); extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len); extern void ptrace_disable(struct task_struct *); -extern int ptrace_check_attach(struct task_struct *task, int kill); +extern int ptrace_check_attach(struct task_struct *task, bool ignore_state); extern int ptrace_request(struct task_struct *child, long request, unsigned long addr, unsigned long data); extern void ptrace_notify(int exit_code); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 134f34cb142b..eb191116edf7 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -97,10 +97,24 @@ void __ptrace_unlink(struct task_struct *child) spin_unlock(&child->sighand->siglock); } -/* - * Check that we have indeed attached to the thing.. +/** + * ptrace_check_attach - check whether ptracee is ready for ptrace operation + * @child: ptracee to check for + * @ignore_state: don't check whether @child is currently %TASK_TRACED + * + * Check whether @child is being ptraced by %current and ready for further + * ptrace operations. If @ignore_state is %false, @child also should be in + * %TASK_TRACED state and on return the child is guaranteed to be traced + * and not executing. If @ignore_state is %true, @child can be in any + * state. + * + * CONTEXT: + * Grabs and releases tasklist_lock and @child->sighand->siglock. + * + * RETURNS: + * 0 on success, -ESRCH if %child is not ready. */ -int ptrace_check_attach(struct task_struct *child, int kill) +int ptrace_check_attach(struct task_struct *child, bool ignore_state) { int ret = -ESRCH; @@ -119,13 +133,13 @@ int ptrace_check_attach(struct task_struct *child, int kill) */ spin_lock_irq(&child->sighand->siglock); WARN_ON_ONCE(task_is_stopped(child)); - if (task_is_traced(child) || kill) + if (task_is_traced(child) || ignore_state) ret = 0; spin_unlock_irq(&child->sighand->siglock); } read_unlock(&tasklist_lock); - if (!ret && !kill) + if (!ret && !ignore_state) ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH; /* All systems go.. */ -- cgit v1.2.3 From 3759a0d94c18764247b66511d1038f2b93aa95de Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 2 Jun 2011 11:14:00 +0200 Subject: job control: introduce JOBCTL_PENDING_MASK and task_clear_jobctl_pending() This patch introduces JOBCTL_PENDING_MASK and replaces task_clear_jobctl_stop_pending() with task_clear_jobctl_pending() which takes an extra @mask argument. JOBCTL_PENDING_MASK is currently equal to JOBCTL_STOP_PENDING but future patches will add more bits. recalc_sigpending_tsk() is updated to use JOBCTL_PENDING_MASK instead. task_clear_jobctl_pending() takes @mask which in subset of JOBCTL_PENDING_MASK and clears the relevant jobctl bits. If JOBCTL_STOP_PENDING is set, other STOP bits are cleared together. All task_clear_jobctl_stop_pending() users are updated to call task_clear_jobctl_pending() with JOBCTL_STOP_PENDING which is functionally identical to task_clear_jobctl_stop_pending(). This patch doesn't cause any functional change. Signed-off-by: Tejun Heo Signed-off-by: Oleg Nesterov --- fs/exec.c | 2 +- include/linux/sched.h | 5 ++++- kernel/signal.c | 27 +++++++++++++++++---------- 3 files changed, 22 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index 8986bb0f9dc2..4402105287cb 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1772,7 +1772,7 @@ static int zap_process(struct task_struct *start, int exit_code) t = start; do { - task_clear_jobctl_stop_pending(t); + task_clear_jobctl_pending(t, JOBCTL_STOP_PENDING); if (t != current && t->mm) { sigaddset(&t->pending.signal, SIGKILL); signal_wake_up(t, 1); diff --git a/include/linux/sched.h b/include/linux/sched.h index b0dd064eb4fc..5a958b17f9fe 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1817,7 +1817,10 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t * #define JOBCTL_STOP_CONSUME (1 << JOBCTL_STOP_CONSUME_BIT) #define JOBCTL_TRAPPING (1 << JOBCTL_TRAPPING_BIT) -extern void task_clear_jobctl_stop_pending(struct task_struct *task); +#define JOBCTL_PENDING_MASK JOBCTL_STOP_PENDING + +extern void task_clear_jobctl_pending(struct task_struct *task, + unsigned int mask); #ifdef CONFIG_PREEMPT_RCU diff --git a/kernel/signal.c b/kernel/signal.c index 62a6c3bb9f0d..288d952fa3b8 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -124,7 +124,7 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) static int recalc_sigpending_tsk(struct task_struct *t) { - if ((t->jobctl & JOBCTL_STOP_PENDING) || + if ((t->jobctl & JOBCTL_PENDING_MASK) || PENDING(&t->pending, &t->blocked) || PENDING(&t->signal->shared_pending, &t->blocked)) { set_tsk_thread_flag(t, TIF_SIGPENDING); @@ -245,18 +245,25 @@ static void task_clear_jobctl_trapping(struct task_struct *task) } /** - * task_clear_jobctl_stop_pending - clear pending group stop + * task_clear_jobctl_pending - clear jobctl pending bits * @task: target task + * @mask: pending bits to clear * - * Clear group stop states for @task. + * Clear @mask from @task->jobctl. @mask must be subset of + * %JOBCTL_PENDING_MASK. If %JOBCTL_STOP_PENDING is being cleared, other + * STOP bits are cleared together. * * CONTEXT: * Must be called with @task->sighand->siglock held. */ -void task_clear_jobctl_stop_pending(struct task_struct *task) +void task_clear_jobctl_pending(struct task_struct *task, unsigned int mask) { - task->jobctl &= ~(JOBCTL_STOP_PENDING | JOBCTL_STOP_CONSUME | - JOBCTL_STOP_DEQUEUED); + BUG_ON(mask & ~JOBCTL_PENDING_MASK); + + if (mask & JOBCTL_STOP_PENDING) + mask |= JOBCTL_STOP_CONSUME | JOBCTL_STOP_DEQUEUED; + + task->jobctl &= ~mask; } /** @@ -282,7 +289,7 @@ static bool task_participate_group_stop(struct task_struct *task) WARN_ON_ONCE(!(task->jobctl & JOBCTL_STOP_PENDING)); - task_clear_jobctl_stop_pending(task); + task_clear_jobctl_pending(task, JOBCTL_STOP_PENDING); if (!consume) return false; @@ -810,7 +817,7 @@ static int prepare_signal(int sig, struct task_struct *p, int from_ancestor_ns) rm_from_queue(SIG_KERNEL_STOP_MASK, &signal->shared_pending); t = p; do { - task_clear_jobctl_stop_pending(t); + task_clear_jobctl_pending(t, JOBCTL_STOP_PENDING); rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending); wake_up_state(t, __TASK_STOPPED); } while_each_thread(p, t); @@ -926,7 +933,7 @@ static void complete_signal(int sig, struct task_struct *p, int group) signal->group_stop_count = 0; t = p; do { - task_clear_jobctl_stop_pending(t); + task_clear_jobctl_pending(t, JOBCTL_STOP_PENDING); sigaddset(&t->pending.signal, SIGKILL); signal_wake_up(t, 1); } while_each_thread(p, t); @@ -1161,7 +1168,7 @@ int zap_other_threads(struct task_struct *p) p->signal->group_stop_count = 0; while_each_thread(p, t) { - task_clear_jobctl_stop_pending(t); + task_clear_jobctl_pending(t, JOBCTL_STOP_PENDING); count++; /* Don't bother with already dead threads */ -- cgit v1.2.3 From 7dd3db54e77d21eb95e145f19ba53f68250d0e73 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 2 Jun 2011 11:14:00 +0200 Subject: job control: introduce task_set_jobctl_pending() task->jobctl currently hosts JOBCTL_STOP_PENDING and will host TRAP pending bits too. Setting pending conditions on a dying task may make the task unkillable. Currently, each setting site is responsible for checking for the condition but with to-be-added job control traps this becomes too fragile. This patch adds task_set_jobctl_pending() which should be used when setting task->jobctl bits to schedule a stop or trap. The function performs the followings to ease setting pending bits. * Sanity checks. * If fatal signal is pending or PF_EXITING is set, no bit is set. * STOP_SIGMASK is automatically cleared if new value is being set. do_signal_stop() and ptrace_attach() are updated to use task_set_jobctl_pending() instead of setting STOP_PENDING explicitly. The surrounding structures around setting are changed to fit task_set_jobctl_pending() better but there should be no userland visible behavior difference. Signed-off-by: Tejun Heo Cc: Oleg Nesterov Signed-off-by: Oleg Nesterov --- include/linux/sched.h | 2 ++ kernel/ptrace.c | 6 +++--- kernel/signal.c | 46 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 45 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 5a958b17f9fe..5157bd9eee37 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1819,6 +1819,8 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t * #define JOBCTL_PENDING_MASK JOBCTL_STOP_PENDING +extern bool task_set_jobctl_pending(struct task_struct *task, + unsigned int mask); extern void task_clear_jobctl_pending(struct task_struct *task, unsigned int mask); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index eb191116edf7..0c37d999c8b8 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -256,10 +256,10 @@ static int ptrace_attach(struct task_struct *task) * The following task_is_stopped() test is safe as both transitions * in and out of STOPPED are protected by siglock. */ - if (task_is_stopped(task)) { - task->jobctl |= JOBCTL_STOP_PENDING | JOBCTL_TRAPPING; + if (task_is_stopped(task) && + task_set_jobctl_pending(task, + JOBCTL_STOP_PENDING | JOBCTL_TRAPPING)) signal_wake_up(task, 1); - } spin_unlock(&task->sighand->siglock); diff --git a/kernel/signal.c b/kernel/signal.c index 637a171b65b6..9ab91c516c3f 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -223,6 +223,39 @@ static inline void print_dropped_signal(int sig) current->comm, current->pid, sig); } +/** + * task_set_jobctl_pending - set jobctl pending bits + * @task: target task + * @mask: pending bits to set + * + * Clear @mask from @task->jobctl. @mask must be subset of + * %JOBCTL_PENDING_MASK | %JOBCTL_STOP_CONSUME | %JOBCTL_STOP_SIGMASK | + * %JOBCTL_TRAPPING. If stop signo is being set, the existing signo is + * cleared. If @task is already being killed or exiting, this function + * becomes noop. + * + * CONTEXT: + * Must be called with @task->sighand->siglock held. + * + * RETURNS: + * %true if @mask is set, %false if made noop because @task was dying. + */ +bool task_set_jobctl_pending(struct task_struct *task, unsigned int mask) +{ + BUG_ON(mask & ~(JOBCTL_PENDING_MASK | JOBCTL_STOP_CONSUME | + JOBCTL_STOP_SIGMASK | JOBCTL_TRAPPING)); + BUG_ON((mask & JOBCTL_TRAPPING) && !(mask & JOBCTL_PENDING_MASK)); + + if (unlikely(fatal_signal_pending(task) || (task->flags & PF_EXITING))) + return false; + + if (mask & JOBCTL_STOP_SIGMASK) + task->jobctl &= ~JOBCTL_STOP_SIGMASK; + + task->jobctl |= mask; + return true; +} + /** * task_clear_jobctl_trapping - clear jobctl trapping bit * @task: target task @@ -1902,19 +1935,20 @@ static int do_signal_stop(int signr) else WARN_ON_ONCE(!task_ptrace(current)); - current->jobctl &= ~JOBCTL_STOP_SIGMASK; - current->jobctl |= signr | gstop; - sig->group_stop_count = 1; + sig->group_stop_count = 0; + + if (task_set_jobctl_pending(current, signr | gstop)) + sig->group_stop_count++; + for (t = next_thread(current); t != current; t = next_thread(t)) { - t->jobctl &= ~JOBCTL_STOP_SIGMASK; /* * Setting state to TASK_STOPPED for a group * stop is always done with the siglock held, * so this check has no races. */ - if (!(t->flags & PF_EXITING) && !task_is_stopped(t)) { - t->jobctl |= signr | gstop; + if (!task_is_stopped(t) && + task_set_jobctl_pending(t, signr | gstop)) { sig->group_stop_count++; signal_wake_up(t, 0); } -- cgit v1.2.3 From dd1d6772692316fe35094085c5e4d9a370ad3462 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 2 Jun 2011 11:14:00 +0200 Subject: signal: remove three noop tracehooks Remove the following three noop tracehooks in signals.c. * tracehook_force_sigpending() * tracehook_get_signal() * tracehook_finish_jctl() The code area is about to be updated and these hooks don't do anything other than obfuscating the logic. Signed-off-by: Tejun Heo Signed-off-by: Oleg Nesterov --- include/linux/tracehook.h | 52 ----------------------------------------------- kernel/signal.c | 44 +++++++++++++-------------------------- 2 files changed, 14 insertions(+), 82 deletions(-) (limited to 'include') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index e95f5236611f..15745cdd32ce 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -425,58 +425,6 @@ static inline int tracehook_consider_fatal_signal(struct task_struct *task, return (task_ptrace(task) & PT_PTRACED) != 0; } -/** - * tracehook_force_sigpending - let tracing force signal_pending(current) on - * - * Called when recomputing our signal_pending() flag. Return nonzero - * to force the signal_pending() flag on, so that tracehook_get_signal() - * will be called before the next return to user mode. - * - * Called with @current->sighand->siglock held. - */ -static inline int tracehook_force_sigpending(void) -{ - return 0; -} - -/** - * tracehook_get_signal - deliver synthetic signal to traced task - * @task: @current - * @regs: task_pt_regs(@current) - * @info: details of synthetic signal - * @return_ka: sigaction for synthetic signal - * - * Return zero to check for a real pending signal normally. - * Return -1 after releasing the siglock to repeat the check. - * Return a signal number to induce an artificial signal delivery, - * setting *@info and *@return_ka to specify its details and behavior. - * - * The @return_ka->sa_handler value controls the disposition of the - * signal, no matter the signal number. For %SIG_DFL, the return value - * is a representative signal to indicate the behavior (e.g. %SIGTERM - * for death, %SIGQUIT for core dump, %SIGSTOP for job control stop, - * %SIGTSTP for stop unless in an orphaned pgrp), but the signal number - * reported will be @info->si_signo instead. - * - * Called with @task->sighand->siglock held, before dequeuing pending signals. - */ -static inline int tracehook_get_signal(struct task_struct *task, - struct pt_regs *regs, - siginfo_t *info, - struct k_sigaction *return_ka) -{ - return 0; -} - -/** - * tracehook_finish_jctl - report about return from job control stop - * - * This is called by do_signal_stop() after wakeup. - */ -static inline void tracehook_finish_jctl(void) -{ -} - #define DEATH_REAP -1 #define DEATH_DELAYED_GROUP_LEADER -2 diff --git a/kernel/signal.c b/kernel/signal.c index 172a4c79f12c..c99b8b5c0be7 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -150,9 +150,7 @@ void recalc_sigpending_and_wake(struct task_struct *t) void recalc_sigpending(void) { - if (unlikely(tracehook_force_sigpending())) - set_thread_flag(TIF_SIGPENDING); - else if (!recalc_sigpending_tsk(current) && !freezing(current)) + if (!recalc_sigpending_tsk(current) && !freezing(current)) clear_thread_flag(TIF_SIGPENDING); } @@ -2005,8 +2003,6 @@ retry: spin_unlock_irq(¤t->sighand->siglock); - tracehook_finish_jctl(); - return 1; } @@ -2109,37 +2105,25 @@ relock: for (;;) { struct k_sigaction *ka; - /* - * Tracing can induce an artificial signal and choose sigaction. - * The return value in @signr determines the default action, - * but @info->si_signo is the signal number we will report. - */ - signr = tracehook_get_signal(current, regs, info, return_ka); - if (unlikely(signr < 0)) + + if (unlikely(current->jobctl & JOBCTL_STOP_PENDING) && + do_signal_stop(0)) goto relock; - if (unlikely(signr != 0)) - ka = return_ka; - else { - if (unlikely(current->jobctl & JOBCTL_STOP_PENDING) && - do_signal_stop(0)) - goto relock; - signr = dequeue_signal(current, ¤t->blocked, - info); + signr = dequeue_signal(current, ¤t->blocked, info); - if (!signr) - break; /* will return 0 */ + if (!signr) + break; /* will return 0 */ - if (signr != SIGKILL) { - signr = ptrace_signal(signr, info, - regs, cookie); - if (!signr) - continue; - } - - ka = &sighand->action[signr-1]; + if (signr != SIGKILL) { + signr = ptrace_signal(signr, info, + regs, cookie); + if (!signr) + continue; } + ka = &sighand->action[signr-1]; + /* Trace actually delivered signals. */ trace_signal_deliver(signr, info, ka); -- cgit v1.2.3 From e3cc055c18ab575291acf0af7622a2e97c4728fa Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 3 Jun 2011 11:51:21 +0000 Subject: include/net: Remove unnecessary semicolons Semicolons are not necessary after switch/while/for/if braces so remove them. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index f9fe374ac9f4..6a72a58cde59 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -601,7 +601,7 @@ static inline int ipver2af(__u8 ipver) return AF_INET6; default: return 0; - }; + } } /* Convert from an address parameter type to an address family. */ @@ -614,7 +614,7 @@ static inline int param_type2af(__be16 type) return AF_INET6; default: return 0; - }; + } } /* Perform some sanity checks. */ -- cgit v1.2.3 From fb04883371f2cb7867d24783e7d590036dc9b548 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 19 May 2011 15:44:27 +0200 Subject: netfilter: add more values to enum ip_conntrack_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following error is raised (and other similar ones) : net/ipv4/netfilter/nf_nat_standalone.c: In function ‘nf_nat_fn’: net/ipv4/netfilter/nf_nat_standalone.c:119:2: warning: case value ‘4’ not in enumerated type ‘enum ip_conntrack_info’ gcc barfs on adding two enum values and getting a not enumerated result : case IP_CT_RELATED+IP_CT_IS_REPLY: Add missing enum values Signed-off-by: Eric Dumazet CC: David Miller Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_conntrack_common.h | 3 +++ net/ipv4/netfilter/ipt_CLUSTERIP.c | 6 +++--- net/ipv4/netfilter/ipt_MASQUERADE.c | 2 +- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 2 +- net/ipv4/netfilter/nf_nat_core.c | 2 +- net/ipv4/netfilter/nf_nat_rule.c | 2 +- net/ipv4/netfilter/nf_nat_standalone.c | 4 ++-- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 2 +- net/netfilter/nf_conntrack_core.c | 4 ++-- net/netfilter/nf_conntrack_ftp.c | 2 +- net/netfilter/nf_conntrack_h323_main.c | 10 ++++------ net/netfilter/nf_conntrack_irc.c | 3 +-- net/netfilter/nf_conntrack_pptp.c | 3 +-- net/netfilter/nf_conntrack_sane.c | 2 +- net/netfilter/nf_conntrack_sip.c | 2 +- net/netfilter/xt_socket.c | 4 ++-- 16 files changed, 26 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h index 50cdc2559a5a..0d3dd66322ec 100644 --- a/include/linux/netfilter/nf_conntrack_common.h +++ b/include/linux/netfilter/nf_conntrack_common.h @@ -18,6 +18,9 @@ enum ip_conntrack_info { /* >= this indicates reply direction */ IP_CT_IS_REPLY, + IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY, + IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY, + IP_CT_NEW_REPLY = IP_CT_NEW + IP_CT_IS_REPLY, /* Number of distinct IP_CT types (no NEW in reply dirn). */ IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1 }; diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index d609ac3cb9a4..5c9e97c79017 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -307,7 +307,7 @@ clusterip_tg(struct sk_buff *skb, const struct xt_action_param *par) * error messages (RELATED) and information requests (see below) */ if (ip_hdr(skb)->protocol == IPPROTO_ICMP && (ctinfo == IP_CT_RELATED || - ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)) + ctinfo == IP_CT_RELATED_REPLY)) return XT_CONTINUE; /* ip_conntrack_icmp guarantees us that we only have ICMP_ECHO, @@ -321,12 +321,12 @@ clusterip_tg(struct sk_buff *skb, const struct xt_action_param *par) ct->mark = hash; break; case IP_CT_RELATED: - case IP_CT_RELATED+IP_CT_IS_REPLY: + case IP_CT_RELATED_REPLY: /* FIXME: we don't handle expectations at the * moment. they can arrive on a different node than * the master connection (e.g. FTP passive mode) */ case IP_CT_ESTABLISHED: - case IP_CT_ESTABLISHED+IP_CT_IS_REPLY: + case IP_CT_ESTABLISHED_REPLY: break; default: break; diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index d2ed9dc74ebc..9931152a78b5 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -60,7 +60,7 @@ masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par) nat = nfct_nat(ct); NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || - ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)); + ctinfo == IP_CT_RELATED_REPLY)); /* Source address is 0.0.0.0 - locally generated packet that is * probably not supposed to be masqueraded. diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 5a03c02af999..db10075dd88e 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -101,7 +101,7 @@ static unsigned int ipv4_confirm(unsigned int hooknum, /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(skb, &ctinfo); - if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) + if (!ct || ctinfo == IP_CT_RELATED_REPLY) goto out; help = nfct_help(ct); diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index 9c71b2755ce3..3346de5d94d0 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c @@ -433,7 +433,7 @@ int nf_nat_icmp_reply_translation(struct nf_conn *ct, /* Must be RELATED */ NF_CT_ASSERT(skb->nfctinfo == IP_CT_RELATED || - skb->nfctinfo == IP_CT_RELATED+IP_CT_IS_REPLY); + skb->nfctinfo == IP_CT_RELATED_REPLY); /* Redirects on non-null nats must be dropped, else they'll start talking to each other without our translation, and be diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c index 21c30426480b..733c9abc1cbd 100644 --- a/net/ipv4/netfilter/nf_nat_rule.c +++ b/net/ipv4/netfilter/nf_nat_rule.c @@ -53,7 +53,7 @@ ipt_snat_target(struct sk_buff *skb, const struct xt_action_param *par) /* Connection must be valid and new. */ NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || - ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)); + ctinfo == IP_CT_RELATED_REPLY)); NF_CT_ASSERT(par->out != NULL); return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_SRC); diff --git a/net/ipv4/netfilter/nf_nat_standalone.c b/net/ipv4/netfilter/nf_nat_standalone.c index 7317bdf1d457..483b76d042da 100644 --- a/net/ipv4/netfilter/nf_nat_standalone.c +++ b/net/ipv4/netfilter/nf_nat_standalone.c @@ -116,7 +116,7 @@ nf_nat_fn(unsigned int hooknum, switch (ctinfo) { case IP_CT_RELATED: - case IP_CT_RELATED+IP_CT_IS_REPLY: + case IP_CT_RELATED_REPLY: if (ip_hdr(skb)->protocol == IPPROTO_ICMP) { if (!nf_nat_icmp_reply_translation(ct, ctinfo, hooknum, skb)) @@ -144,7 +144,7 @@ nf_nat_fn(unsigned int hooknum, default: /* ESTABLISHED */ NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || - ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY)); + ctinfo == IP_CT_ESTABLISHED_REPLY); } return nf_nat_packet(ct, ctinfo, hooknum, skb); diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index c8af58b22562..4111050a9fc5 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -160,7 +160,7 @@ static unsigned int ipv6_confirm(unsigned int hooknum, /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(skb, &ctinfo); - if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) + if (!ct || ctinfo == IP_CT_RELATED_REPLY) goto out; help = nfct_help(ct); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 2e1c11f78419..0bd568929403 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -850,7 +850,7 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl, /* It exists; we have (non-exclusive) reference. */ if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) { - *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY; + *ctinfo = IP_CT_ESTABLISHED_REPLY; /* Please set reply bit if this packet OK */ *set_reply = 1; } else { @@ -1143,7 +1143,7 @@ static void nf_conntrack_attach(struct sk_buff *nskb, struct sk_buff *skb) /* This ICMP is in reverse direction to the packet which caused it */ ct = nf_ct_get(skb, &ctinfo); if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) - ctinfo = IP_CT_RELATED + IP_CT_IS_REPLY; + ctinfo = IP_CT_RELATED_REPLY; else ctinfo = IP_CT_RELATED; diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index e17cb7c7dd8f..6f5801eac999 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -368,7 +368,7 @@ static int help(struct sk_buff *skb, /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + ctinfo != IP_CT_ESTABLISHED_REPLY) { pr_debug("ftp: Conntrackinfo = %u\n", ctinfo); return NF_ACCEPT; } diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 18b2ce5c8ced..f03c2d4539f6 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -571,10 +571,9 @@ static int h245_help(struct sk_buff *skb, unsigned int protoff, int ret; /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; - } + pr_debug("nf_ct_h245: skblen = %u\n", skb->len); spin_lock_bh(&nf_h323_lock); @@ -1125,10 +1124,9 @@ static int q931_help(struct sk_buff *skb, unsigned int protoff, int ret; /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; - } + pr_debug("nf_ct_q931: skblen = %u\n", skb->len); spin_lock_bh(&nf_h323_lock); diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index b394aa318776..4f9390b98697 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -125,8 +125,7 @@ static int help(struct sk_buff *skb, unsigned int protoff, return NF_ACCEPT; /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) + if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; /* Not a full tcp header? */ diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 088944824e13..2fd4565144de 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -519,8 +519,7 @@ conntrack_pptp_help(struct sk_buff *skb, unsigned int protoff, u_int16_t msg; /* don't do any tracking before tcp handshake complete */ - if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) + if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; nexthdr_off = protoff; diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c index d9e27734b2a2..8501823b3f9b 100644 --- a/net/netfilter/nf_conntrack_sane.c +++ b/net/netfilter/nf_conntrack_sane.c @@ -78,7 +78,7 @@ static int help(struct sk_buff *skb, ct_sane_info = &nfct_help(ct)->help.ct_sane_info; /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) + ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; /* Not a full tcp header? */ diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index cb5a28581782..93faf6a3a637 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1423,7 +1423,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, typeof(nf_nat_sip_seq_adjust_hook) nf_nat_sip_seq_adjust; if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) + ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; /* No Data ? */ diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 9cc46356b577..fe39f7e913df 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -143,9 +143,9 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, ct = nf_ct_get(skb, &ctinfo); if (ct && !nf_ct_is_untracked(ct) && ((iph->protocol != IPPROTO_ICMP && - ctinfo == IP_CT_IS_REPLY + IP_CT_ESTABLISHED) || + ctinfo == IP_CT_ESTABLISHED_REPLY) || (iph->protocol == IPPROTO_ICMP && - ctinfo == IP_CT_IS_REPLY + IP_CT_RELATED)) && + ctinfo == IP_CT_RELATED_REPLY)) && (ct->status & IPS_SRC_NAT_DONE)) { daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; -- cgit v1.2.3 From a9c667f8f0656631ee5438baaf21bf30d5f67375 Mon Sep 17 00:00:00 2001 From: Lukas Czerner Date: Mon, 6 Jun 2011 09:51:52 -0400 Subject: ext4: fixed tracepoints cleanup While creating fixed tracepoints for ext3, basically by porting them from ext4, I found a lot of useless retyping, wrong type usage, useless variable passing and other inconsistencies in the ext4 fixed tracepoint code. This patch cleans the fixed tracepoint code for ext4 and also simplify some of them. Signed-off-by: Lukas Czerner Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 2 +- fs/ext4/mballoc.c | 6 +- include/trace/events/ext4.h | 179 +++++++++++++++++++------------------------- 3 files changed, 80 insertions(+), 107 deletions(-) (limited to 'include') diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index a5763e3505ba..e3126c051006 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2634,7 +2634,7 @@ static int ext4_writepage(struct page *page, struct buffer_head *page_bufs = NULL; struct inode *inode = page->mapping->host; - trace_ext4_writepage(inode, page); + trace_ext4_writepage(page); size = i_size_read(inode); if (page->index == size >> PAGE_CACHE_SHIFT) len = size & ~PAGE_CACHE_MASK; diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index eb71dd59f2e4..6ed859d56850 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -3578,8 +3578,8 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh, free += next - bit; trace_ext4_mballoc_discard(sb, NULL, group, bit, next - bit); - trace_ext4_mb_release_inode_pa(sb, pa->pa_inode, pa, - grp_blk_start + bit, next - bit); + trace_ext4_mb_release_inode_pa(pa, grp_blk_start + bit, + next - bit); mb_free_blocks(pa->pa_inode, e4b, bit, next - bit); bit = next + 1; } @@ -3608,7 +3608,7 @@ ext4_mb_release_group_pa(struct ext4_buddy *e4b, ext4_group_t group; ext4_grpblk_t bit; - trace_ext4_mb_release_group_pa(sb, pa); + trace_ext4_mb_release_group_pa(pa); BUG_ON(pa->pa_deleted == 0); ext4_get_group_no_and_offset(sb, pa->pa_pstart, &group, &bit); BUG_ON(group != e4b->bd_group && pa->pa_len != 0); diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index e09592d2f916..5ce2b2f5f524 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -26,7 +26,7 @@ TRACE_EVENT(ext4_free_inode, __field( umode_t, mode ) __field( uid_t, uid ) __field( gid_t, gid ) - __field( blkcnt_t, blocks ) + __field( __u64, blocks ) ), TP_fast_assign( @@ -40,9 +40,8 @@ TRACE_EVENT(ext4_free_inode, TP_printk("dev %d,%d ino %lu mode 0%o uid %u gid %u blocks %llu", MAJOR(__entry->dev), MINOR(__entry->dev), - (unsigned long) __entry->ino, - __entry->mode, __entry->uid, __entry->gid, - (unsigned long long) __entry->blocks) + (unsigned long) __entry->ino, __entry->mode, + __entry->uid, __entry->gid, __entry->blocks) ); TRACE_EVENT(ext4_request_inode, @@ -178,7 +177,7 @@ TRACE_EVENT(ext4_begin_ordered_truncate, TP_printk("dev %d,%d ino %lu new_size %lld", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - (long long) __entry->new_size) + __entry->new_size) ); DECLARE_EVENT_CLASS(ext4__write_begin, @@ -204,7 +203,7 @@ DECLARE_EVENT_CLASS(ext4__write_begin, __entry->flags = flags; ), - TP_printk("dev %d,%d ino %lu pos %llu len %u flags %u", + TP_printk("dev %d,%d ino %lu pos %lld len %u flags %u", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, __entry->pos, __entry->len, __entry->flags) @@ -248,7 +247,7 @@ DECLARE_EVENT_CLASS(ext4__write_end, __entry->copied = copied; ), - TP_printk("dev %d,%d ino %lu pos %llu len %u copied %u", + TP_printk("dev %d,%d ino %lu pos %lld len %u copied %u", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, __entry->pos, __entry->len, __entry->copied) @@ -286,29 +285,6 @@ DEFINE_EVENT(ext4__write_end, ext4_da_write_end, TP_ARGS(inode, pos, len, copied) ); -TRACE_EVENT(ext4_writepage, - TP_PROTO(struct inode *inode, struct page *page), - - TP_ARGS(inode, page), - - TP_STRUCT__entry( - __field( dev_t, dev ) - __field( ino_t, ino ) - __field( pgoff_t, index ) - - ), - - TP_fast_assign( - __entry->dev = inode->i_sb->s_dev; - __entry->ino = inode->i_ino; - __entry->index = page->index; - ), - - TP_printk("dev %d,%d ino %lu page_index %lu", - MAJOR(__entry->dev), MINOR(__entry->dev), - (unsigned long) __entry->ino, __entry->index) -); - TRACE_EVENT(ext4_da_writepages, TP_PROTO(struct inode *inode, struct writeback_control *wbc), @@ -341,7 +317,7 @@ TRACE_EVENT(ext4_da_writepages, ), TP_printk("dev %d,%d ino %lu nr_to_write %ld pages_skipped %ld " - "range_start %llu range_end %llu sync_mode %d" + "range_start %lld range_end %lld sync_mode %d" "for_kupdate %d range_cyclic %d writeback_index %lu", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, __entry->nr_to_write, @@ -449,7 +425,14 @@ DECLARE_EVENT_CLASS(ext4__page_op, TP_printk("dev %d,%d ino %lu page_index %lu", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - __entry->index) + (unsigned long) __entry->index) +); + +DEFINE_EVENT(ext4__page_op, ext4_writepage, + + TP_PROTO(struct page *page), + + TP_ARGS(page) ); DEFINE_EVENT(ext4__page_op, ext4_readpage, @@ -489,7 +472,7 @@ TRACE_EVENT(ext4_invalidatepage, TP_printk("dev %d,%d ino %lu page_index %lu offset %lu", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - __entry->index, __entry->offset) + (unsigned long) __entry->index, __entry->offset) ); TRACE_EVENT(ext4_discard_blocks, @@ -562,12 +545,10 @@ DEFINE_EVENT(ext4__mb_new_pa, ext4_mb_new_group_pa, ); TRACE_EVENT(ext4_mb_release_inode_pa, - TP_PROTO(struct super_block *sb, - struct inode *inode, - struct ext4_prealloc_space *pa, + TP_PROTO(struct ext4_prealloc_space *pa, unsigned long long block, unsigned int count), - TP_ARGS(sb, inode, pa, block, count), + TP_ARGS(pa, block, count), TP_STRUCT__entry( __field( dev_t, dev ) @@ -578,8 +559,8 @@ TRACE_EVENT(ext4_mb_release_inode_pa, ), TP_fast_assign( - __entry->dev = sb->s_dev; - __entry->ino = inode->i_ino; + __entry->dev = pa->pa_inode->i_sb->s_dev; + __entry->ino = pa->pa_inode->i_ino; __entry->block = block; __entry->count = count; ), @@ -591,10 +572,9 @@ TRACE_EVENT(ext4_mb_release_inode_pa, ); TRACE_EVENT(ext4_mb_release_group_pa, - TP_PROTO(struct super_block *sb, - struct ext4_prealloc_space *pa), + TP_PROTO(struct ext4_prealloc_space *pa), - TP_ARGS(sb, pa), + TP_ARGS(pa), TP_STRUCT__entry( __field( dev_t, dev ) @@ -604,7 +584,7 @@ TRACE_EVENT(ext4_mb_release_group_pa, ), TP_fast_assign( - __entry->dev = sb->s_dev; + __entry->dev = pa->pa_inode->i_sb->s_dev; __entry->pa_pstart = pa->pa_pstart; __entry->pa_len = pa->pa_len; ), @@ -666,10 +646,10 @@ TRACE_EVENT(ext4_request_blocks, __field( ino_t, ino ) __field( unsigned int, flags ) __field( unsigned int, len ) - __field( __u64, logical ) + __field( __u32, logical ) + __field( __u32, lleft ) + __field( __u32, lright ) __field( __u64, goal ) - __field( __u64, lleft ) - __field( __u64, lright ) __field( __u64, pleft ) __field( __u64, pright ) ), @@ -687,17 +667,13 @@ TRACE_EVENT(ext4_request_blocks, __entry->pright = ar->pright; ), - TP_printk("dev %d,%d ino %lu flags %u len %u lblk %llu goal %llu " - "lleft %llu lright %llu pleft %llu pright %llu ", + TP_printk("dev %d,%d ino %lu flags %u len %u lblk %u goal %llu " + "lleft %u lright %u pleft %llu pright %llu ", MAJOR(__entry->dev), MINOR(__entry->dev), - (unsigned long) __entry->ino, - __entry->flags, __entry->len, - (unsigned long long) __entry->logical, - (unsigned long long) __entry->goal, - (unsigned long long) __entry->lleft, - (unsigned long long) __entry->lright, - (unsigned long long) __entry->pleft, - (unsigned long long) __entry->pright) + (unsigned long) __entry->ino, __entry->flags, + __entry->len, __entry->logical, __entry->goal, + __entry->lleft, __entry->lright, __entry->pleft, + __entry->pright) ); TRACE_EVENT(ext4_allocate_blocks, @@ -711,10 +687,10 @@ TRACE_EVENT(ext4_allocate_blocks, __field( __u64, block ) __field( unsigned int, flags ) __field( unsigned int, len ) - __field( __u64, logical ) + __field( __u32, logical ) + __field( __u32, lleft ) + __field( __u32, lright ) __field( __u64, goal ) - __field( __u64, lleft ) - __field( __u64, lright ) __field( __u64, pleft ) __field( __u64, pright ) ), @@ -733,17 +709,13 @@ TRACE_EVENT(ext4_allocate_blocks, __entry->pright = ar->pright; ), - TP_printk("dev %d,%d ino %lu flags %u len %u block %llu lblk %llu " - "goal %llu lleft %llu lright %llu pleft %llu pright %llu", + TP_printk("dev %d,%d ino %lu flags %u len %u block %llu lblk %u " + "goal %llu lleft %u lright %u pleft %llu pright %llu", MAJOR(__entry->dev), MINOR(__entry->dev), - (unsigned long) __entry->ino, - __entry->flags, __entry->len, __entry->block, - (unsigned long long) __entry->logical, - (unsigned long long) __entry->goal, - (unsigned long long) __entry->lleft, - (unsigned long long) __entry->lright, - (unsigned long long) __entry->pleft, - (unsigned long long) __entry->pright) + (unsigned long) __entry->ino, __entry->flags, + __entry->len, __entry->block, __entry->logical, + __entry->goal, __entry->lleft, __entry->lright, + __entry->pleft, __entry->pright) ); TRACE_EVENT(ext4_free_blocks, @@ -755,10 +727,10 @@ TRACE_EVENT(ext4_free_blocks, TP_STRUCT__entry( __field( dev_t, dev ) __field( ino_t, ino ) - __field( umode_t, mode ) + __field( umode_t, mode ) __field( __u64, block ) __field( unsigned long, count ) - __field( int, flags ) + __field( int, flags ) ), TP_fast_assign( @@ -798,7 +770,7 @@ TRACE_EVENT(ext4_sync_file_enter, __entry->parent = dentry->d_parent->d_inode->i_ino; ), - TP_printk("dev %d,%d ino %ld parent %ld datasync %d ", + TP_printk("dev %d,%d ino %lu parent %lu datasync %d ", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, (unsigned long) __entry->parent, __entry->datasync) @@ -821,7 +793,7 @@ TRACE_EVENT(ext4_sync_file_exit, __entry->dev = inode->i_sb->s_dev; ), - TP_printk("dev %d,%d ino %ld ret %d", + TP_printk("dev %d,%d ino %lu ret %d", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, __entry->ret) @@ -1005,7 +977,7 @@ DECLARE_EVENT_CLASS(ext4__mballoc, __entry->result_len = len; ), - TP_printk("dev %d,%d inode %lu extent %u/%d/%u ", + TP_printk("dev %d,%d inode %lu extent %u/%d/%d ", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, __entry->result_group, __entry->result_start, @@ -1093,7 +1065,7 @@ TRACE_EVENT(ext4_da_update_reserve_space, "allocated_meta_blocks %d", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - __entry->mode, (unsigned long long) __entry->i_blocks, + __entry->mode, __entry->i_blocks, __entry->used_blocks, __entry->reserved_data_blocks, __entry->reserved_meta_blocks, __entry->allocated_meta_blocks) ); @@ -1127,7 +1099,7 @@ TRACE_EVENT(ext4_da_reserve_space, "reserved_data_blocks %d reserved_meta_blocks %d", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - __entry->mode, (unsigned long long) __entry->i_blocks, + __entry->mode, __entry->i_blocks, __entry->md_needed, __entry->reserved_data_blocks, __entry->reserved_meta_blocks) ); @@ -1164,7 +1136,7 @@ TRACE_EVENT(ext4_da_release_space, "allocated_meta_blocks %d", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - __entry->mode, (unsigned long long) __entry->i_blocks, + __entry->mode, __entry->i_blocks, __entry->freed_blocks, __entry->reserved_data_blocks, __entry->reserved_meta_blocks, __entry->allocated_meta_blocks) ); @@ -1239,14 +1211,15 @@ TRACE_EVENT(ext4_direct_IO_enter, __entry->rw = rw; ), - TP_printk("dev %d,%d ino %lu pos %llu len %lu rw %d", + TP_printk("dev %d,%d ino %lu pos %lld len %lu rw %d", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - (unsigned long long) __entry->pos, __entry->len, __entry->rw) + __entry->pos, __entry->len, __entry->rw) ); TRACE_EVENT(ext4_direct_IO_exit, - TP_PROTO(struct inode *inode, loff_t offset, unsigned long len, int rw, int ret), + TP_PROTO(struct inode *inode, loff_t offset, unsigned long len, + int rw, int ret), TP_ARGS(inode, offset, len, rw, ret), @@ -1268,10 +1241,10 @@ TRACE_EVENT(ext4_direct_IO_exit, __entry->ret = ret; ), - TP_printk("dev %d,%d ino %lu pos %llu len %lu rw %d ret %d", + TP_printk("dev %d,%d ino %lu pos %lld len %lu rw %d ret %d", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - (unsigned long long) __entry->pos, __entry->len, + __entry->pos, __entry->len, __entry->rw, __entry->ret) ); @@ -1296,15 +1269,15 @@ TRACE_EVENT(ext4_fallocate_enter, __entry->mode = mode; ), - TP_printk("dev %d,%d ino %ld pos %llu len %llu mode %d", + TP_printk("dev %d,%d ino %lu pos %lld len %lld mode %d", MAJOR(__entry->dev), MINOR(__entry->dev), - (unsigned long) __entry->ino, - (unsigned long long) __entry->pos, - (unsigned long long) __entry->len, __entry->mode) + (unsigned long) __entry->ino, __entry->pos, + __entry->len, __entry->mode) ); TRACE_EVENT(ext4_fallocate_exit, - TP_PROTO(struct inode *inode, loff_t offset, unsigned int max_blocks, int ret), + TP_PROTO(struct inode *inode, loff_t offset, + unsigned int max_blocks, int ret), TP_ARGS(inode, offset, max_blocks, ret), @@ -1312,7 +1285,7 @@ TRACE_EVENT(ext4_fallocate_exit, __field( ino_t, ino ) __field( dev_t, dev ) __field( loff_t, pos ) - __field( unsigned, blocks ) + __field( unsigned int, blocks ) __field( int, ret ) ), @@ -1324,10 +1297,10 @@ TRACE_EVENT(ext4_fallocate_exit, __entry->ret = ret; ), - TP_printk("dev %d,%d ino %ld pos %llu blocks %d ret %d", + TP_printk("dev %d,%d ino %lu pos %lld blocks %u ret %d", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - (unsigned long long) __entry->pos, __entry->blocks, + __entry->pos, __entry->blocks, __entry->ret) ); @@ -1350,7 +1323,7 @@ TRACE_EVENT(ext4_unlink_enter, __entry->dev = dentry->d_inode->i_sb->s_dev; ), - TP_printk("dev %d,%d ino %ld size %lld parent %ld", + TP_printk("dev %d,%d ino %lu size %lld parent %lu", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, __entry->size, (unsigned long) __entry->parent) @@ -1373,7 +1346,7 @@ TRACE_EVENT(ext4_unlink_exit, __entry->ret = ret; ), - TP_printk("dev %d,%d ino %ld ret %d", + TP_printk("dev %d,%d ino %lu ret %d", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, __entry->ret) @@ -1387,7 +1360,7 @@ DECLARE_EVENT_CLASS(ext4__truncate, TP_STRUCT__entry( __field( ino_t, ino ) __field( dev_t, dev ) - __field( blkcnt_t, blocks ) + __field( __u64, blocks ) ), TP_fast_assign( @@ -1396,9 +1369,9 @@ DECLARE_EVENT_CLASS(ext4__truncate, __entry->blocks = inode->i_blocks; ), - TP_printk("dev %d,%d ino %lu blocks %lu", + TP_printk("dev %d,%d ino %lu blocks %llu", MAJOR(__entry->dev), MINOR(__entry->dev), - (unsigned long) __entry->ino, (unsigned long) __entry->blocks) + (unsigned long) __entry->ino, __entry->blocks) ); DEFINE_EVENT(ext4__truncate, ext4_truncate_enter, @@ -1417,7 +1390,7 @@ DEFINE_EVENT(ext4__truncate, ext4_truncate_exit, DECLARE_EVENT_CLASS(ext4__map_blocks_enter, TP_PROTO(struct inode *inode, ext4_lblk_t lblk, - unsigned len, unsigned flags), + unsigned int len, unsigned int flags), TP_ARGS(inode, lblk, len, flags), @@ -1425,8 +1398,8 @@ DECLARE_EVENT_CLASS(ext4__map_blocks_enter, __field( ino_t, ino ) __field( dev_t, dev ) __field( ext4_lblk_t, lblk ) - __field( unsigned, len ) - __field( unsigned, flags ) + __field( unsigned int, len ) + __field( unsigned int, flags ) ), TP_fast_assign( @@ -1440,7 +1413,7 @@ DECLARE_EVENT_CLASS(ext4__map_blocks_enter, TP_printk("dev %d,%d ino %lu lblk %u len %u flags %u", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - (unsigned) __entry->lblk, __entry->len, __entry->flags) + __entry->lblk, __entry->len, __entry->flags) ); DEFINE_EVENT(ext4__map_blocks_enter, ext4_ext_map_blocks_enter, @@ -1459,7 +1432,7 @@ DEFINE_EVENT(ext4__map_blocks_enter, ext4_ind_map_blocks_enter, DECLARE_EVENT_CLASS(ext4__map_blocks_exit, TP_PROTO(struct inode *inode, ext4_lblk_t lblk, - ext4_fsblk_t pblk, unsigned len, int ret), + ext4_fsblk_t pblk, unsigned int len, int ret), TP_ARGS(inode, lblk, pblk, len, ret), @@ -1468,7 +1441,7 @@ DECLARE_EVENT_CLASS(ext4__map_blocks_exit, __field( dev_t, dev ) __field( ext4_lblk_t, lblk ) __field( ext4_fsblk_t, pblk ) - __field( unsigned, len ) + __field( unsigned int, len ) __field( int, ret ) ), @@ -1484,7 +1457,7 @@ DECLARE_EVENT_CLASS(ext4__map_blocks_exit, TP_printk("dev %d,%d ino %lu lblk %u pblk %llu len %u ret %d", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - (unsigned) __entry->lblk, (unsigned long long) __entry->pblk, + __entry->lblk, __entry->pblk, __entry->len, __entry->ret) ); @@ -1524,7 +1497,7 @@ TRACE_EVENT(ext4_ext_load_extent, TP_printk("dev %d,%d ino %lu lblk %u pblk %llu", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - (unsigned) __entry->lblk, (unsigned long long) __entry->pblk) + __entry->lblk, __entry->pblk) ); TRACE_EVENT(ext4_load_inode, -- cgit v1.2.3 From c2419b4a4727f67af2fc2cd68b0d878b75e781bb Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Tue, 31 May 2011 10:50:10 -0400 Subject: xen: allow enable use of VGA console on dom0 Get the information about the VGA console hardware from Xen, and put it into the form the bootloader normally generates, so that the rest of the kernel can deal with VGA as usual. [ Impact: make VGA console work in dom0 ] Signed-off-by: Jeremy Fitzhardinge [v1: Rebased on 2.6.39] [v2: Removed incorrect comments and fixed compile warnings] Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/Makefile | 2 +- arch/x86/xen/enlighten.c | 8 ++++++ arch/x86/xen/vga.c | 67 +++++++++++++++++++++++++++++++++++++++++++++ arch/x86/xen/xen-ops.h | 11 ++++++++ include/xen/interface/xen.h | 39 ++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 arch/x86/xen/vga.c (limited to 'include') diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile index 17c565de3d64..a6575b949b11 100644 --- a/arch/x86/xen/Makefile +++ b/arch/x86/xen/Makefile @@ -18,5 +18,5 @@ obj-y := enlighten.o setup.o multicalls.o mmu.o irq.o \ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= spinlock.o obj-$(CONFIG_XEN_DEBUG_FS) += debugfs.o - +obj-$(CONFIG_XEN_DOM0) += vga.o obj-$(CONFIG_SWIOTLB_XEN) += pci-swiotlb-xen.o diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index e3c6a06cf725..4abd2d5d04f7 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -1241,6 +1241,14 @@ asmlinkage void __init xen_start_kernel(void) if (pci_xen) x86_init.pci.arch_init = pci_xen_init; } else { + const struct dom0_vga_console_info *info = + (void *)((char *)xen_start_info + + xen_start_info->console.dom0.info_off); + + xen_init_vga(info, xen_start_info->console.dom0.info_size); + xen_start_info->console.domU.mfn = 0; + xen_start_info->console.domU.evtchn = 0; + /* Make sure ACS will be enabled */ pci_request_acs(); } diff --git a/arch/x86/xen/vga.c b/arch/x86/xen/vga.c new file mode 100644 index 000000000000..1cd7f4d11e29 --- /dev/null +++ b/arch/x86/xen/vga.c @@ -0,0 +1,67 @@ +#include +#include + +#include +#include + +#include + +#include "xen-ops.h" + +void __init xen_init_vga(const struct dom0_vga_console_info *info, size_t size) +{ + struct screen_info *screen_info = &boot_params.screen_info; + + /* This is drawn from a dump from vgacon:startup in + * standard Linux. */ + screen_info->orig_video_mode = 3; + screen_info->orig_video_isVGA = 1; + screen_info->orig_video_lines = 25; + screen_info->orig_video_cols = 80; + screen_info->orig_video_ega_bx = 3; + screen_info->orig_video_points = 16; + screen_info->orig_y = screen_info->orig_video_lines - 1; + + switch (info->video_type) { + case XEN_VGATYPE_TEXT_MODE_3: + if (size < offsetof(struct dom0_vga_console_info, u.text_mode_3) + + sizeof(info->u.text_mode_3)) + break; + screen_info->orig_video_lines = info->u.text_mode_3.rows; + screen_info->orig_video_cols = info->u.text_mode_3.columns; + screen_info->orig_x = info->u.text_mode_3.cursor_x; + screen_info->orig_y = info->u.text_mode_3.cursor_y; + screen_info->orig_video_points = + info->u.text_mode_3.font_height; + break; + + case XEN_VGATYPE_VESA_LFB: + if (size < offsetof(struct dom0_vga_console_info, + u.vesa_lfb.gbl_caps)) + break; + screen_info->orig_video_isVGA = VIDEO_TYPE_VLFB; + screen_info->lfb_width = info->u.vesa_lfb.width; + screen_info->lfb_height = info->u.vesa_lfb.height; + screen_info->lfb_depth = info->u.vesa_lfb.bits_per_pixel; + screen_info->lfb_base = info->u.vesa_lfb.lfb_base; + screen_info->lfb_size = info->u.vesa_lfb.lfb_size; + screen_info->lfb_linelength = info->u.vesa_lfb.bytes_per_line; + screen_info->red_size = info->u.vesa_lfb.red_size; + screen_info->red_pos = info->u.vesa_lfb.red_pos; + screen_info->green_size = info->u.vesa_lfb.green_size; + screen_info->green_pos = info->u.vesa_lfb.green_pos; + screen_info->blue_size = info->u.vesa_lfb.blue_size; + screen_info->blue_pos = info->u.vesa_lfb.blue_pos; + screen_info->rsvd_size = info->u.vesa_lfb.rsvd_size; + screen_info->rsvd_pos = info->u.vesa_lfb.rsvd_pos; + if (size >= offsetof(struct dom0_vga_console_info, + u.vesa_lfb.gbl_caps) + + sizeof(info->u.vesa_lfb.gbl_caps)) + screen_info->capabilities = info->u.vesa_lfb.gbl_caps; + if (size >= offsetof(struct dom0_vga_console_info, + u.vesa_lfb.mode_attrs) + + sizeof(info->u.vesa_lfb.mode_attrs)) + screen_info->vesa_attributes = info->u.vesa_lfb.mode_attrs; + break; + } +} diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index 3112f55638c4..e14c54eff23d 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h @@ -88,6 +88,17 @@ static inline void xen_uninit_lock_cpu(int cpu) } #endif +struct dom0_vga_console_info; + +#ifdef CONFIG_XEN_DOM0 +void __init xen_init_vga(const struct dom0_vga_console_info *, size_t size); +#else +static inline void __init xen_init_vga(const struct dom0_vga_console_info *info, + size_t size) +{ +} +#endif + /* Declare an asm function, along with symbols needed to make it inlineable */ #define DECL_ASM(ret, name, ...) \ diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h index b33257bc7e83..29b362eab6cc 100644 --- a/include/xen/interface/xen.h +++ b/include/xen/interface/xen.h @@ -449,6 +449,45 @@ struct start_info { int8_t cmd_line[MAX_GUEST_CMDLINE]; }; +struct dom0_vga_console_info { + uint8_t video_type; +#define XEN_VGATYPE_TEXT_MODE_3 0x03 +#define XEN_VGATYPE_VESA_LFB 0x23 + + union { + struct { + /* Font height, in pixels. */ + uint16_t font_height; + /* Cursor location (column, row). */ + uint16_t cursor_x, cursor_y; + /* Number of rows and columns (dimensions in characters). */ + uint16_t rows, columns; + } text_mode_3; + + struct { + /* Width and height, in pixels. */ + uint16_t width, height; + /* Bytes per scan line. */ + uint16_t bytes_per_line; + /* Bits per pixel. */ + uint16_t bits_per_pixel; + /* LFB physical address, and size (in units of 64kB). */ + uint32_t lfb_base; + uint32_t lfb_size; + /* RGB mask offsets and sizes, as defined by VBE 1.2+ */ + uint8_t red_pos, red_size; + uint8_t green_pos, green_size; + uint8_t blue_pos, blue_size; + uint8_t rsvd_pos, rsvd_size; + + /* VESA capabilities (offset 0xa, VESA command 0x4f00). */ + uint32_t gbl_caps; + /* Mode attributes (offset 0x0, VESA command 0x4f01). */ + uint16_t mode_attrs; + } vesa_lfb; + } u; +}; + /* These flags are passed in the 'flags' field of start_info_t. */ #define SIF_PRIVILEGED (1<<0) /* Is the domain privileged? */ #define SIF_INITDOMAIN (1<<1) /* Is this the initial control domain? */ -- cgit v1.2.3 From b084f598df36b62dfae83c10ed17f0b66b50f442 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 31 May 2011 12:24:58 -0400 Subject: nfsd: fix dependency of nfsd on auth_rpcgss Commit b0b0c0a26e84 "nfsd: add proc file listing kernel's gss_krb5 enctypes" added an nunnecessary dependency of nfsd on the auth_rpcgss module. It's a little ad hoc, but since the only piece of information nfsd needs from rpcsec_gss_krb5 is a single static string, one solution is just to share it with an include file. Cc: stable@kernel.org Reported-by: Michael Guntsche Cc: Kevin Coffman Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsctl.c | 19 ++++++------------- include/linux/sunrpc/gss_krb5_enctypes.h | 4 ++++ net/sunrpc/auth_gss/gss_krb5_mech.c | 3 ++- 3 files changed, 12 insertions(+), 14 deletions(-) create mode 100644 include/linux/sunrpc/gss_krb5_enctypes.h (limited to 'include') diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 1f5eae40f34e..2b1449dd2f49 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "idmap.h" #include "nfsd.h" @@ -189,18 +190,10 @@ static struct file_operations export_features_operations = { .release = single_release, }; -#ifdef CONFIG_SUNRPC_GSS +#if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE) static int supported_enctypes_show(struct seq_file *m, void *v) { - struct gss_api_mech *k5mech; - - k5mech = gss_mech_get_by_name("krb5"); - if (k5mech == NULL) - goto out; - if (k5mech->gm_upcall_enctypes != NULL) - seq_printf(m, k5mech->gm_upcall_enctypes); - gss_mech_put(k5mech); -out: + seq_printf(m, KRB5_SUPPORTED_ENCTYPES); return 0; } @@ -215,7 +208,7 @@ static struct file_operations supported_enctypes_ops = { .llseek = seq_lseek, .release = single_release, }; -#endif /* CONFIG_SUNRPC_GSS */ +#endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */ extern int nfsd_pool_stats_open(struct inode *inode, struct file *file); extern int nfsd_pool_stats_release(struct inode *inode, struct file *file); @@ -1427,9 +1420,9 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, -#ifdef CONFIG_SUNRPC_GSS +#if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE) [NFSD_SupportedEnctypes] = {"supported_krb5_enctypes", &supported_enctypes_ops, S_IRUGO}, -#endif /* CONFIG_SUNRPC_GSS */ +#endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */ #ifdef CONFIG_NFSD_V4 [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Gracetime] = {"nfsv4gracetime", &transaction_ops, S_IWUSR|S_IRUSR}, diff --git a/include/linux/sunrpc/gss_krb5_enctypes.h b/include/linux/sunrpc/gss_krb5_enctypes.h new file mode 100644 index 000000000000..ec6234eee89c --- /dev/null +++ b/include/linux/sunrpc/gss_krb5_enctypes.h @@ -0,0 +1,4 @@ +/* + * Dumb way to share this static piece of information with nfsd + */ +#define KRB5_SUPPORTED_ENCTYPES "18,17,16,23,3,1,2" diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index 0a9a2ec2e469..c3b75333b821 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -43,6 +43,7 @@ #include #include #include +#include #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_AUTH @@ -750,7 +751,7 @@ static struct gss_api_mech gss_kerberos_mech = { .gm_ops = &gss_kerberos_ops, .gm_pf_num = ARRAY_SIZE(gss_kerberos_pfs), .gm_pfs = gss_kerberos_pfs, - .gm_upcall_enctypes = "18,17,16,23,3,1,2", + .gm_upcall_enctypes = KRB5_SUPPORTED_ENCTYPES, }; static int __init init_kerberos_module(void) -- cgit v1.2.3 From 5f98ecdbcef1920323d8777c0ba55dbd4335d3cf Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sun, 5 Jun 2011 11:47:29 +0900 Subject: swiotlb: Export swioltb_nr_tbl and utilize it as appropiate. By default the io_tlb_nslabs is set to zero, and gets set to whatever value is passed in via swiotlb_init_with_tbl function. The default value passed in is 64MB. However, if the user provides the 'swiotlb=' the default value is ignored and the value provided by the user is used... Except when the SWIOTLB is used under Xen - there the default value of 64MB is used and the Xen-SWIOTLB has no mechanism to get the 'io_tlb_nslabs' filled out by setup_io_tlb_npages functions. This patch provides a function for the Xen-SWIOTLB to call to see if the io_tlb_nslabs is set and if so use that value. Signed-off-by: FUJITA Tomonori Signed-off-by: Konrad Rzeszutek Wilk --- drivers/xen/swiotlb-xen.c | 12 +++++++++--- include/linux/swiotlb.h | 1 + lib/swiotlb.c | 5 +++++ 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 54469c3eeacd..5ac3f4843f5c 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -147,9 +147,15 @@ void __init xen_swiotlb_init(int verbose) { unsigned long bytes; int rc; - - xen_io_tlb_nslabs = (64 * 1024 * 1024 >> IO_TLB_SHIFT); - xen_io_tlb_nslabs = ALIGN(xen_io_tlb_nslabs, IO_TLB_SEGSIZE); + unsigned long nr_tbl; + + nr_tbl = swioltb_nr_tbl(); + if (nr_tbl) + xen_io_tlb_nslabs = nr_tbl; + else { + xen_io_tlb_nslabs = (64 * 1024 * 1024 >> IO_TLB_SHIFT); + xen_io_tlb_nslabs = ALIGN(xen_io_tlb_nslabs, IO_TLB_SEGSIZE); + } bytes = xen_io_tlb_nslabs << IO_TLB_SHIFT; diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h index 8c0e349f4a6c..445702c60d04 100644 --- a/include/linux/swiotlb.h +++ b/include/linux/swiotlb.h @@ -24,6 +24,7 @@ extern int swiotlb_force; extern void swiotlb_init(int verbose); extern void swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose); +extern unsigned long swioltb_nr_tbl(void); /* * Enumeration for sync targets diff --git a/lib/swiotlb.c b/lib/swiotlb.c index 93ca08b8a451..99093b396145 100644 --- a/lib/swiotlb.c +++ b/lib/swiotlb.c @@ -110,6 +110,11 @@ setup_io_tlb_npages(char *str) __setup("swiotlb=", setup_io_tlb_npages); /* make io_tlb_overflow tunable too? */ +unsigned long swioltb_nr_tbl(void) +{ + return io_tlb_nslabs; +} + /* Note that this doesn't work with highmem page */ static dma_addr_t swiotlb_virt_to_bus(struct device *hwdev, volatile void *address) -- cgit v1.2.3 From f7a2d73fe75c71941fb0a6b4d8fe7da8144f2c7b Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 6 Jun 2011 15:36:24 -0400 Subject: x86, efi: Fix argument types for SetVariable() The spec says this takes uint32 for attributes, not uintn. Signed-off-by: Matthew Garrett Link: http://lkml.kernel.org/r/1307388985-7852-1-git-send-email-mjg@redhat.com Signed-off-by: H. Peter Anvin --- arch/x86/platform/efi/efi.c | 2 +- include/linux/efi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 0d3a4fa34560..f4f6de987b7b 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -122,7 +122,7 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, static efi_status_t virt_efi_set_variable(efi_char16_t *name, efi_guid_t *vendor, - unsigned long attr, + u32 attr, unsigned long data_size, void *data) { diff --git a/include/linux/efi.h b/include/linux/efi.h index e376270cd26e..0758753a17a1 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -168,7 +168,7 @@ typedef efi_status_t efi_get_variable_t (efi_char16_t *name, efi_guid_t *vendor, typedef efi_status_t efi_get_next_variable_t (unsigned long *name_size, efi_char16_t *name, efi_guid_t *vendor); typedef efi_status_t efi_set_variable_t (efi_char16_t *name, efi_guid_t *vendor, - unsigned long attr, unsigned long data_size, + u32 attr, unsigned long data_size, void *data); typedef efi_status_t efi_get_next_high_mono_count_t (u32 *count); typedef void efi_reset_system_t (int reset_type, efi_status_t status, -- cgit v1.2.3 From 3b3702377c576f6624348c7c6fd113bfd934fbd7 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 6 Jun 2011 15:36:25 -0400 Subject: x86, efi: Add infrastructure for UEFI 2.0 runtime services We're currently missing support for any of the runtime service calls introduced with the UEFI 2.0 spec in 2006. Add the infrastructure for supporting them. Signed-off-by: Matthew Garrett Link: http://lkml.kernel.org/r/1307388985-7852-2-git-send-email-mjg@redhat.com Signed-off-by: H. Peter Anvin --- arch/x86/platform/efi/efi.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/efi.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index f4f6de987b7b..a0e42447de7c 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -131,6 +131,18 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, data_size, data); } +static efi_status_t virt_efi_query_variable_info(u32 attr, + u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size) +{ + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + return efi_call_virt4(query_variable_info, attr, storage_space, + remaining_space, max_variable_size); +} + static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) { return efi_call_virt1(get_next_high_mono_count, count); @@ -145,6 +157,28 @@ static void virt_efi_reset_system(int reset_type, data_size, data); } +static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, + unsigned long count, + unsigned long sg_list) +{ + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + return efi_call_virt3(update_capsule, capsules, count, sg_list); +} + +static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, + unsigned long count, + u64 *max_size, + int *reset_type) +{ + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + return efi_call_virt4(query_capsule_caps, capsules, count, max_size, + reset_type); +} + static efi_status_t __init phys_efi_set_virtual_address_map( unsigned long memory_map_size, unsigned long descriptor_size, @@ -651,6 +685,9 @@ void __init efi_enter_virtual_mode(void) efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; efi.reset_system = virt_efi_reset_system; efi.set_virtual_address_map = NULL; + efi.query_variable_info = virt_efi_query_variable_info; + efi.update_capsule = virt_efi_update_capsule; + efi.query_capsule_caps = virt_efi_query_capsule_caps; if (__supported_pte_mask & _PAGE_NX) runtime_code_page_mkexec(); early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size); diff --git a/include/linux/efi.h b/include/linux/efi.h index 0758753a17a1..ec2572693925 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -101,6 +101,13 @@ typedef struct { u64 attribute; } efi_memory_desc_t; +typedef struct { + efi_guid_t guid; + u32 headersize; + u32 flags; + u32 imagesize; +} efi_capsule_header_t; + typedef int (*efi_freemem_callback_t) (u64 start, u64 end, void *arg); /* @@ -156,6 +163,9 @@ typedef struct { unsigned long set_variable; unsigned long get_next_high_mono_count; unsigned long reset_system; + unsigned long update_capsule; + unsigned long query_capsule_caps; + unsigned long query_variable_info; } efi_runtime_services_t; typedef efi_status_t efi_get_time_t (efi_time_t *tm, efi_time_cap_t *tc); @@ -177,6 +187,17 @@ typedef efi_status_t efi_set_virtual_address_map_t (unsigned long memory_map_siz unsigned long descriptor_size, u32 descriptor_version, efi_memory_desc_t *virtual_map); +typedef efi_status_t efi_query_variable_info_t(u32 attr, + u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size); +typedef efi_status_t efi_update_capsule_t(efi_capsule_header_t **capsules, + unsigned long count, + unsigned long sg_list); +typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules, + unsigned long count, + u64 *max_size, + int *reset_type); /* * EFI Configuration Table and GUID definitions @@ -218,6 +239,13 @@ typedef struct { #define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL) +#define EFI_2_30_SYSTEM_TABLE_REVISION ((2 << 16) | (30)) +#define EFI_2_20_SYSTEM_TABLE_REVISION ((2 << 16) | (20)) +#define EFI_2_10_SYSTEM_TABLE_REVISION ((2 << 16) | (10)) +#define EFI_2_00_SYSTEM_TABLE_REVISION ((2 << 16) | (00)) +#define EFI_1_10_SYSTEM_TABLE_REVISION ((1 << 16) | (10)) +#define EFI_1_02_SYSTEM_TABLE_REVISION ((1 << 16) | (02)) + typedef struct { efi_table_hdr_t hdr; unsigned long fw_vendor; /* physical addr of CHAR16 vendor string */ @@ -250,6 +278,7 @@ struct efi_memory_map { */ extern struct efi { efi_system_table_t *systab; /* EFI system table */ + unsigned int runtime_version; /* Runtime services version */ unsigned long mps; /* MPS table */ unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ unsigned long acpi20; /* ACPI table (ACPI 2.0) */ @@ -266,6 +295,9 @@ extern struct efi { efi_get_variable_t *get_variable; efi_get_next_variable_t *get_next_variable; efi_set_variable_t *set_variable; + efi_query_variable_info_t *query_variable_info; + efi_update_capsule_t *update_capsule; + efi_query_capsule_caps_t *query_capsule_caps; efi_get_next_high_mono_count_t *get_next_high_mono_count; efi_reset_system_t *reset_system; efi_set_virtual_address_map_t *set_virtual_address_map; -- cgit v1.2.3 From 3019de124b9f5b1526cb3668b74af14371e21795 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 6 Jun 2011 16:41:33 -0700 Subject: net: Rework netdev_drivername() to avoid warning. This interface uses a temporary buffer, but for no real reason. And now can generate warnings like: net/sched/sch_generic.c: In function dev_watchdog net/sched/sch_generic.c:254:10: warning: unused variable drivername Just return driver->name directly or "". Reported-by: Connor Hansen Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- net/core/dev.c | 16 +++++----------- net/sched/sch_generic.c | 3 +-- 3 files changed, 7 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ca333e79e10f..54b8b4d7b68f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2555,7 +2555,7 @@ extern void netdev_class_remove_file(struct class_attribute *class_attr); extern struct kobj_ns_type_operations net_ns_type_operations; -extern char *netdev_drivername(const struct net_device *dev, char *buffer, int len); +extern const char *netdev_drivername(const struct net_device *dev); extern void linkwatch_run_queue(void); diff --git a/net/core/dev.c b/net/core/dev.c index 939307891e71..1af6cb27f67a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6264,29 +6264,23 @@ err_name: /** * netdev_drivername - network driver for the device * @dev: network device - * @buffer: buffer for resulting name - * @len: size of buffer * * Determine network driver for device. */ -char *netdev_drivername(const struct net_device *dev, char *buffer, int len) +const char *netdev_drivername(const struct net_device *dev) { const struct device_driver *driver; const struct device *parent; - - if (len <= 0 || !buffer) - return buffer; - buffer[0] = 0; + const char *empty = ""; parent = dev->dev.parent; - if (!parent) - return buffer; + return empty; driver = parent->driver; if (driver && driver->name) - strlcpy(buffer, driver->name, len); - return buffer; + return driver->name; + return empty; } static int __netdev_printk(const char *level, const struct net_device *dev, diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index b1721d71c27c..b4c680900d7a 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -251,9 +251,8 @@ static void dev_watchdog(unsigned long arg) } if (some_queue_timedout) { - char drivername[64]; WARN_ONCE(1, KERN_INFO "NETDEV WATCHDOG: %s (%s): transmit queue %u timed out\n", - dev->name, netdev_drivername(dev, drivername, 64), i); + dev->name, netdev_drivername(dev), i); dev->netdev_ops->ndo_tx_timeout(dev); } if (!mod_timer(&dev->watchdog_timer, -- cgit v1.2.3 From 13fcb7bd322164c67926ffe272846d4860196dc6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 6 Jun 2011 22:42:06 -0700 Subject: af_packet: prevent information leak In 2.6.27, commit 393e52e33c6c2 (packet: deliver VLAN TCI to userspace) added a small information leak. Add padding field and make sure its zeroed before copy to user. Signed-off-by: Eric Dumazet CC: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_packet.h | 2 ++ net/packet/af_packet.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h index 6d66ce1791a9..7b318630139f 100644 --- a/include/linux/if_packet.h +++ b/include/linux/if_packet.h @@ -62,6 +62,7 @@ struct tpacket_auxdata { __u16 tp_mac; __u16 tp_net; __u16 tp_vlan_tci; + __u16 tp_padding; }; /* Rx ring - header status */ @@ -101,6 +102,7 @@ struct tpacket2_hdr { __u32 tp_sec; __u32 tp_nsec; __u16 tp_vlan_tci; + __u16 tp_padding; }; #define TPACKET2_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket2_hdr)) + sizeof(struct sockaddr_ll)) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index ba248d93399a..c0c3cda19712 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -804,6 +804,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, } else { h.h2->tp_vlan_tci = 0; } + h.h2->tp_padding = 0; hdrlen = sizeof(*h.h2); break; default: @@ -1736,6 +1737,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, } else { aux.tp_vlan_tci = 0; } + aux.tp_padding = 0; put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux); } -- cgit v1.2.3 From a6b7a407865aab9f849dd99a71072b7cd1175116 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Mon, 6 Jun 2011 10:43:46 +0000 Subject: net: remove interrupt.h inclusion from netdevice.h * remove interrupt.g inclusion from netdevice.h -- not needed * fixup fallout, add interrupt.h and hardirq.h back where needed. Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- drivers/atm/eni.h | 1 + drivers/atm/firestream.c | 1 + drivers/atm/horizon.c | 1 + drivers/atm/idt77252.c | 1 + drivers/atm/iphase.c | 1 + drivers/atm/zatm.c | 1 + drivers/infiniband/hw/amso1100/c2.c | 1 + drivers/infiniband/ulp/iser/iscsi_iser.h | 1 + drivers/isdn/hardware/mISDN/avmfritz.c | 1 + drivers/isdn/hardware/mISDN/hfcmulti.c | 1 + drivers/isdn/hardware/mISDN/hfcpci.c | 1 + drivers/isdn/hardware/mISDN/mISDNinfineon.c | 1 + drivers/isdn/hardware/mISDN/mISDNipac.c | 1 + drivers/isdn/hardware/mISDN/netjet.c | 1 + drivers/isdn/hardware/mISDN/speedfax.c | 1 + drivers/isdn/hardware/mISDN/w6692.c | 1 + drivers/isdn/hisax/hisax.h | 1 + drivers/isdn/hisax/hisax_fcpcipnp.c | 1 + drivers/media/dvb/b2c2/flexcop-common.h | 1 + drivers/media/dvb/dm1105/dm1105.c | 1 + drivers/media/dvb/mantis/mantis_common.h | 1 + drivers/media/dvb/pluto2/pluto2.c | 1 + drivers/net/3c503.c | 1 + drivers/net/8139cp.c | 1 + drivers/net/8139too.c | 1 + drivers/net/8390.h | 1 + drivers/net/ac3200.c | 1 + drivers/net/acenic.h | 1 + drivers/net/amd8111e.c | 1 + drivers/net/arcnet/arc-rimi.c | 1 + drivers/net/arcnet/com20020-isa.c | 1 + drivers/net/arcnet/com20020-pci.c | 1 + drivers/net/arcnet/com20020.c | 1 + drivers/net/arcnet/com90io.c | 1 + drivers/net/arcnet/com90xx.c | 1 + drivers/net/arm/at91_ether.c | 1 + drivers/net/arm/ep93xx_eth.c | 1 + drivers/net/arm/ks8695net.c | 1 + drivers/net/atl1c/atl1c.h | 1 + drivers/net/atl1e/atl1e.h | 1 + drivers/net/b44.c | 1 + drivers/net/bmac.c | 1 + drivers/net/bnx2x/bnx2x_cmn.c | 1 + drivers/net/caif/caif_serial.c | 1 + drivers/net/can/sja1000/sja1000.h | 1 + drivers/net/cassini.c | 1 + drivers/net/cxgb4vf/adapter.h | 1 + drivers/net/dm9000.c | 1 + drivers/net/dnet.c | 1 + drivers/net/e100.c | 2 ++ drivers/net/e1000e/ethtool.c | 1 + drivers/net/e1000e/netdev.c | 1 + drivers/net/e2100.c | 1 + drivers/net/enic/enic_main.c | 1 + drivers/net/es3210.c | 1 + drivers/net/ethoc.c | 1 + drivers/net/fec_mpc52xx.c | 1 + drivers/net/greth.c | 1 + drivers/net/hamradio/baycom_ser_fdx.c | 1 + drivers/net/hamradio/baycom_ser_hdx.c | 1 + drivers/net/hp-plus.c | 1 + drivers/net/hp.c | 1 + drivers/net/ibmveth.c | 1 + drivers/net/ifb.c | 1 + drivers/net/ipg.c | 1 + drivers/net/irda/ali-ircc.c | 1 + drivers/net/irda/donauboe.c | 1 + drivers/net/irda/nsc-ircc.c | 1 + drivers/net/irda/pxaficp_ir.c | 1 + drivers/net/irda/sir_dev.c | 1 + drivers/net/irda/smsc-ircc2.c | 1 + drivers/net/irda/via-ircc.c | 1 + drivers/net/irda/vlsi_ir.c | 1 + drivers/net/irda/w83977af_ir.c | 1 + drivers/net/ixgbe/ixgbe_ethtool.c | 1 + drivers/net/ixgbe/ixgbe_main.c | 1 + drivers/net/ixp2000/ixpdev.c | 1 + drivers/net/jme.h | 1 + drivers/net/ks8842.c | 1 + drivers/net/ks8851.c | 1 + drivers/net/ks8851_mll.c | 1 + drivers/net/ksz884x.c | 1 + drivers/net/lne390.c | 1 + drivers/net/macb.c | 1 + drivers/net/mace.c | 1 + drivers/net/netx-eth.c | 1 + drivers/net/niu.c | 1 + drivers/net/ns83820.c | 1 + drivers/net/ppp_async.c | 1 + drivers/net/ppp_synctty.c | 1 + drivers/net/ps3_gelic_net.c | 1 + drivers/net/qlge/qlge.h | 1 + drivers/net/r8169.c | 1 + drivers/net/sc92031.c | 1 + drivers/net/sfc/nic.c | 1 + drivers/net/sis190.c | 1 + drivers/net/skge.h | 1 + drivers/net/sky2.c | 1 + drivers/net/smc-mca.c | 1 + drivers/net/smc-ultra.c | 1 + drivers/net/smsc911x.c | 1 + drivers/net/smsc9420.c | 1 + drivers/net/spider_net.c | 1 + drivers/net/starfire.c | 1 + drivers/net/stmmac/stmmac_ethtool.c | 1 + drivers/net/tg3.c | 1 + drivers/net/tlan.c | 2 ++ drivers/net/tokenring/ibmtr.c | 1 + drivers/net/tsi108_eth.c | 1 + drivers/net/tulip/de2104x.c | 1 + drivers/net/tulip/pnic.c | 1 + drivers/net/tulip/tulip_core.c | 1 + drivers/net/vxge/vxge-config.h | 1 + drivers/net/vxge/vxge-main.c | 1 + drivers/net/wan/cycx_main.c | 1 + drivers/net/wan/dscc4.c | 1 + drivers/net/wan/farsync.c | 1 + drivers/net/wan/wanxl.c | 1 + drivers/net/wireless/adm8211.c | 1 + drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/atmel.c | 1 + drivers/net/wireless/hostap/hostap_wlan.h | 1 + drivers/net/wireless/ipw2x00/ipw2200.h | 1 + drivers/net/wireless/ipw2x00/libipw_wx.c | 1 + drivers/net/wireless/iwlegacy/iwl-dev.h | 1 + drivers/net/wireless/iwlwifi/iwl-dev.h | 1 + drivers/net/wireless/libertas/cfg.c | 1 + drivers/net/wireless/libertas/cmd.c | 1 + drivers/net/wireless/libertas/cmdresp.c | 1 + drivers/net/wireless/libertas/debugfs.c | 1 + drivers/net/wireless/libertas/ethtool.c | 1 + drivers/net/wireless/libertas/if_spi.c | 2 ++ drivers/net/wireless/libertas/main.c | 1 + drivers/net/wireless/libertas/mesh.c | 1 + drivers/net/wireless/libertas/rx.c | 1 + drivers/net/wireless/libertas/tx.c | 1 + drivers/net/wireless/libertas_tf/cmd.c | 1 + drivers/net/wireless/libertas_tf/main.c | 1 + drivers/net/wireless/mwl8k.c | 1 + drivers/net/wireless/p54/p54pci.h | 1 + drivers/net/wireless/prism54/islpci_dev.c | 1 + drivers/net/wireless/prism54/islpci_dev.h | 1 + drivers/net/wireless/prism54/islpci_hotplug.c | 1 + drivers/net/wireless/rt2x00/rt2x00.h | 1 + drivers/net/wireless/rtl818x/rtl8180/dev.c | 1 + drivers/net/wireless/wl1251/sdio.c | 1 + drivers/net/wireless/wl1251/spi.c | 1 + drivers/net/wireless/wl12xx/io.h | 1 + drivers/net/wireless/wl12xx/spi.c | 1 + drivers/s390/net/ctcm_mpc.h | 1 + include/linux/arcdevice.h | 1 + include/linux/dccp.h | 1 + include/linux/netdevice.h | 1 - include/net/bluetooth/hci_core.h | 1 + include/net/sock.h | 1 + net/atm/pppoatm.c | 1 + net/caif/chnl_net.c | 1 + net/can/bcm.c | 1 + net/rds/ib.h | 1 + net/rds/iw.h | 1 + net/sched/sch_atm.c | 1 + net/sunrpc/xprtrdma/svc_rdma_transport.c | 1 + net/sunrpc/xprtrdma/verbs.c | 1 + 163 files changed, 165 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/atm/eni.h b/drivers/atm/eni.h index e4c9525e60b3..493a6932507e 100644 --- a/drivers/atm/eni.h +++ b/drivers/atm/eni.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/drivers/atm/firestream.c b/drivers/atm/firestream.c index ef7a658312a6..7c7b571647f9 100644 --- a/drivers/atm/firestream.c +++ b/drivers/atm/firestream.c @@ -44,6 +44,7 @@ #include /* for request_region */ #include #include +#include #include #include #include diff --git a/drivers/atm/horizon.c b/drivers/atm/horizon.c index d58e3fcb9db3..287506183893 100644 --- a/drivers/atm/horizon.c +++ b/drivers/atm/horizon.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c index 1f8d724a18bf..be0dbfeb541c 100644 --- a/drivers/atm/idt77252.c +++ b/drivers/atm/idt77252.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index dee4f01a64d8..957106f636ea 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c index 624917902b65..7f8c5132ff32 100644 --- a/drivers/atm/zatm.c +++ b/drivers/atm/zatm.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/infiniband/hw/amso1100/c2.c b/drivers/infiniband/hw/amso1100/c2.c index 0cfc455630d0..444470a28de2 100644 --- a/drivers/infiniband/hw/amso1100/c2.c +++ b/drivers/infiniband/hw/amso1100/c2.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h index 2f02ab0ccc1e..342cbc1bdaae 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.h +++ b/drivers/infiniband/ulp/iser/iscsi_iser.h @@ -45,6 +45,7 @@ #include #include +#include #include #include #include diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c index 472a2af79446..861b6511f3ee 100644 --- a/drivers/isdn/hardware/mISDN/avmfritz.c +++ b/drivers/isdn/hardware/mISDN/avmfritz.c @@ -20,6 +20,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ +#include #include #include #include diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index f6f3c87cc7c2..a440d7fff0ad 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -152,6 +152,7 @@ #define HFC_MULTI_VERSION "2.03" +#include #include #include #include diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index b01a7be1300f..3261de18a91e 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -44,6 +44,7 @@ * */ +#include #include #include #include diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c index bc0529ac88a1..6218775ce87d 100644 --- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c +++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c @@ -38,6 +38,7 @@ * */ +#include #include #include #include diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c index 64ecc6f5ffaf..d2ffb1d9b831 100644 --- a/drivers/isdn/hardware/mISDN/mISDNipac.c +++ b/drivers/isdn/hardware/mISDN/mISDNipac.c @@ -20,6 +20,7 @@ * */ +#include #include #include #include diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c index db25b6b2ae39..5ef9f11ee74b 100644 --- a/drivers/isdn/hardware/mISDN/netjet.c +++ b/drivers/isdn/hardware/mISDN/netjet.c @@ -20,6 +20,7 @@ * */ +#include #include #include #include diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c index 9e07246bb9e7..4d0d41ea1228 100644 --- a/drivers/isdn/hardware/mISDN/speedfax.c +++ b/drivers/isdn/hardware/mISDN/speedfax.c @@ -22,6 +22,7 @@ * */ +#include #include #include #include diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c index 9e84870b971c..e10e0284533c 100644 --- a/drivers/isdn/hardware/mISDN/w6692.c +++ b/drivers/isdn/hardware/mISDN/w6692.c @@ -21,6 +21,7 @@ * */ +#include #include #include #include diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index de1c669c7b13..0a5c42a3f125 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c index 8b0a7d86b30f..478ebab54ca4 100644 --- a/drivers/isdn/hisax/hisax_fcpcipnp.c +++ b/drivers/isdn/hisax/hisax_fcpcipnp.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include diff --git a/drivers/media/dvb/b2c2/flexcop-common.h b/drivers/media/dvb/b2c2/flexcop-common.h index 9e2148a19967..437912e49824 100644 --- a/drivers/media/dvb/b2c2/flexcop-common.h +++ b/drivers/media/dvb/b2c2/flexcop-common.h @@ -6,6 +6,7 @@ #ifndef __FLEXCOP_COMMON_H__ #define __FLEXCOP_COMMON_H__ +#include #include #include diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index b2b0c45f32a9..55e6533f15e9 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/media/dvb/mantis/mantis_common.h b/drivers/media/dvb/mantis/mantis_common.h index bd400d21b81f..49dbca145bb8 100644 --- a/drivers/media/dvb/mantis/mantis_common.h +++ b/drivers/media/dvb/mantis/mantis_common.h @@ -21,6 +21,7 @@ #ifndef __MANTIS_COMMON_H #define __MANTIS_COMMON_H +#include #include #include diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c index 7cb79ec685f0..80fb51004461 100644 --- a/drivers/media/dvb/pluto2/pluto2.c +++ b/drivers/media/dvb/pluto2/pluto2.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/3c503.c b/drivers/net/3c503.c index d84f6e8903a5..554a26c55188 100644 --- a/drivers/net/3c503.c +++ b/drivers/net/3c503.c @@ -49,6 +49,7 @@ static const char version[] = #include #include #include +#include #include #include diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 10c45051caea..73b10b07f9b5 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 98517a373473..ed6355cc5261 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -100,6 +100,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/8390.h b/drivers/net/8390.h index 3d9e8fb4fbee..58a12e4c78f9 100644 --- a/drivers/net/8390.h +++ b/drivers/net/8390.h @@ -9,6 +9,7 @@ #include #include +#include #include #define TX_PAGES 12 /* Two Tx slots */ diff --git a/drivers/net/ac3200.c b/drivers/net/ac3200.c index 5181e9322119..f07b2e980fbc 100644 --- a/drivers/net/ac3200.c +++ b/drivers/net/ac3200.c @@ -32,6 +32,7 @@ static const char version[] = #include #include #include +#include #include #include diff --git a/drivers/net/acenic.h b/drivers/net/acenic.h index 0681da7e8753..fd25a3b47fef 100644 --- a/drivers/net/acenic.h +++ b/drivers/net/acenic.h @@ -1,5 +1,6 @@ #ifndef _ACENIC_H_ #define _ACENIC_H_ +#include /* diff --git a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c index 81475ea3a711..db6d2da5a8f3 100644 --- a/drivers/net/amd8111e.c +++ b/drivers/net/amd8111e.c @@ -75,6 +75,7 @@ Revision History: #include #include #include +#include #include #include #include diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c index 9efbbbae47ca..25197b698dd6 100644 --- a/drivers/net/arcnet/arc-rimi.c +++ b/drivers/net/arcnet/arc-rimi.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c index 37272827ee55..45c61a2c5fbd 100644 --- a/drivers/net/arcnet/com20020-isa.c +++ b/drivers/net/arcnet/com20020-isa.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c index 48a1dbf01e60..d427493997b6 100644 --- a/drivers/net/arcnet/com20020-pci.c +++ b/drivers/net/arcnet/com20020-pci.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c index c9e459400ff9..7bfb91f32857 100644 --- a/drivers/net/arcnet/com20020.c +++ b/drivers/net/arcnet/com20020.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c index eb27976dab37..487d780ebbdf 100644 --- a/drivers/net/arcnet/com90io.c +++ b/drivers/net/arcnet/com90io.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c index f3b46f71e293..b80fbe40aa0e 100644 --- a/drivers/net/arcnet/com90xx.c +++ b/drivers/net/arcnet/com90xx.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c index e07b314ed8fd..29dc43523cec 100644 --- a/drivers/net/arm/at91_ether.c +++ b/drivers/net/arm/at91_ether.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c index 5a77001b6d10..a167addd5382 100644 --- a/drivers/net/arm/ep93xx_eth.c +++ b/drivers/net/arm/ep93xx_eth.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/arm/ks8695net.c b/drivers/net/arm/ks8695net.c index a7b0caa18179..bb62b3f51837 100644 --- a/drivers/net/arm/ks8695net.c +++ b/drivers/net/arm/ks8695net.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h index 925929d764ca..dfe4370ccc90 100644 --- a/drivers/net/atl1c/atl1c.h +++ b/drivers/net/atl1c/atl1c.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/atl1e/atl1e.h b/drivers/net/atl1e/atl1e.h index 490d3b38e0cb..9ac37e3ec8fb 100644 --- a/drivers/net/atl1e/atl1e.h +++ b/drivers/net/atl1e/atl1e.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/b44.c b/drivers/net/b44.c index a69331e06b8d..085560e1d17a 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/bmac.c b/drivers/net/bmac.c index a1b8c8b8010b..d2e58e2bea78 100644 --- a/drivers/net/bmac.c +++ b/drivers/net/bmac.c @@ -7,6 +7,7 @@ * May 1999, Al Viro: proper release of /proc/net/bmac entry, switched to * dynamic procfs inode. */ +#include #include #include #include diff --git a/drivers/net/bnx2x/bnx2x_cmn.c b/drivers/net/bnx2x/bnx2x_cmn.c index 9b248348c0e6..831c6ec98eed 100644 --- a/drivers/net/bnx2x/bnx2x_cmn.c +++ b/drivers/net/bnx2x/bnx2x_cmn.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index 3df0c0f8b8bf..1cd0b59d13cb 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -4,6 +4,7 @@ * License terms: GNU General Public License (GPL) version 2 */ +#include #include #include #include diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h index de8e778f6832..78bd4ecac140 100644 --- a/drivers/net/can/sja1000/sja1000.h +++ b/drivers/net/can/sja1000/sja1000.h @@ -47,6 +47,7 @@ #ifndef SJA1000_DEV_H #define SJA1000_DEV_H +#include #include #include diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 22ce03e55b83..b414f5ae0da5 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -75,6 +75,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/cxgb4vf/adapter.h b/drivers/net/cxgb4vf/adapter.h index 4fd821aadc8a..6e9a8d9ef592 100644 --- a/drivers/net/cxgb4vf/adapter.h +++ b/drivers/net/cxgb4vf/adapter.h @@ -40,6 +40,7 @@ #ifndef __CXGB4VF_ADAPTER_H__ #define __CXGB4VF_ADAPTER_H__ +#include #include #include #include diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c index ee597e676ee5..863e9c459e65 100644 --- a/drivers/net/dm9000.c +++ b/drivers/net/dm9000.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/dnet.c b/drivers/net/dnet.c index 8318ea06cb6d..b22691046678 100644 --- a/drivers/net/dnet.c +++ b/drivers/net/dnet.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/e100.c b/drivers/net/e100.c index e336c7937f05..c1352c60c299 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -149,6 +149,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include #include #include #include diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c index 859d0d3af6c9..cb1a3623253e 100644 --- a/drivers/net/e1000e/ethtool.c +++ b/drivers/net/e1000e/ethtool.c @@ -28,6 +28,7 @@ /* ethtool support for e1000 */ +#include #include #include #include diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 3310c3d477d7..888bd9cc2710 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/e2100.c b/drivers/net/e2100.c index 94ec973b2bdc..d50a9998ae77 100644 --- a/drivers/net/e2100.c +++ b/drivers/net/e2100.c @@ -44,6 +44,7 @@ static const char version[] = #include #include #include +#include #include #include diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c index 1daf79b36165..9600f435c0fb 100644 --- a/drivers/net/enic/enic_main.c +++ b/drivers/net/enic/enic_main.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/es3210.c b/drivers/net/es3210.c index 0ba5e7b90584..7a09575ecff0 100644 --- a/drivers/net/es3210.c +++ b/drivers/net/es3210.c @@ -54,6 +54,7 @@ static const char version[] = #include #include #include +#include #include #include diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index a83dd312c3ac..9162c705c4fa 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c index 9f81b1ac130e..eba0f696a51f 100644 --- a/drivers/net/fec_mpc52xx.c +++ b/drivers/net/fec_mpc52xx.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/greth.c b/drivers/net/greth.c index f181304a7ab6..69b86d7fac85 100644 --- a/drivers/net/greth.c +++ b/drivers/net/greth.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index 99cdce33df8b..a974727dd9a2 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -76,6 +76,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c index d92fe6ca788f..e349d867449b 100644 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ b/drivers/net/hamradio/baycom_ser_hdx.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c index 82bffc3cabdf..29917363ebfb 100644 --- a/drivers/net/hp-plus.c +++ b/drivers/net/hp-plus.c @@ -30,6 +30,7 @@ static const char version[] = #include #include #include +#include #include #include diff --git a/drivers/net/hp.c b/drivers/net/hp.c index ef2014375e62..18564d4a7c04 100644 --- a/drivers/net/hp.c +++ b/drivers/net/hp.c @@ -30,6 +30,7 @@ static const char version[] = #include #include #include +#include #include #include diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index b388d782c7c4..838c5b673767 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index 4fecaed67fc4..ce53f4a23b19 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c index 58cd3202b48c..d4aa40adf1e9 100644 --- a/drivers/net/ipg.c +++ b/drivers/net/ipg.c @@ -22,6 +22,7 @@ */ #include #include +#include #include #include #include diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index d532dde5120f..963067d3bda2 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c index 174cafad2c1a..b45b2cc42804 100644 --- a/drivers/net/irda/donauboe.c +++ b/drivers/net/irda/donauboe.c @@ -152,6 +152,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index 7a963d4e6d06..b56636da6cc3 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c index 001ed0a255f6..b1d1ce3dd8b5 100644 --- a/drivers/net/irda/pxaficp_ir.c +++ b/drivers/net/irda/pxaficp_ir.c @@ -12,6 +12,7 @@ * Infra-red driver (SIR/FIR) for the PXA2xx embedded microprocessor * */ +#include #include #include #include diff --git a/drivers/net/irda/sir_dev.c b/drivers/net/irda/sir_dev.c index efe05bb34dd8..5039f08f5a5b 100644 --- a/drivers/net/irda/sir_dev.c +++ b/drivers/net/irda/sir_dev.c @@ -11,6 +11,7 @@ * ********************************************************************/ +#include #include #include #include diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c index 8800e1fe4129..d5072af81b2d 100644 --- a/drivers/net/irda/smsc-ircc2.c +++ b/drivers/net/irda/smsc-ircc2.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c index f504b262ba36..6d6479049aa1 100644 --- a/drivers/net/irda/via-ircc.c +++ b/drivers/net/irda/via-ircc.c @@ -46,6 +46,7 @@ F02 Oct/28/02: Add SB device ID for 3147 and 3177. #include #include #include +#include #include #include #include diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c index c3d07382b7fa..9021d0131727 100644 --- a/drivers/net/irda/vlsi_ir.c +++ b/drivers/net/irda/vlsi_ir.c @@ -36,6 +36,7 @@ MODULE_LICENSE("GPL"); #include #include +#include #include #include #include diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 1f9c3f08d1a3..c4366601b067 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index cb1555bc8548..4950d03d3ef8 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -27,6 +27,7 @@ /* ethtool support for ixgbe */ +#include #include #include #include diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 08e8e25c159d..06cfaf31bcf3 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c index 78ddd8b79e7e..e122493ab70e 100644 --- a/drivers/net/ixp2000/ixpdev.c +++ b/drivers/net/ixp2000/ixpdev.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/jme.h b/drivers/net/jme.h index e9aaeca96abc..0d5da06489d8 100644 --- a/drivers/net/jme.h +++ b/drivers/net/jme.h @@ -24,6 +24,7 @@ #ifndef __JME_H_INCLUDED__ #define __JME_H_INCLUDED__ +#include #define DRV_NAME "jme" #define DRV_VERSION "1.0.8" diff --git a/drivers/net/ks8842.c b/drivers/net/ks8842.c index fc12ac0d9f2e..4a6ae057e3b1 100644 --- a/drivers/net/ks8842.c +++ b/drivers/net/ks8842.c @@ -23,6 +23,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include diff --git a/drivers/net/ks8851.c b/drivers/net/ks8851.c index bcd9ba68c9f2..f56743a28fc0 100644 --- a/drivers/net/ks8851.c +++ b/drivers/net/ks8851.c @@ -13,6 +13,7 @@ #define DEBUG +#include #include #include #include diff --git a/drivers/net/ks8851_mll.c b/drivers/net/ks8851_mll.c index 61631cace913..aefbdd896d6a 100644 --- a/drivers/net/ks8851_mll.c +++ b/drivers/net/ks8851_mll.c @@ -23,6 +23,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include diff --git a/drivers/net/ksz884x.c b/drivers/net/ksz884x.c index 41ea5920c158..2ac6c6c984b3 100644 --- a/drivers/net/ksz884x.c +++ b/drivers/net/ksz884x.c @@ -17,6 +17,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include diff --git a/drivers/net/lne390.c b/drivers/net/lne390.c index 8a1097cf8a83..f9888d20177b 100644 --- a/drivers/net/lne390.c +++ b/drivers/net/lne390.c @@ -41,6 +41,7 @@ static const char *version = #include #include #include +#include #include #include diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 6c6a02869dfc..818f0b8c2772 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/mace.c b/drivers/net/mace.c index 1c5221f79d6f..2074e9724ba3 100644 --- a/drivers/net/mace.c +++ b/drivers/net/mace.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/netx-eth.c b/drivers/net/netx-eth.c index 2e4b42175f3f..2dfee892d200 100644 --- a/drivers/net/netx-eth.c +++ b/drivers/net/netx-eth.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include diff --git a/drivers/net/niu.c b/drivers/net/niu.c index cc25bff0bd3b..543eb17acdc5 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c index 3e4040f2f3cb..d3afb458987e 100644 --- a/drivers/net/ns83820.c +++ b/drivers/net/ns83820.c @@ -106,6 +106,7 @@ #include #include #include +#include #include /* for iph */ #include /* for IPPROTO_... */ #include diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index a1b82c9c67d2..6436ba916fe4 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index 2573f525f11c..736a39ee05bb 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c index d47abb4e7eb6..35e47c3cab35 100644 --- a/drivers/net/ps3_gelic_net.c +++ b/drivers/net/ps3_gelic_net.c @@ -28,6 +28,7 @@ #undef DEBUG +#include #include #include #include diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h index d32850715f5c..7d8483f9012e 100644 --- a/drivers/net/qlge/qlge.h +++ b/drivers/net/qlge/qlge.h @@ -7,6 +7,7 @@ #ifndef _QLGE_H_ #define _QLGE_H_ +#include #include #include #include diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index ef1ce2ebeb4a..73108249462e 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c index fa74314ef789..9da47337b7c3 100644 --- a/drivers/net/sc92031.c +++ b/drivers/net/sc92031.c @@ -22,6 +22,7 @@ * matching, so you need to enable IFF_PROMISC when using it. */ +#include #include #include #include diff --git a/drivers/net/sfc/nic.c b/drivers/net/sfc/nic.c index f2a2b947f860..bafa23a6874c 100644 --- a/drivers/net/sfc/nic.c +++ b/drivers/net/sfc/nic.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c index b436e007eea0..8ad7bfbaa3af 100644 --- a/drivers/net/sis190.c +++ b/drivers/net/sis190.c @@ -21,6 +21,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include diff --git a/drivers/net/skge.h b/drivers/net/skge.h index 598bf7a1a55e..a2eb34115844 100644 --- a/drivers/net/skge.h +++ b/drivers/net/skge.h @@ -3,6 +3,7 @@ */ #ifndef _SKGE_H #define _SKGE_H +#include /* PCI config registers */ #define PCI_DEV_REG1 0x40 diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 3ee41da130c2..d252cb123a5f 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/smc-mca.c b/drivers/net/smc-mca.c index d07c39cb4daf..34934fb23b97 100644 --- a/drivers/net/smc-mca.c +++ b/drivers/net/smc-mca.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c index 235a3c6c9f91..ba44ede29198 100644 --- a/drivers/net/smc-ultra.c +++ b/drivers/net/smc-ultra.c @@ -62,6 +62,7 @@ static const char version[] = #include #include #include +#include #include #include #include diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index c6d47d10590c..054597625d55 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/smsc9420.c b/drivers/net/smsc9420.c index 4c92ad8be765..5e5e64da5cb9 100644 --- a/drivers/net/smsc9420.c +++ b/drivers/net/smsc9420.c @@ -19,6 +19,7 @@ *************************************************************************** */ +#include #include #include #include diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index 949f124e1278..9bc6c20e8b97 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index 36045f3b0327..860a50815f84 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -30,6 +30,7 @@ #define DRV_VERSION "2.1" #define DRV_RELDATE "July 6, 2008" +#include #include #include #include diff --git a/drivers/net/stmmac/stmmac_ethtool.c b/drivers/net/stmmac/stmmac_ethtool.c index ae5213a8c4cd..720c5a1531bc 100644 --- a/drivers/net/stmmac/stmmac_ethtool.c +++ b/drivers/net/stmmac/stmmac_ethtool.c @@ -24,6 +24,7 @@ #include #include +#include #include #include diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index a1f9f9eef37d..c8a145d72f17 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index ace6404e2fac..145871b3130b 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -29,8 +29,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include +#include #include #include #include diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c index e896ad0e8d24..e257a00fe14b 100644 --- a/drivers/net/tokenring/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -123,6 +123,7 @@ in the event that chatty debug messages are desired - jjs 12/30/98 */ /* some 95 OS send many non UI frame; this allow removing the warning */ #define TR_FILTERNONUI 1 +#include #include #include #include diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c index 5c633a32eaeb..64cb9ac19ed9 100644 --- a/drivers/net/tsi108_eth.c +++ b/drivers/net/tsi108_eth.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c index e2f692351180..ce90efc6ba3c 100644 --- a/drivers/net/tulip/de2104x.c +++ b/drivers/net/tulip/de2104x.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/tulip/pnic.c b/drivers/net/tulip/pnic.c index aa4d9dad0395..52d898bdbeb4 100644 --- a/drivers/net/tulip/pnic.c +++ b/drivers/net/tulip/pnic.c @@ -13,6 +13,7 @@ Please submit bugs to http://bugzilla.kernel.org/ . */ +#include #include #include #include "tulip.h" diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index 82f87647207e..1246998a677c 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -28,6 +28,7 @@ #include #include "tulip.h" #include +#include #include #include #include diff --git a/drivers/net/vxge/vxge-config.h b/drivers/net/vxge/vxge-config.h index 359b9b9f8041..6219006d9d2e 100644 --- a/drivers/net/vxge/vxge-config.h +++ b/drivers/net/vxge/vxge-config.h @@ -13,6 +13,7 @@ ******************************************************************************/ #ifndef VXGE_CONFIG_H #define VXGE_CONFIG_H +#include #include #include diff --git a/drivers/net/vxge/vxge-main.c b/drivers/net/vxge/vxge-main.c index 8ab870a2ad02..e658edd1c959 100644 --- a/drivers/net/vxge/vxge-main.c +++ b/drivers/net/vxge/vxge-main.c @@ -44,6 +44,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include diff --git a/drivers/net/wan/cycx_main.c b/drivers/net/wan/cycx_main.c index 859dba9b972e..a0976d1f97c6 100644 --- a/drivers/net/wan/cycx_main.c +++ b/drivers/net/wan/cycx_main.c @@ -50,6 +50,7 @@ #include /* WAN router definitions */ #include /* cyclomx common user API definitions */ #include /* __init (when not using as a module) */ +#include unsigned int cycx_debug; diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index acb9ea830628..3590d588327c 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -99,6 +99,7 @@ #include #include +#include #include #include diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index e050bd65e037..1eba06f6fa4c 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c index db73a7be199f..4ea89fe0006f 100644 --- a/drivers/net/wan/wanxl.c +++ b/drivers/net/wan/wanxl.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index afe2cbc6cb24..43ebc44fc82c 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index f75068b4b310..e99dfe6065bf 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -19,6 +19,7 @@ #include #include +#include #include #include diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index 39a11e8af4fa..7e45ca2e78ef 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -40,6 +40,7 @@ ******************************************************************************/ #include +#include #include #include diff --git a/drivers/net/wireless/hostap/hostap_wlan.h b/drivers/net/wireless/hostap/hostap_wlan.h index 88dc6a52bdf1..7bb0b4b3f2cb 100644 --- a/drivers/net/wireless/hostap/hostap_wlan.h +++ b/drivers/net/wireless/hostap/hostap_wlan.h @@ -1,6 +1,7 @@ #ifndef HOSTAP_WLAN_H #define HOSTAP_WLAN_H +#include #include #include #include diff --git a/drivers/net/wireless/ipw2x00/ipw2200.h b/drivers/net/wireless/ipw2x00/ipw2200.h index 91795b5a93c5..ecb561d7a7a0 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.h +++ b/drivers/net/wireless/ipw2x00/ipw2200.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/wireless/ipw2x00/libipw_wx.c b/drivers/net/wireless/ipw2x00/libipw_wx.c index d7bd6cf00a81..6623e5052254 100644 --- a/drivers/net/wireless/ipw2x00/libipw_wx.c +++ b/drivers/net/wireless/ipw2x00/libipw_wx.c @@ -30,6 +30,7 @@ ******************************************************************************/ +#include #include #include #include diff --git a/drivers/net/wireless/iwlegacy/iwl-dev.h b/drivers/net/wireless/iwlegacy/iwl-dev.h index be0106c6a2da..416448acd971 100644 --- a/drivers/net/wireless/iwlegacy/iwl-dev.h +++ b/drivers/net/wireless/iwlegacy/iwl-dev.h @@ -32,6 +32,7 @@ #ifndef __iwl_legacy_dev_h__ #define __iwl_legacy_dev_h__ +#include #include /* for struct pci_device_id */ #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 22a6e3ec7094..1f4797d9848d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -31,6 +31,7 @@ #ifndef __iwl_dev_h__ #define __iwl_dev_h__ +#include #include /* for struct pci_device_id */ #include #include diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 5d637af2d7c3..b456a53b64b1 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 71c8f3fccfa1..9dcf967e1ee4 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -3,6 +3,7 @@ * It prepares command and sends it to firmware when it is ready. */ +#include #include #include #include diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c index 207fc361db84..2ffe5a1ff003 100644 --- a/drivers/net/wireless/libertas/cmdresp.c +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -3,6 +3,7 @@ * responses as well as events generated by firmware. */ +#include #include #include #include diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c index 23250f621761..1af182778844 100644 --- a/drivers/net/wireless/libertas/debugfs.c +++ b/drivers/net/wireless/libertas/debugfs.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c index 29dbce4a9f86..4dfb3bfd2cf3 100644 --- a/drivers/net/wireless/libertas/ethtool.c +++ b/drivers/net/wireless/libertas/ethtool.c @@ -1,3 +1,4 @@ +#include #include #include #include diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index 463352c890d7..4fa0be9943f9 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -19,6 +19,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include #include #include #include diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 8c40949cb076..cf3d2c8e1969 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c index 24cf06680c6b..7969d104189d 100644 --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c index fdb0448301a0..bfb8898ae518 100644 --- a/drivers/net/wireless/libertas/rx.c +++ b/drivers/net/wireless/libertas/rx.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c index bbb95f88dc01..f19495b178f6 100644 --- a/drivers/net/wireless/libertas/tx.c +++ b/drivers/net/wireless/libertas/tx.c @@ -1,6 +1,7 @@ /* * This file contains the handling of TX in wlan driver. */ +#include #include #include #include diff --git a/drivers/net/wireless/libertas_tf/cmd.c b/drivers/net/wireless/libertas_tf/cmd.c index 8945afd6ce3e..13557fe0bf95 100644 --- a/drivers/net/wireless/libertas_tf/cmd.c +++ b/drivers/net/wireless/libertas_tf/cmd.c @@ -9,6 +9,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include "libertas_tf.h" diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c index d4005081f1df..5beb58142e97 100644 --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -9,6 +9,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 32261189bcef..8ff43c281411 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include diff --git a/drivers/net/wireless/p54/p54pci.h b/drivers/net/wireless/p54/p54pci.h index ee9bc62a4fa2..7aa509f7e387 100644 --- a/drivers/net/wireless/p54/p54pci.h +++ b/drivers/net/wireless/p54/p54pci.h @@ -1,5 +1,6 @@ #ifndef P54PCI_H #define P54PCI_H +#include /* * Defines for PCI based mac80211 Prism54 driver diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c index ec2c75d77cea..5d0f61508a2e 100644 --- a/drivers/net/wireless/prism54/islpci_dev.c +++ b/drivers/net/wireless/prism54/islpci_dev.c @@ -18,6 +18,7 @@ * */ +#include #include #include diff --git a/drivers/net/wireless/prism54/islpci_dev.h b/drivers/net/wireless/prism54/islpci_dev.h index c4d0f19b7cbc..c40403877f97 100644 --- a/drivers/net/wireless/prism54/islpci_dev.h +++ b/drivers/net/wireless/prism54/islpci_dev.h @@ -22,6 +22,7 @@ #ifndef _ISLPCI_DEV_H #define _ISLPCI_DEV_H +#include #include #include #include diff --git a/drivers/net/wireless/prism54/islpci_hotplug.c b/drivers/net/wireless/prism54/islpci_hotplug.c index b5e64d71b7a6..9e68e0cb718e 100644 --- a/drivers/net/wireless/prism54/islpci_hotplug.c +++ b/drivers/net/wireless/prism54/islpci_hotplug.c @@ -17,6 +17,7 @@ * */ +#include #include #include #include diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index c446db69bd3c..4efaf886fb89 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -29,6 +29,7 @@ #define RT2X00_H #include +#include #include #include #include diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 80db5cabc9b9..66b29dc07cc3 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include diff --git a/drivers/net/wireless/wl1251/sdio.c b/drivers/net/wireless/wl1251/sdio.c index f51a0241a440..f78694295c39 100644 --- a/drivers/net/wireless/wl1251/sdio.c +++ b/drivers/net/wireless/wl1251/sdio.c @@ -19,6 +19,7 @@ * Copyright (C) 2008 Google Inc * Copyright (C) 2009 Bob Copeland (me@bobcopeland.com) */ +#include #include #include #include diff --git a/drivers/net/wireless/wl1251/spi.c b/drivers/net/wireless/wl1251/spi.c index af6448c4d3e2..eaa5f9556200 100644 --- a/drivers/net/wireless/wl1251/spi.c +++ b/drivers/net/wireless/wl1251/spi.c @@ -19,6 +19,7 @@ * */ +#include #include #include #include diff --git a/drivers/net/wireless/wl12xx/io.h b/drivers/net/wireless/wl12xx/io.h index beed621a8ae0..20b00319e444 100644 --- a/drivers/net/wireless/wl12xx/io.h +++ b/drivers/net/wireless/wl12xx/io.h @@ -25,6 +25,7 @@ #ifndef __IO_H__ #define __IO_H__ +#include #include "reg.h" #define HW_ACCESS_MEMORY_MAX_RANGE 0x1FFC0 diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 51662bb68019..beebf64c5359 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -21,6 +21,7 @@ * */ +#include #include #include #include diff --git a/drivers/s390/net/ctcm_mpc.h b/drivers/s390/net/ctcm_mpc.h index 5336120cddf1..1fa07b0c11c0 100644 --- a/drivers/s390/net/ctcm_mpc.h +++ b/drivers/s390/net/ctcm_mpc.h @@ -12,6 +12,7 @@ #ifndef _CTC_MPC_H_ #define _CTC_MPC_H_ +#include #include #include "fsm.h" diff --git a/include/linux/arcdevice.h b/include/linux/arcdevice.h index 7d650a0e3d8f..7216b0daf544 100644 --- a/include/linux/arcdevice.h +++ b/include/linux/arcdevice.h @@ -20,6 +20,7 @@ #include #ifdef __KERNEL__ +#include #ifndef bool #define bool int diff --git a/include/linux/dccp.h b/include/linux/dccp.h index d638e85dc501..710c04302a15 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -236,6 +236,7 @@ enum dccp_packet_dequeueing_policy { #ifdef __KERNEL__ #include +#include #include #include #include diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ca333e79e10f..336288567c78 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1563,7 +1563,6 @@ struct packet_type { struct list_head list; }; -#include #include extern rwlock_t dev_base_lock; /* Device list lock */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6c994c004d15..7851c05833e2 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -25,6 +25,7 @@ #ifndef __HCI_CORE_H #define __HCI_CORE_H +#include #include /* HCI upper protocols */ diff --git a/include/net/sock.h b/include/net/sock.h index f2046e404a61..ebbc0bafe661 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -40,6 +40,7 @@ #ifndef _SOCK_H #define _SOCK_H +#include #include #include #include diff --git a/net/atm/pppoatm.c b/net/atm/pppoatm.c index e9aced0ec56b..db4a11c61d15 100644 --- a/net/atm/pppoatm.c +++ b/net/atm/pppoatm.c @@ -37,6 +37,7 @@ #include #include +#include #include #include #include diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c index adbb424403d4..c628a57953c9 100644 --- a/net/caif/chnl_net.c +++ b/net/caif/chnl_net.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/net/can/bcm.c b/net/can/bcm.c index 184a6572b67e..d6c8ae5b2e6a 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -43,6 +43,7 @@ #include #include +#include #include #include #include diff --git a/net/rds/ib.h b/net/rds/ib.h index 4297d92788dc..edfaaaf164eb 100644 --- a/net/rds/ib.h +++ b/net/rds/ib.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include "rds.h" diff --git a/net/rds/iw.h b/net/rds/iw.h index 90151922178c..04ce3b193f79 100644 --- a/net/rds/iw.h +++ b/net/rds/iw.h @@ -1,6 +1,7 @@ #ifndef _RDS_IW_H #define _RDS_IW_H +#include #include #include #include "rds.h" diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 3f08158b8688..e25e49061a0d 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index c3c232a88d94..a385430c722a 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 80f8da344df5..28236bab57f9 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -47,6 +47,7 @@ * o buffer memory */ +#include #include /* for Tavor hack below */ #include -- cgit v1.2.3 From 5cec93c216db77c45f7ce970d46283bcb1933884 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Sun, 5 Jun 2011 13:50:24 -0400 Subject: x86-64: Emulate legacy vsyscalls There's a fair amount of code in the vsyscall page. It contains a syscall instruction (in the gettimeofday fallback) and who knows what will happen if an exploit jumps into the middle of some other code. Reduce the risk by replacing the vsyscalls with short magic incantations that cause the kernel to emulate the real vsyscalls. These incantations are useless if entered in the middle. This causes vsyscalls to be a little more expensive than real syscalls. Fortunately sensible programs don't use them. The only exception is time() which is still called by glibc through the vsyscall - but calling time() millions of times per second is not sensible. glibc has this fixed in the development tree. This patch is not perfect: the vread_tsc and vread_hpet functions are still at a fixed address. Fixing that might involve making alternative patching work in the vDSO. Signed-off-by: Andy Lutomirski Acked-by: Linus Torvalds Cc: Jesper Juhl Cc: Borislav Petkov Cc: Arjan van de Ven Cc: Jan Beulich Cc: richard -rw- weinberger Cc: Mikael Pettersson Cc: Andi Kleen Cc: Brian Gerst Cc: Louis Rilling Cc: Valdis.Kletnieks@vt.edu Cc: pageexec@freemail.hu Link: http://lkml.kernel.org/r/e64e1b3c64858820d12c48fa739efbd1485e79d5.1307292171.git.luto@mit.edu [ Removed the CONFIG option - it's simpler to just do it unconditionally. Tidied up the code as well. ] Signed-off-by: Ingo Molnar --- arch/x86/include/asm/irq_vectors.h | 6 +- arch/x86/include/asm/traps.h | 4 + arch/x86/include/asm/vsyscall.h | 12 ++ arch/x86/kernel/Makefile | 1 + arch/x86/kernel/entry_64.S | 2 + arch/x86/kernel/traps.c | 6 + arch/x86/kernel/vsyscall_64.c | 261 +++++++++++++++++-------------------- arch/x86/kernel/vsyscall_emu_64.S | 27 ++++ include/linux/seccomp.h | 10 ++ 9 files changed, 189 insertions(+), 140 deletions(-) create mode 100644 arch/x86/kernel/vsyscall_emu_64.S (limited to 'include') diff --git a/arch/x86/include/asm/irq_vectors.h b/arch/x86/include/asm/irq_vectors.h index 6e976ee3b3ef..a563c509edcb 100644 --- a/arch/x86/include/asm/irq_vectors.h +++ b/arch/x86/include/asm/irq_vectors.h @@ -17,7 +17,8 @@ * Vectors 0 ... 31 : system traps and exceptions - hardcoded events * Vectors 32 ... 127 : device interrupts * Vector 128 : legacy int80 syscall interface - * Vectors 129 ... INVALIDATE_TLB_VECTOR_START-1 : device interrupts + * Vector 204 : legacy x86_64 vsyscall emulation + * Vectors 129 ... INVALIDATE_TLB_VECTOR_START-1 except 204 : device interrupts * Vectors INVALIDATE_TLB_VECTOR_START ... 255 : special interrupts * * 64-bit x86 has per CPU IDT tables, 32-bit has one shared IDT table. @@ -50,6 +51,9 @@ #ifdef CONFIG_X86_32 # define SYSCALL_VECTOR 0x80 #endif +#ifdef CONFIG_X86_64 +# define VSYSCALL_EMU_VECTOR 0xcc +#endif /* * Vectors 0x30-0x3f are used for ISA interrupts. diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h index 0310da67307f..2bae0a513b40 100644 --- a/arch/x86/include/asm/traps.h +++ b/arch/x86/include/asm/traps.h @@ -1,6 +1,8 @@ #ifndef _ASM_X86_TRAPS_H #define _ASM_X86_TRAPS_H +#include + #include #include /* TRAP_TRACE, ... */ @@ -38,6 +40,7 @@ asmlinkage void alignment_check(void); asmlinkage void machine_check(void); #endif /* CONFIG_X86_MCE */ asmlinkage void simd_coprocessor_error(void); +asmlinkage void emulate_vsyscall(void); dotraplinkage void do_divide_error(struct pt_regs *, long); dotraplinkage void do_debug(struct pt_regs *, long); @@ -64,6 +67,7 @@ dotraplinkage void do_alignment_check(struct pt_regs *, long); dotraplinkage void do_machine_check(struct pt_regs *, long); #endif dotraplinkage void do_simd_coprocessor_error(struct pt_regs *, long); +dotraplinkage void do_emulate_vsyscall(struct pt_regs *, long); #ifdef CONFIG_X86_32 dotraplinkage void do_iret_error(struct pt_regs *, long); #endif diff --git a/arch/x86/include/asm/vsyscall.h b/arch/x86/include/asm/vsyscall.h index d55597351f6a..bb710cb0cdc1 100644 --- a/arch/x86/include/asm/vsyscall.h +++ b/arch/x86/include/asm/vsyscall.h @@ -31,6 +31,18 @@ extern struct timezone sys_tz; extern void map_vsyscall(void); +/* Emulation */ + +static inline bool is_vsyscall_entry(unsigned long addr) +{ + return (addr & ~0xC00UL) == VSYSCALL_START; +} + +static inline int vsyscall_entry_nr(unsigned long addr) +{ + return (addr & 0xC00UL) >> 10; +} + #endif /* __KERNEL__ */ #endif /* _ASM_X86_VSYSCALL_H */ diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 90b06d4daee2..cc0469a65120 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -44,6 +44,7 @@ obj-y += probe_roms.o obj-$(CONFIG_X86_32) += sys_i386_32.o i386_ksyms_32.o obj-$(CONFIG_X86_64) += sys_x86_64.o x8664_ksyms_64.o obj-$(CONFIG_X86_64) += syscall_64.o vsyscall_64.o vread_tsc_64.o +obj-$(CONFIG_X86_64) += vsyscall_emu_64.o obj-y += bootflag.o e820.o obj-y += pci-dma.o quirks.o topology.o kdebugfs.o obj-y += alternative.o i8253.o pci-nommu.o hw_breakpoint.o diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 72c4a777bb91..e949793d6b93 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -1123,6 +1123,8 @@ zeroentry spurious_interrupt_bug do_spurious_interrupt_bug zeroentry coprocessor_error do_coprocessor_error errorentry alignment_check do_alignment_check zeroentry simd_coprocessor_error do_simd_coprocessor_error +zeroentry emulate_vsyscall do_emulate_vsyscall + /* Reload gs selector with exception handling */ /* edi: new selector */ diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index b9b67166f9de..fbc097a085ca 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -872,6 +872,12 @@ void __init trap_init(void) set_bit(SYSCALL_VECTOR, used_vectors); #endif +#ifdef CONFIG_X86_64 + BUG_ON(test_bit(VSYSCALL_EMU_VECTOR, used_vectors)); + set_system_intr_gate(VSYSCALL_EMU_VECTOR, &emulate_vsyscall); + set_bit(VSYSCALL_EMU_VECTOR, used_vectors); +#endif + /* * Should be a barrier for any external CPU state: */ diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index 70a5f6eebd6c..10cd8ac3395a 100644 --- a/arch/x86/kernel/vsyscall_64.c +++ b/arch/x86/kernel/vsyscall_64.c @@ -2,6 +2,8 @@ * Copyright (C) 2001 Andrea Arcangeli SuSE * Copyright 2003 Andi Kleen, SuSE Labs. * + * [ NOTE: this mechanism is now deprecated in favor of the vDSO. ] + * * Thanks to hpa@transmeta.com for some useful hint. * Special thanks to Ingo Molnar for his early experience with * a different vsyscall implementation for Linux/IA32 and for the name. @@ -11,10 +13,9 @@ * vsyscalls. One vsyscall can reserve more than 1 slot to avoid * jumping out of line if necessary. We cannot add more with this * mechanism because older kernels won't return -ENOSYS. - * If we want more than four we need a vDSO. * - * Note: the concept clashes with user mode linux. If you use UML and - * want per guest time just set the kernel.vsyscall64 sysctl to 0. + * Note: the concept clashes with user mode linux. UML users should + * use the vDSO. */ /* Disable profiling for userspace code: */ @@ -32,6 +33,8 @@ #include #include #include +#include +#include #include #include @@ -44,10 +47,7 @@ #include #include #include - -#define __vsyscall(nr) \ - __attribute__ ((unused, __section__(".vsyscall_" #nr))) notrace -#define __syscall_clobber "r11","cx","memory" +#include DEFINE_VVAR(int, vgetcpu_mode); DEFINE_VVAR(struct vsyscall_gtod_data, vsyscall_gtod_data) = @@ -71,146 +71,129 @@ void update_vsyscall(struct timespec *wall_time, struct timespec *wtm, unsigned long flags; write_seqlock_irqsave(&vsyscall_gtod_data.lock, flags); + /* copy vsyscall data */ - vsyscall_gtod_data.clock.vread = clock->vread; - vsyscall_gtod_data.clock.cycle_last = clock->cycle_last; - vsyscall_gtod_data.clock.mask = clock->mask; - vsyscall_gtod_data.clock.mult = mult; - vsyscall_gtod_data.clock.shift = clock->shift; - vsyscall_gtod_data.wall_time_sec = wall_time->tv_sec; - vsyscall_gtod_data.wall_time_nsec = wall_time->tv_nsec; - vsyscall_gtod_data.wall_to_monotonic = *wtm; - vsyscall_gtod_data.wall_time_coarse = __current_kernel_time(); + vsyscall_gtod_data.clock.vread = clock->vread; + vsyscall_gtod_data.clock.cycle_last = clock->cycle_last; + vsyscall_gtod_data.clock.mask = clock->mask; + vsyscall_gtod_data.clock.mult = mult; + vsyscall_gtod_data.clock.shift = clock->shift; + vsyscall_gtod_data.wall_time_sec = wall_time->tv_sec; + vsyscall_gtod_data.wall_time_nsec = wall_time->tv_nsec; + vsyscall_gtod_data.wall_to_monotonic = *wtm; + vsyscall_gtod_data.wall_time_coarse = __current_kernel_time(); + write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags); } -/* RED-PEN may want to readd seq locking, but then the variable should be - * write-once. - */ -static __always_inline void do_get_tz(struct timezone * tz) +static void warn_bad_vsyscall(const char *level, struct pt_regs *regs, + const char *message) { - *tz = VVAR(vsyscall_gtod_data).sys_tz; -} + static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); + struct task_struct *tsk; -static __always_inline int gettimeofday(struct timeval *tv, struct timezone *tz) -{ - int ret; - asm volatile("syscall" - : "=a" (ret) - : "0" (__NR_gettimeofday),"D" (tv),"S" (tz) - : __syscall_clobber ); - return ret; -} + if (!show_unhandled_signals || !__ratelimit(&rs)) + return; -static __always_inline void do_vgettimeofday(struct timeval * tv) -{ - cycle_t now, base, mask, cycle_delta; - unsigned seq; - unsigned long mult, shift, nsec; - cycle_t (*vread)(void); - do { - seq = read_seqbegin(&VVAR(vsyscall_gtod_data).lock); - - vread = VVAR(vsyscall_gtod_data).clock.vread; - if (unlikely(!vread)) { - gettimeofday(tv,NULL); - return; - } - - now = vread(); - base = VVAR(vsyscall_gtod_data).clock.cycle_last; - mask = VVAR(vsyscall_gtod_data).clock.mask; - mult = VVAR(vsyscall_gtod_data).clock.mult; - shift = VVAR(vsyscall_gtod_data).clock.shift; - - tv->tv_sec = VVAR(vsyscall_gtod_data).wall_time_sec; - nsec = VVAR(vsyscall_gtod_data).wall_time_nsec; - } while (read_seqretry(&VVAR(vsyscall_gtod_data).lock, seq)); - - /* calculate interval: */ - cycle_delta = (now - base) & mask; - /* convert to nsecs: */ - nsec += (cycle_delta * mult) >> shift; - - while (nsec >= NSEC_PER_SEC) { - tv->tv_sec += 1; - nsec -= NSEC_PER_SEC; - } - tv->tv_usec = nsec / NSEC_PER_USEC; -} + tsk = current; -int __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz) -{ - if (tv) - do_vgettimeofday(tv); - if (tz) - do_get_tz(tz); - return 0; + printk("%s%s[%d] %s ip:%lx sp:%lx ax:%lx si:%lx di:%lx\n", + level, tsk->comm, task_pid_nr(tsk), + message, regs->ip - 2, regs->sp, regs->ax, regs->si, regs->di); } -/* This will break when the xtime seconds get inaccurate, but that is - * unlikely */ -time_t __vsyscall(1) vtime(time_t *t) +void dotraplinkage do_emulate_vsyscall(struct pt_regs *regs, long error_code) { - unsigned seq; - time_t result; + const char *vsyscall_name; + struct task_struct *tsk; + unsigned long caller; + int vsyscall_nr; + long ret; + + /* Kernel code must never get here. */ + BUG_ON(!user_mode(regs)); + + local_irq_enable(); + + /* + * x86-ism here: regs->ip points to the instruction after the int 0xcc, + * and int 0xcc is two bytes long. + */ + if (!is_vsyscall_entry(regs->ip - 2)) { + warn_bad_vsyscall(KERN_WARNING, regs, "illegal int 0xcc (exploit attempt?)"); + goto sigsegv; + } + vsyscall_nr = vsyscall_entry_nr(regs->ip - 2); - do { - seq = read_seqbegin(&VVAR(vsyscall_gtod_data).lock); + if (get_user(caller, (unsigned long __user *)regs->sp) != 0) { + warn_bad_vsyscall(KERN_WARNING, regs, "int 0xcc with bad stack (exploit attempt?)"); + goto sigsegv; + } - result = VVAR(vsyscall_gtod_data).wall_time_sec; + tsk = current; + if (seccomp_mode(&tsk->seccomp)) + do_exit(SIGKILL); + + switch (vsyscall_nr) { + case 0: + vsyscall_name = "gettimeofday"; + ret = sys_gettimeofday( + (struct timeval __user *)regs->di, + (struct timezone __user *)regs->si); + break; + + case 1: + vsyscall_name = "time"; + ret = sys_time((time_t __user *)regs->di); + break; + + case 2: + vsyscall_name = "getcpu"; + ret = sys_getcpu((unsigned __user *)regs->di, + (unsigned __user *)regs->si, + 0); + break; + + default: + /* + * If we get here, then vsyscall_nr indicates that int 0xcc + * happened at an address in the vsyscall page that doesn't + * contain int 0xcc. That can't happen. + */ + BUG(); + } - } while (read_seqretry(&VVAR(vsyscall_gtod_data).lock, seq)); + if (ret == -EFAULT) { + /* + * Bad news -- userspace fed a bad pointer to a vsyscall. + * + * With a real vsyscall, that would have caused SIGSEGV. + * To make writing reliable exploits using the emulated + * vsyscalls harder, generate SIGSEGV here as well. + */ + warn_bad_vsyscall(KERN_INFO, regs, + "vsyscall fault (exploit attempt?)"); + goto sigsegv; + } - if (t) - *t = result; - return result; -} + regs->ax = ret; -/* Fast way to get current CPU and node. - This helps to do per node and per CPU caches in user space. - The result is not guaranteed without CPU affinity, but usually - works out because the scheduler tries to keep a thread on the same - CPU. + /* Emulate a ret instruction. */ + regs->ip = caller; + regs->sp += 8; - tcache must point to a two element sized long array. - All arguments can be NULL. */ -long __vsyscall(2) -vgetcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache) -{ - unsigned int p; - unsigned long j = 0; - - /* Fast cache - only recompute value once per jiffies and avoid - relatively costly rdtscp/cpuid otherwise. - This works because the scheduler usually keeps the process - on the same CPU and this syscall doesn't guarantee its - results anyways. - We do this here because otherwise user space would do it on - its own in a likely inferior way (no access to jiffies). - If you don't like it pass NULL. */ - if (tcache && tcache->blob[0] == (j = VVAR(jiffies))) { - p = tcache->blob[1]; - } else if (VVAR(vgetcpu_mode) == VGETCPU_RDTSCP) { - /* Load per CPU data from RDTSCP */ - native_read_tscp(&p); - } else { - /* Load per CPU data from GDT */ - asm("lsl %1,%0" : "=r" (p) : "r" (__PER_CPU_SEG)); - } - if (tcache) { - tcache->blob[0] = j; - tcache->blob[1] = p; - } - if (cpu) - *cpu = p & 0xfff; - if (node) - *node = p >> 12; - return 0; + local_irq_disable(); + return; + +sigsegv: + regs->ip -= 2; /* The faulting instruction should be the int 0xcc. */ + force_sig(SIGSEGV, current); } -/* Assume __initcall executes before all user space. Hopefully kmod - doesn't violate that. We'll find out if it does. */ +/* + * Assume __initcall executes before all user space. Hopefully kmod + * doesn't violate that. We'll find out if it does. + */ static void __cpuinit vsyscall_set_cpu(int cpu) { unsigned long d; @@ -221,13 +204,15 @@ static void __cpuinit vsyscall_set_cpu(int cpu) if (cpu_has(&cpu_data(cpu), X86_FEATURE_RDTSCP)) write_rdtscp_aux((node << 12) | cpu); - /* Store cpu number in limit so that it can be loaded quickly - in user space in vgetcpu. - 12 bits for the CPU and 8 bits for the node. */ + /* + * Store cpu number in limit so that it can be loaded quickly + * in user space in vgetcpu. (12 bits for the CPU and 8 bits for the node) + */ d = 0x0f40000000000ULL; d |= cpu; d |= (node & 0xf) << 12; d |= (node >> 4) << 48; + write_gdt_entry(get_cpu_gdt_table(cpu), GDT_ENTRY_PER_CPU, &d, DESCTYPE_S); } @@ -241,8 +226,10 @@ static int __cpuinit cpu_vsyscall_notifier(struct notifier_block *n, unsigned long action, void *arg) { long cpu = (long)arg; + if (action == CPU_ONLINE || action == CPU_ONLINE_FROZEN) smp_call_function_single(cpu, cpu_vsyscall_init, NULL, 1); + return NOTIFY_DONE; } @@ -256,21 +243,17 @@ void __init map_vsyscall(void) /* Note that VSYSCALL_MAPPED_PAGES must agree with the code below. */ __set_fixmap(VSYSCALL_FIRST_PAGE, physaddr_page0, PAGE_KERNEL_VSYSCALL); __set_fixmap(VVAR_PAGE, physaddr_vvar_page, PAGE_KERNEL_VVAR); - BUILD_BUG_ON((unsigned long)__fix_to_virt(VVAR_PAGE) != - (unsigned long)VVAR_ADDRESS); + BUILD_BUG_ON((unsigned long)__fix_to_virt(VVAR_PAGE) != (unsigned long)VVAR_ADDRESS); } static int __init vsyscall_init(void) { - BUG_ON(((unsigned long) &vgettimeofday != - VSYSCALL_ADDR(__NR_vgettimeofday))); - BUG_ON((unsigned long) &vtime != VSYSCALL_ADDR(__NR_vtime)); - BUG_ON((VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE))); - BUG_ON((unsigned long) &vgetcpu != VSYSCALL_ADDR(__NR_vgetcpu)); + BUG_ON(VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE)); + on_each_cpu(cpu_vsyscall_init, NULL, 1); /* notifier priority > KVM */ hotcpu_notifier(cpu_vsyscall_notifier, 30); + return 0; } - __initcall(vsyscall_init); diff --git a/arch/x86/kernel/vsyscall_emu_64.S b/arch/x86/kernel/vsyscall_emu_64.S new file mode 100644 index 000000000000..ffa845eae5ca --- /dev/null +++ b/arch/x86/kernel/vsyscall_emu_64.S @@ -0,0 +1,27 @@ +/* + * vsyscall_emu_64.S: Vsyscall emulation page + * + * Copyright (c) 2011 Andy Lutomirski + * + * Subject to the GNU General Public License, version 2 + */ + +#include +#include + +/* The unused parts of the page are filled with 0xcc by the linker script. */ + +.section .vsyscall_0, "a" +ENTRY(vsyscall_0) + int $VSYSCALL_EMU_VECTOR +END(vsyscall_0) + +.section .vsyscall_1, "a" +ENTRY(vsyscall_1) + int $VSYSCALL_EMU_VECTOR +END(vsyscall_1) + +.section .vsyscall_2, "a" +ENTRY(vsyscall_2) + int $VSYSCALL_EMU_VECTOR +END(vsyscall_2) diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 167c33361d9c..cc7a4e9cc7ad 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -19,6 +19,11 @@ static inline void secure_computing(int this_syscall) extern long prctl_get_seccomp(void); extern long prctl_set_seccomp(unsigned long); +static inline int seccomp_mode(seccomp_t *s) +{ + return s->mode; +} + #else /* CONFIG_SECCOMP */ #include @@ -37,6 +42,11 @@ static inline long prctl_set_seccomp(unsigned long arg2) return -EINVAL; } +static inline int seccomp_mode(seccomp_t *s) +{ + return 0; +} + #endif /* CONFIG_SECCOMP */ #endif /* _LINUX_SECCOMP_H */ -- cgit v1.2.3 From 6dc1418e13144162e8bc4858789010d8f0e1e65c Mon Sep 17 00:00:00 2001 From: Tomoki Sekiyama Date: Mon, 23 May 2011 15:45:44 -0700 Subject: HID: yurex: recognize GeneralKeys wireless presenter as generic HID Unfortunately, the device seems to have the same Vendor ID and Product ID as YUREX leg-shakes sensors, and the commit 6bc235a2e2 ("USB: add driver for Meywa-Denki & Kayac YUREX") added the ID to hid_ignore_list. I believe that we can distinguish YUREX and the Wireless Presenter by device type. The patch below makes the driver ignore only YUREX (bInterfaceProtocol==0), and recognize Wireless Presenter (bInterfaceProtocol is keyboard or mouse) as generic HID. (I don't have the Wireless Presenter, so not yet ested.) ** YUREX lsusb information: Bus 002 Device 007: ID 0c45:1010 Microdia Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 1.10 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 8 idVendor 0x0c45 Microdia idProduct 0x1010 bcdDevice 0.03 iManufacturer 1 JESS iProduct 2 YUREX iSerial 3 10000269 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 34 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xa0 (Bus Powered) Remote Wakeup MaxPower 100mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 3 Human Interface Device bInterfaceSubClass 1 Boot Interface Subclass bInterfaceProtocol 0 None iInterface 0 HID Device Descriptor: bLength 9 bDescriptorType 33 bcdHID 1.10 bCountryCode 0 Not supported bNumDescriptors 1 bDescriptorType 34 Report wDescriptorLength 31 Report Descriptors: ** UNAVAILABLE ** Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0008 1x 8 bytes bInterval 10 Device Status: 0x0002 (Bus Powered) Remote Wakeup Enabled Addresses https://bugzilla.kernel.org/show_bug.cgi?id=26922 Signed-off-by: Tomoki Sekiyama Cc: Greg KH Cc: "Rafael J. Wysocki" Cc: Maciej Rutecki Reported-by: Thomas B?chler Tested-by: Thomas B?chler Signed-off-by: Andrew Morton Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 6 +++++- drivers/hid/usbhid/hid-core.c | 2 ++ include/linux/hid.h | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index c957c4b4fe70..9d6495d19e02 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1769,7 +1769,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) }, { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) }, { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, - { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_JESS_YUREX) }, { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, @@ -1910,6 +1909,11 @@ static bool hid_ignore(struct hid_device *hdev) hdev->product <= USB_DEVICE_ID_HANWANG_TABLET_LAST) return true; break; + case USB_VENDOR_ID_JESS: + if (hdev->product == USB_DEVICE_ID_JESS_YUREX && + hdev->type == HID_TYPE_USBNONE) + return true; + break; } if (hdev->type == HID_TYPE_USBMOUSE && diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 38c261a40c74..ad978f5748d3 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1191,6 +1191,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * if (intf->cur_altsetting->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) hid->type = HID_TYPE_USBMOUSE; + else if (intf->cur_altsetting->desc.bInterfaceProtocol == 0) + hid->type = HID_TYPE_USBNONE; if (dev->manufacturer) strlcpy(hid->name, dev->manufacturer, sizeof(hid->name)); diff --git a/include/linux/hid.h b/include/linux/hid.h index 42f7e2fb501f..9cf8e7ae7450 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -453,7 +453,8 @@ struct hid_input { enum hid_type { HID_TYPE_OTHER = 0, - HID_TYPE_USBMOUSE + HID_TYPE_USBMOUSE, + HID_TYPE_USBNONE }; struct hid_driver; -- cgit v1.2.3 From 21c13a4f7bc185552c4b402b792c3bbb9aa69df0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 7 Jun 2011 11:35:52 -0400 Subject: usb-storage: redo incorrect reads Some USB mass-storage devices have bugs that cause them not to handle the first READ(10) command they receive correctly. The Corsair Padlock v2 returns completely bogus data for its first read (possibly it returns the data in encrypted form even though the device is supposed to be unlocked). The Feiya SD/SDHC card reader fails to complete the first READ(10) command after it is plugged in or after a new card is inserted, returning a status code that indicates it thinks the command was invalid, which prevents the kernel from retrying the read. Since the first read of a new device or a new medium is for the partition sector, the kernel is unable to retrieve the device's partition table. Users have to manually issue an "hdparm -z" or "blockdev --rereadpt" command before they can access the device. This patch (as1470) works around the problem. It adds a new quirk flag, US_FL_INVALID_READ10, indicating that the first READ(10) should always be retried immediately, as should any failing READ(10) commands (provided the preceding READ(10) command succeeded, to avoid getting stuck in a loop). The patch also adds appropriate unusual_devs entries containing the new flag. Signed-off-by: Alan Stern Tested-by: Sven Geggus Tested-by: Paul Hartman CC: Matthew Dharm CC: Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 2 ++ drivers/usb/storage/transport.c | 29 +++++++++++++++++++++++++++++ drivers/usb/storage/unusual_devs.h | 19 +++++++++++++++++++ drivers/usb/storage/usb.c | 13 ++++++++++++- drivers/usb/storage/usb.h | 2 ++ include/linux/usb_usual.h | 4 +++- 6 files changed, 67 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index d9a203b058f1..fd248a318211 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2598,6 +2598,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. unlock ejectable media); m = MAX_SECTORS_64 (don't transfer more than 64 sectors = 32 KB at a time); + n = INITIAL_READ10 (force a retry of the + initial READ(10) command); o = CAPACITY_OK (accept the capacity reported by the device); r = IGNORE_RESIDUE (the device reports diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 00418995d8e9..e8ae21b2d387 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -819,6 +819,35 @@ Retry_Sense: } } + /* + * Some devices don't work or return incorrect data the first + * time they get a READ(10) command, or for the first READ(10) + * after a media change. If the INITIAL_READ10 flag is set, + * keep track of whether READ(10) commands succeed. If the + * previous one succeeded and this one failed, set the REDO_READ10 + * flag to force a retry. + */ + if (unlikely((us->fflags & US_FL_INITIAL_READ10) && + srb->cmnd[0] == READ_10)) { + if (srb->result == SAM_STAT_GOOD) { + set_bit(US_FLIDX_READ10_WORKED, &us->dflags); + } else if (test_bit(US_FLIDX_READ10_WORKED, &us->dflags)) { + clear_bit(US_FLIDX_READ10_WORKED, &us->dflags); + set_bit(US_FLIDX_REDO_READ10, &us->dflags); + } + + /* + * Next, if the REDO_READ10 flag is set, return a result + * code that will cause the SCSI core to retry the READ(10) + * command immediately. + */ + if (test_bit(US_FLIDX_REDO_READ10, &us->dflags)) { + clear_bit(US_FLIDX_REDO_READ10, &us->dflags); + srb->result = DID_IMM_RETRY << 16; + srb->sense_buffer[0] = 0; + } + } + /* Did we transfer less than the minimum amount required? */ if ((srb->result == SAM_STAT_GOOD || srb->sense_buffer[2] == 0) && scsi_bufflen(srb) - scsi_get_resid(srb) < srb->underflow) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index c1602b8c5594..ccff3483eebc 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1114,6 +1114,16 @@ UNUSUAL_DEV( 0x090c, 0x1132, 0x0000, 0xffff, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), +/* Reported by Paul Hartman + * This card reader returns "Illegal Request, Logical Block Address + * Out of Range" for the first READ(10) after a new card is inserted. + */ +UNUSUAL_DEV( 0x090c, 0x6000, 0x0100, 0x0100, + "Feiya", + "SD/SDHC Card Reader", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_INITIAL_READ10 ), + /* This Pentax still camera is not conformant * to the USB storage specification: - * - It does not like the INQUIRY command. So we must handle this command @@ -1888,6 +1898,15 @@ UNUSUAL_DEV( 0x1908, 0x3335, 0x0200, 0x0200, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_READ_DISC_INFO ), +/* Reported by Sven Geggus + * This encrypted pen drive returns bogus data for the initial READ(10). + */ +UNUSUAL_DEV( 0x1b1c, 0x1ab5, 0x0200, 0x0200, + "Corsair", + "Padlock v2", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_INITIAL_READ10 ), + /* Patch by Richard Schütz * This external hard drive enclosure uses a JMicron chip which * needs the US_FL_IGNORE_RESIDUE flag to work properly. */ diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 5ee7ac42e08f..0ca095820f3e 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -440,7 +440,8 @@ static void adjust_quirks(struct us_data *us) US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 | US_FL_CAPACITY_OK | US_FL_IGNORE_RESIDUE | US_FL_SINGLE_LUN | US_FL_NO_WP_DETECT | - US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16); + US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16 | + US_FL_INITIAL_READ10); p = quirks; while (*p) { @@ -490,6 +491,9 @@ static void adjust_quirks(struct us_data *us) case 'm': f |= US_FL_MAX_SECTORS_64; break; + case 'n': + f |= US_FL_INITIAL_READ10; + break; case 'o': f |= US_FL_CAPACITY_OK; break; @@ -953,6 +957,13 @@ int usb_stor_probe2(struct us_data *us) if (result) goto BadDevice; + /* + * If the device returns invalid data for the first READ(10) + * command, indicate the command should be retried. + */ + if (us->fflags & US_FL_INITIAL_READ10) + set_bit(US_FLIDX_REDO_READ10, &us->dflags); + /* Acquire all the other resources and add the host */ result = usb_stor_acquire_resources(us); if (result) diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 89d3bfff98df..7b0f2113632e 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -73,6 +73,8 @@ struct us_unusual_dev { #define US_FLIDX_RESETTING 4 /* device reset in progress */ #define US_FLIDX_TIMED_OUT 5 /* SCSI midlayer timed out */ #define US_FLIDX_DONT_SCAN 6 /* don't scan (disconnect) */ +#define US_FLIDX_REDO_READ10 7 /* redo READ(10) command */ +#define US_FLIDX_READ10_WORKED 8 /* previous READ(10) succeeded */ #define USB_STOR_STRING_LEN 32 diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h index 71693d4a4fe1..17df3600bcef 100644 --- a/include/linux/usb_usual.h +++ b/include/linux/usb_usual.h @@ -62,7 +62,9 @@ US_FLAG(NO_READ_DISC_INFO, 0x00040000) \ /* cannot handle READ_DISC_INFO */ \ US_FLAG(NO_READ_CAPACITY_16, 0x00080000) \ - /* cannot handle READ_CAPACITY_16 */ + /* cannot handle READ_CAPACITY_16 */ \ + US_FLAG(INITIAL_READ10, 0x00100000) \ + /* Initial READ(10) (and others) must be retried */ #define US_FLAG(name, value) US_FL_##name = value , enum { US_DO_ALL_FLAGS }; -- cgit v1.2.3 From 325fd182cafe5c5ead51c27afb6b8be83c9081d4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 7 Jun 2011 15:39:18 +0100 Subject: USB: gadget.h depends on ch9.h so include ch9.h directly The struct definitions are used. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/gadget.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index dd1571db55e7..55d1a88ff11f 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -16,6 +16,7 @@ #define __LINUX_USB_GADGET_H #include +#include struct usb_ep; -- cgit v1.2.3 From e73a9891b3a1c9fc0970e0c9dbe2cc47933ad752 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 6 Jun 2011 14:19:03 +0900 Subject: usb: renesas_usbhs: add DMAEngine support USB DMA was installed on "normal DMAC" when SH7724 or older SuperH, but the "USB-DMAC" was prepared on recent SuperH. These 2 DMAC have a little bit different behavior. This patch add DMAEngine code for "normal DMAC", but it is still using PIO fifo. The DMA fifo will be formally supported in the future. You can enable DMA fifo by local fixup usbhs_fifo_pio_push_handler -> usbhs_fifo_dma_push_handler usbhs_fifo_pio_pop_handler -> usbhs_fifo_dma_pop_handler on usbhsg_ep_enable. This DMAEngine was tested by g_file_storage on SH7724 Ecovec board Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 2 + drivers/usb/renesas_usbhs/common.h | 35 +++ drivers/usb/renesas_usbhs/fifo.c | 380 +++++++++++++++++++++++++++++++++ drivers/usb/renesas_usbhs/fifo.h | 24 +++ drivers/usb/renesas_usbhs/mod_gadget.c | 70 +++++- drivers/usb/renesas_usbhs/pipe.c | 4 +- drivers/usb/renesas_usbhs/pipe.h | 4 +- include/linux/usb/renesas_usbhs.h | 17 ++ 8 files changed, 533 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index e510b29216b3..665259aec871 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -304,6 +304,8 @@ static int __devinit usbhs_probe(struct platform_device *pdev) priv->dparam->pipe_type = usbhsc_default_pipe_type; priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type); } + if (!priv->dparam->pio_dma_border) + priv->dparam->pio_dma_border = 64; /* 64byte */ /* FIXME */ /* runtime power control ? */ diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 06d7239a044d..b410463a1212 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -36,6 +36,12 @@ struct usbhs_priv; #define CFIFO 0x0014 #define CFIFOSEL 0x0020 #define CFIFOCTR 0x0022 +#define D0FIFO 0x0100 +#define D0FIFOSEL 0x0028 +#define D0FIFOCTR 0x002A +#define D1FIFO 0x0120 +#define D1FIFOSEL 0x002C +#define D1FIFOCTR 0x002E #define INTENB0 0x0030 #define INTENB1 0x0032 #define BRDYENB 0x0036 @@ -60,6 +66,30 @@ struct usbhs_priv; #define PIPEMAXP 0x006C #define PIPEPERI 0x006E #define PIPEnCTR 0x0070 +#define PIPE1TRE 0x0090 +#define PIPE1TRN 0x0092 +#define PIPE2TRE 0x0094 +#define PIPE2TRN 0x0096 +#define PIPE3TRE 0x0098 +#define PIPE3TRN 0x009A +#define PIPE4TRE 0x009C +#define PIPE4TRN 0x009E +#define PIPE5TRE 0x00A0 +#define PIPE5TRN 0x00A2 +#define PIPEBTRE 0x00A4 +#define PIPEBTRN 0x00A6 +#define PIPECTRE 0x00A8 +#define PIPECTRN 0x00AA +#define PIPEDTRE 0x00AC +#define PIPEDTRN 0x00AE +#define PIPEETRE 0x00B0 +#define PIPEETRN 0x00B2 +#define PIPEFTRE 0x00B4 +#define PIPEFTRN 0x00B6 +#define PIPE9TRE 0x00B8 +#define PIPE9TRN 0x00BA +#define PIPEATRE 0x00BC +#define PIPEATRN 0x00BE /* SYSCFG */ #define SCKE (1 << 10) /* USB Module Clock Enable */ @@ -78,6 +108,7 @@ struct usbhs_priv; #define RHST_HIGH_SPEED 3 /* High-speed connection */ /* CFIFOSEL */ +#define DREQE (1 << 12) /* DMA Transfer Request Enable */ #define MBW_32 (0x2 << 10) /* CFIFO Port Access Bit Width */ /* CFIFOCTR */ @@ -164,6 +195,10 @@ struct usbhs_priv; #define CCPL (1 << 2) /* Control Transfer End Enable */ +/* PIPEnTRE */ +#define TRENB (1 << 9) /* Transaction Counter Enable */ +#define TRCLR (1 << 8) /* Transaction Counter Clear */ + /* FRMNUM */ #define FRNM_MASK (0x7FF) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 14baaad20b79..2016a2448ccb 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -20,6 +20,8 @@ #include "./pipe.h" #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_fifo_is_busy(f) ((f)->pipe) /* see usbhs_pipe_select_fifo */ @@ -43,6 +45,7 @@ static struct usbhs_pkt_handle usbhsf_null_handler = { void usbhs_pkt_init(struct usbhs_pkt *pkt) { + pkt->dma = DMA_ADDR_INVALID; INIT_LIST_HEAD(&pkt->node); } @@ -136,6 +139,9 @@ int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type) case USBHSF_PKT_TRY_RUN: func = pkt->handler->try_run; break; + case USBHSF_PKT_DMA_DONE: + func = pkt->handler->dma_done; + break; default: dev_err(dev, "unknown pkt hander\n"); goto __usbhs_pkt_handler_end; @@ -507,6 +513,330 @@ struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler = { .try_run = usbhsf_ctrl_stage_end, }; +/* + * DMA fifo functions + */ +static struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo, + struct usbhs_pkt *pkt) +{ + if (&usbhs_fifo_dma_push_handler == pkt->handler) + return fifo->tx_chan; + + if (&usbhs_fifo_dma_pop_handler == pkt->handler) + return fifo->rx_chan; + + return NULL; +} + +static struct usbhs_fifo *usbhsf_get_dma_fifo(struct usbhs_priv *priv, + struct usbhs_pkt *pkt) +{ + struct usbhs_fifo *fifo; + + /* DMA :: D0FIFO */ + fifo = usbhsf_get_d0fifo(priv); + if (usbhsf_dma_chan_get(fifo, pkt) && + !usbhsf_fifo_is_busy(fifo)) + return fifo; + + /* DMA :: D1FIFO */ + fifo = usbhsf_get_d1fifo(priv); + if (usbhsf_dma_chan_get(fifo, pkt) && + !usbhsf_fifo_is_busy(fifo)) + return fifo; + + return NULL; +} + +#define usbhsf_dma_start(p, f) __usbhsf_dma_ctrl(p, f, DREQE) +#define usbhsf_dma_stop(p, f) __usbhsf_dma_ctrl(p, f, 0) +static void __usbhsf_dma_ctrl(struct usbhs_pipe *pipe, + struct usbhs_fifo *fifo, + u16 dreqe) +{ + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + + usbhs_bset(priv, fifo->sel, DREQE, dreqe); +} + +#define usbhsf_dma_map(p) __usbhsf_dma_map_ctrl(p, 1) +#define usbhsf_dma_unmap(p) __usbhsf_dma_map_ctrl(p, 0) +static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); + + return info->dma_map_ctrl(pkt, map); +} + +static void usbhsf_dma_complete(void *arg); +static void usbhsf_dma_prepare_tasklet(unsigned long data) +{ + struct usbhs_pkt *pkt = (struct usbhs_pkt *)data; + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe); + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct scatterlist sg; + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt); + struct device *dev = usbhs_priv_to_dev(priv); + enum dma_data_direction dir; + dma_cookie_t cookie; + + dir = usbhs_pipe_is_dir_in(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + sg_init_table(&sg, 1); + sg_set_page(&sg, virt_to_page(pkt->dma), + pkt->length, offset_in_page(pkt->dma)); + sg_dma_address(&sg) = pkt->dma + pkt->actual; + sg_dma_len(&sg) = pkt->trans; + + desc = chan->device->device_prep_slave_sg(chan, &sg, 1, dir, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!desc) + return; + + desc->callback = usbhsf_dma_complete; + desc->callback_param = pipe; + + cookie = desc->tx_submit(desc); + if (cookie < 0) { + dev_err(dev, "Failed to submit dma descriptor\n"); + return; + } + + dev_dbg(dev, " %s %d (%d/ %d)\n", + fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); + + usbhsf_dma_start(pipe, fifo); + dma_async_issue_pending(chan); +} + +static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo; + int len = pkt->length - pkt->actual; + int ret; + + if (usbhs_pipe_is_busy(pipe)) + return 0; + + /* use PIO if packet is less than pio_dma_border or pipe is DCP */ + if ((len < usbhs_get_dparam(priv, pio_dma_border)) || + usbhs_pipe_is_dcp(pipe)) + goto usbhsf_pio_prepare_push; + + if (len % 4) /* 32bit alignment */ + goto usbhsf_pio_prepare_push; + + /* get enable DMA fifo */ + fifo = usbhsf_get_dma_fifo(priv, pkt); + if (!fifo) + goto usbhsf_pio_prepare_push; + + if (usbhsf_dma_map(pkt) < 0) + goto usbhsf_pio_prepare_push; + + ret = usbhsf_fifo_select(pipe, fifo, 0); + if (ret < 0) + goto usbhsf_pio_prepare_push_unmap; + + pkt->trans = len; + + tasklet_init(&fifo->tasklet, + usbhsf_dma_prepare_tasklet, + (unsigned long)pkt); + + tasklet_schedule(&fifo->tasklet); + + return 0; + +usbhsf_pio_prepare_push_unmap: + usbhsf_dma_unmap(pkt); +usbhsf_pio_prepare_push: + /* + * change handler to PIO + */ + pkt->handler = &usbhs_fifo_pio_push_handler; + + return pkt->handler->prepare(pkt, is_done); +} + +static int usbhsf_dma_push_done(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + + pkt->actual = pkt->trans; + + *is_done = !pkt->zero; /* send zero packet ? */ + + usbhsf_dma_stop(pipe, pipe->fifo); + usbhsf_dma_unmap(pkt); + usbhsf_fifo_unselect(pipe, pipe->fifo); + + return 0; +} + +struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = { + .prepare = usbhsf_dma_prepare_push, + .dma_done = usbhsf_dma_push_done, +}; + +static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo; + int len, ret; + + if (usbhs_pipe_is_busy(pipe)) + return 0; + + if (usbhs_pipe_is_dcp(pipe)) + goto usbhsf_pio_prepare_pop; + + /* get enable DMA fifo */ + fifo = usbhsf_get_dma_fifo(priv, pkt); + if (!fifo) + goto usbhsf_pio_prepare_pop; + + ret = usbhsf_fifo_select(pipe, fifo, 0); + if (ret < 0) + goto usbhsf_pio_prepare_pop; + + /* use PIO if packet is less than pio_dma_border */ + len = usbhsf_fifo_rcv_len(priv, fifo); + len = min(pkt->length - pkt->actual, len); + if (len % 4) /* 32bit alignment */ + goto usbhsf_pio_prepare_pop_unselect; + + if (len < usbhs_get_dparam(priv, pio_dma_border)) + goto usbhsf_pio_prepare_pop_unselect; + + ret = usbhsf_fifo_barrier(priv, fifo); + if (ret < 0) + goto usbhsf_pio_prepare_pop_unselect; + + if (usbhsf_dma_map(pkt) < 0) + goto usbhsf_pio_prepare_pop_unselect; + + /* DMA */ + + /* + * usbhs_fifo_dma_pop_handler :: prepare + * enabled irq to come here. + * but it is no longer needed for DMA. disable it. + */ + usbhsf_rx_irq_ctrl(pipe, 0); + + pkt->trans = len; + + tasklet_init(&fifo->tasklet, + usbhsf_dma_prepare_tasklet, + (unsigned long)pkt); + + tasklet_schedule(&fifo->tasklet); + + return 0; + +usbhsf_pio_prepare_pop_unselect: + usbhsf_fifo_unselect(pipe, fifo); +usbhsf_pio_prepare_pop: + + /* + * change handler to PIO + */ + pkt->handler = &usbhs_fifo_pio_pop_handler; + + return pkt->handler->try_run(pkt, is_done); +} + +static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + int maxp = usbhs_pipe_get_maxpacket(pipe); + + usbhsf_dma_stop(pipe, pipe->fifo); + usbhsf_dma_unmap(pkt); + usbhsf_fifo_unselect(pipe, pipe->fifo); + + pkt->actual += pkt->trans; + + if ((pkt->actual == pkt->length) || /* receive all data */ + (pkt->trans < maxp)) { /* short packet */ + *is_done = 1; + } else { + /* re-enable */ + usbhsf_prepare_pop(pkt, is_done); + } + + return 0; +} + +struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = { + .prepare = usbhsf_prepare_pop, + .try_run = usbhsf_dma_try_pop, + .dma_done = usbhsf_dma_pop_done +}; + +/* + * DMA setting + */ +static bool usbhsf_dma_filter(struct dma_chan *chan, void *param) +{ + struct sh_dmae_slave *slave = param; + + /* + * FIXME + * + * usbhs doesn't recognize id = 0 as valid DMA + */ + if (0 == slave->slave_id) + return false; + + chan->private = slave; + + return true; +} + +static void usbhsf_dma_quit(struct usbhs_priv *priv, struct usbhs_fifo *fifo) +{ + if (fifo->tx_chan) + dma_release_channel(fifo->tx_chan); + if (fifo->rx_chan) + dma_release_channel(fifo->rx_chan); + + fifo->tx_chan = NULL; + fifo->rx_chan = NULL; +} + +static void usbhsf_dma_init(struct usbhs_priv *priv, + struct usbhs_fifo *fifo) +{ + struct device *dev = usbhs_priv_to_dev(priv); + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + fifo->tx_chan = dma_request_channel(mask, usbhsf_dma_filter, + &fifo->tx_slave); + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + fifo->rx_chan = dma_request_channel(mask, usbhsf_dma_filter, + &fifo->rx_slave); + + if (fifo->tx_chan || fifo->rx_chan) + dev_info(dev, "enable DMAEngine (%s%s%s)\n", + fifo->name, + fifo->tx_chan ? "[TX]" : " ", + fifo->rx_chan ? "[RX]" : " "); +} + /* * irq functions */ @@ -570,6 +900,19 @@ static int usbhsf_irq_ready(struct usbhs_priv *priv, return 0; } +static void usbhsf_dma_complete(void *arg) +{ + struct usbhs_pipe *pipe = arg; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct device *dev = usbhs_priv_to_dev(priv); + int ret; + + ret = usbhs_pkt_dmadone(pipe); + if (ret < 0) + dev_err(dev, "dma_complete run_error %d : %d\n", + usbhs_pipe_number(pipe), ret); +} + /* * fifo init */ @@ -577,6 +920,8 @@ void usbhs_fifo_init(struct usbhs_priv *priv) { struct usbhs_mod *mod = usbhs_mod_get_current(priv); struct usbhs_fifo *cfifo = usbhsf_get_cfifo(priv); + struct usbhs_fifo *d0fifo = usbhsf_get_d0fifo(priv); + struct usbhs_fifo *d1fifo = usbhsf_get_d1fifo(priv); mod->irq_empty = usbhsf_irq_empty; mod->irq_ready = usbhsf_irq_ready; @@ -584,6 +929,19 @@ void usbhs_fifo_init(struct usbhs_priv *priv) mod->irq_brdysts = 0; cfifo->pipe = NULL; + cfifo->tx_chan = NULL; + cfifo->rx_chan = NULL; + + d0fifo->pipe = NULL; + d0fifo->tx_chan = NULL; + d0fifo->rx_chan = NULL; + + d1fifo->pipe = NULL; + d1fifo->tx_chan = NULL; + d1fifo->rx_chan = NULL; + + usbhsf_dma_init(priv, usbhsf_get_d0fifo(priv)); + usbhsf_dma_init(priv, usbhsf_get_d1fifo(priv)); } void usbhs_fifo_quit(struct usbhs_priv *priv) @@ -594,6 +952,9 @@ void usbhs_fifo_quit(struct usbhs_priv *priv) mod->irq_ready = NULL; mod->irq_bempsts = 0; mod->irq_brdysts = 0; + + usbhsf_dma_quit(priv, usbhsf_get_d0fifo(priv)); + usbhsf_dma_quit(priv, usbhsf_get_d1fifo(priv)); } int usbhs_fifo_probe(struct usbhs_priv *priv) @@ -602,10 +963,29 @@ int usbhs_fifo_probe(struct usbhs_priv *priv) /* CFIFO */ fifo = usbhsf_get_cfifo(priv); + fifo->name = "CFIFO"; fifo->port = CFIFO; fifo->sel = CFIFOSEL; fifo->ctr = CFIFOCTR; + /* D0FIFO */ + fifo = usbhsf_get_d0fifo(priv); + fifo->name = "D0FIFO"; + fifo->port = D0FIFO; + fifo->sel = D0FIFOSEL; + fifo->ctr = D0FIFOCTR; + fifo->tx_slave.slave_id = usbhs_get_dparam(priv, d0_tx_id); + fifo->rx_slave.slave_id = usbhs_get_dparam(priv, d0_rx_id); + + /* D1FIFO */ + fifo = usbhsf_get_d1fifo(priv); + fifo->name = "D1FIFO"; + fifo->port = D1FIFO; + fifo->sel = D1FIFOSEL; + fifo->ctr = D1FIFOCTR; + fifo->tx_slave.slave_id = usbhs_get_dparam(priv, d1_tx_id); + fifo->rx_slave.slave_id = usbhs_get_dparam(priv, d1_rx_id); + return 0; } diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 94db269f84c2..ed6d8e56c13c 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -17,18 +17,33 @@ #ifndef RENESAS_USB_FIFO_H #define RENESAS_USB_FIFO_H +#include +#include +#include #include "pipe.h" +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + struct usbhs_fifo { + char *name; u32 port; /* xFIFO */ u32 sel; /* xFIFOSEL */ u32 ctr; /* xFIFOCTR */ struct usbhs_pipe *pipe; + struct tasklet_struct tasklet; + + struct dma_chan *tx_chan; + struct dma_chan *rx_chan; + + struct sh_dmae_slave tx_slave; + struct sh_dmae_slave rx_slave; }; struct usbhs_fifo_info { struct usbhs_fifo cfifo; + struct usbhs_fifo d0fifo; + struct usbhs_fifo d1fifo; }; struct usbhs_pkt_handle; @@ -36,8 +51,10 @@ struct usbhs_pkt { struct list_head node; struct usbhs_pipe *pipe; struct usbhs_pkt_handle *handler; + dma_addr_t dma; void *buf; int length; + int trans; int actual; int zero; }; @@ -45,6 +62,7 @@ struct usbhs_pkt { struct usbhs_pkt_handle { int (*prepare)(struct usbhs_pkt *pkt, int *is_done); int (*try_run)(struct usbhs_pkt *pkt, int *is_done); + int (*dma_done)(struct usbhs_pkt *pkt, int *is_done); }; /* @@ -61,12 +79,17 @@ void usbhs_fifo_quit(struct usbhs_priv *priv); enum { USBHSF_PKT_PREPARE, USBHSF_PKT_TRY_RUN, + USBHSF_PKT_DMA_DONE, }; extern struct usbhs_pkt_handle usbhs_fifo_pio_push_handler; extern struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler; extern struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler; +extern struct usbhs_pkt_handle usbhs_fifo_dma_push_handler; +extern struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler; + + void usbhs_pkt_init(struct usbhs_pkt *pkt); void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, struct usbhs_pkt_handle *handler, @@ -76,5 +99,6 @@ int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type); #define usbhs_pkt_start(p) __usbhs_pkt_handler(p, USBHSF_PKT_PREPARE) #define usbhs_pkt_run(p) __usbhs_pkt_handler(p, USBHSF_PKT_TRY_RUN) +#define usbhs_pkt_dmadone(p) __usbhs_pkt_handler(p, USBHSF_PKT_DMA_DONE) #endif /* RENESAS_USB_FIFO_H */ diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 89d2b16fbb10..31d28dc78aa3 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -160,6 +160,71 @@ static void usbhsg_queue_done(struct usbhs_pkt *pkt) usbhsg_queue_pop(uep, ureq, 0); } +static int usbhsg_dma_map(struct device *dev, + struct usbhs_pkt *pkt, + enum dma_data_direction dir) +{ + struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt); + struct usb_request *req = &ureq->req; + + if (pkt->dma != DMA_ADDR_INVALID) { + dev_err(dev, "dma is already mapped\n"); + return -EIO; + } + + if (req->dma == DMA_ADDR_INVALID) { + pkt->dma = dma_map_single(dev, pkt->buf, pkt->length, dir); + } else { + dma_sync_single_for_device(dev, req->dma, req->length, dir); + pkt->dma = req->dma; + } + + if (dma_mapping_error(dev, pkt->dma)) { + dev_err(dev, "dma mapping error %x\n", pkt->dma); + return -EIO; + } + + return 0; +} + +static int usbhsg_dma_unmap(struct device *dev, + struct usbhs_pkt *pkt, + enum dma_data_direction dir) +{ + struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt); + struct usb_request *req = &ureq->req; + + if (pkt->dma == DMA_ADDR_INVALID) { + dev_err(dev, "dma is not mapped\n"); + return -EIO; + } + + if (req->dma == DMA_ADDR_INVALID) + dma_unmap_single(dev, pkt->dma, pkt->length, dir); + else + dma_sync_single_for_cpu(dev, req->dma, req->length, dir); + + pkt->dma = DMA_ADDR_INVALID; + + return 0; +} + +static int usbhsg_dma_map_ctrl(struct usbhs_pkt *pkt, int map) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe); + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + enum dma_data_direction dir; + + dir = usbhs_pipe_is_dir_in(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + if (map) + return usbhsg_dma_map(dev, pkt, dir); + else + return usbhsg_dma_unmap(dev, pkt, dir); +} + /* * USB_TYPE_STANDARD / clear feature functions */ @@ -434,6 +499,8 @@ static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep, usbhs_pkt_init(usbhsg_ureq_to_pkt(ureq)); + ureq->req.dma = DMA_ADDR_INVALID; + return &ureq->req; } @@ -569,7 +636,8 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) * pipe initialize and enable DCP */ usbhs_pipe_init(priv, - usbhsg_queue_done); + usbhsg_queue_done, + usbhsg_dma_map_ctrl); usbhs_fifo_init(priv); usbhsg_uep_init(gpriv); diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index c0505876fd8c..d0ae846632cd 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -532,7 +532,8 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) } void usbhs_pipe_init(struct usbhs_priv *priv, - void (*done)(struct usbhs_pkt *pkt)) + void (*done)(struct usbhs_pkt *pkt), + int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)) { struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); struct device *dev = usbhs_priv_to_dev(priv); @@ -572,6 +573,7 @@ void usbhs_pipe_init(struct usbhs_priv *priv, } info->done = done; + info->dma_map_ctrl = dma_map_ctrl; } struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 484adbed6dfb..35e100477e55 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -44,6 +44,7 @@ struct usbhs_pipe_info { int bufnmb_last; /* FIXME : driver needs good allocator */ void (*done)(struct usbhs_pkt *pkt); + int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map); }; /* @@ -82,7 +83,8 @@ void usbhs_pipe_remove(struct usbhs_priv *priv); int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe); void usbhs_pipe_init(struct usbhs_priv *priv, - void (*done)(struct usbhs_pkt *pkt)); + void (*done)(struct usbhs_pkt *pkt), + int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)); int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe); int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe); diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 3a7f1d982dd6..8977431259c6 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -110,6 +110,23 @@ struct renesas_usbhs_driver_param { * delay time from notify_hotplug callback */ int detection_delay; + + /* + * option: + * + * dma id for dmaengine + */ + int d0_tx_id; + int d0_rx_id; + int d1_tx_id; + int d1_rx_id; + + /* + * option: + * + * pio <--> dma border. + */ + int pio_dma_border; /* default is 64byte */ }; /* -- cgit v1.2.3 From 7808edcd306f22aeb23775d34e70b7fa2f58b852 Mon Sep 17 00:00:00 2001 From: Nicos Gollan Date: Thu, 5 May 2011 21:00:37 +0200 Subject: Basic support for Moschip 9900 family I/O chips Add I/O based support for serial and parallel ports of the following chips: Vendor: Moschip (0x9710) Parts (device IDs) * 9900 (0x9900) * 9904 (0x9904 * 9901 (0x9912, also sold as 9912) * 9922 (0x9922) On all chips but the 9900, a single port is provided per PCI subdevice (subvendor-ID 0xA000, subdevice-IDs 0x1000 for serial, 0x2000 for parallel with proper class codes). In cascading configurations, the 9900 provides two devices per subdevice, with subvendor-ID 0xA000 and subdevice-IDs 0x30ps where p is the number of parallel ports and s the number of serial ports. Basic testing was only done on the serial part of a 9912 to the point where it can be used for a serial kernel console, and advanced features are completely untested. It is possible to reduce functionality of the chips by adding a configuration EEPROM, and the datasheet [1] is inconsistent w.r.t subdevices in the 4s+2s1p and 2s1p+4s configurations. The subdevice-ID 0x3012 should likely read 0x3011 with a serial port in function 3, which would be consistent with the BAR layouts. For now, the drivers ignore subdevices with ID 0x1000 and no class code. The parallel ports are integrated in parport_serial even for purely parallel parts to reduce the footprint of the patch. [1] http://www.moschip.com/data/products/MCS9900/MCS9900_Datasheet.pdf Signed-off-by: Nicos Gollan Signed-off-by: Greg Kroah-Hartman --- drivers/parport/parport_serial.c | 66 +++++++++++++++++++------ drivers/tty/serial/8250_pci.c | 104 ++++++++++++++++++++++++++++++++++++++- include/linux/pci_ids.h | 4 ++ 3 files changed, 157 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index f01e26440f11..342a3de6c674 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -33,6 +33,9 @@ enum parport_pc_pci_cards { netmos_9xx5_combo, netmos_9855, netmos_9855_2p, + netmos_9900, + netmos_9900_2p, + netmos_99xx_1p, avlab_1s1p, avlab_1s2p, avlab_2s1p, @@ -72,22 +75,20 @@ static int __devinit netmos_parallel_init(struct pci_dev *dev, struct parport_pc dev->subsystem_vendor == PCI_VENDOR_ID_IBM && dev->subsystem_device == 0x0299) return -ENODEV; - /* - * Netmos uses the subdevice ID to indicate the number of parallel - * and serial ports. The form is 0x00PS, where

is the number of - * parallel ports and is the number of serial ports. - */ - par->numports = (dev->subsystem_device & 0xf0) >> 4; - if (par->numports > ARRAY_SIZE(par->addr)) - par->numports = ARRAY_SIZE(par->addr); - /* - * This function is currently only called for cards with up to - * one parallel port. - * Parallel port BAR is either before or after serial ports BARS; - * hence, lo should be either 0 or equal to the number of serial ports. - */ - if (par->addr[0].lo != 0) - par->addr[0].lo = dev->subsystem_device & 0xf; + + if (dev->device == PCI_DEVICE_ID_NETMOS_9912) { + par->numports = 1; + } else { + /* + * Netmos uses the subdevice ID to indicate the number of parallel + * and serial ports. The form is 0x00PS, where

is the number of + * parallel ports and is the number of serial ports. + */ + par->numports = (dev->subsystem_device & 0xf0) >> 4; + if (par->numports > ARRAY_SIZE(par->addr)) + par->numports = ARRAY_SIZE(par->addr); + } + return 0; } @@ -97,6 +98,9 @@ static struct parport_pc_pci cards[] __devinitdata = { /* netmos_9xx5_combo */ { 1, { { 2, -1 }, }, netmos_parallel_init }, /* netmos_9855 */ { 1, { { 0, -1 }, }, netmos_parallel_init }, /* netmos_9855_2p */ { 2, { { 0, -1 }, { 2, -1 }, } }, + /* netmos_9900 */ {1, { { 3, 4 }, }, netmos_parallel_init }, + /* netmos_9900_2p */ {2, { { 0, 1 }, { 3, 4 }, } }, + /* netmos_99xx_1p */ {1, { { 0, 1 }, } }, /* avlab_1s1p */ { 1, { { 1, 2}, } }, /* avlab_1s2p */ { 2, { { 1, 2}, { 3, 4 },} }, /* avlab_2s1p */ { 1, { { 2, 3}, } }, @@ -127,6 +131,14 @@ static struct pci_device_id parport_serial_pci_tbl[] = { 0x1000, 0x0022, 0, 0, netmos_9855_2p }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3011, 0, 0, netmos_9900 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3012, 0, 0, netmos_9900 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3020, 0, 0, netmos_9900_2p }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, + 0xA000, 0x2000, 0, 0, netmos_99xx_1p }, /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/ { PCI_VENDOR_ID_AFAVLAB, 0x2110, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p }, @@ -219,6 +231,24 @@ static struct pciserial_board pci_parport_serial_boards[] __devinitdata = { .base_baud = 115200, .uart_offset = 8, }, + [netmos_9900] = { /* n/t */ + .flags = FL_BASE0 | FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [netmos_9900_2p] = { /* parallel only */ /* n/t */ + .flags = FL_BASE0, + .num_ports = 0, + .base_baud = 115200, + .uart_offset = 8, + }, + [netmos_99xx_1p] = { /* parallel only */ /* n/t */ + .flags = FL_BASE0, + .num_ports = 0, + .base_baud = 115200, + .uart_offset = 8, + }, [avlab_1s1p] = { /* n/t */ .flags = FL_BASE0 | FL_BASE_BARS, .num_ports = 1, @@ -285,6 +315,10 @@ static int __devinit serial_register (struct pci_dev *dev, struct serial_private *serial; board = &pci_parport_serial_boards[id->driver_data]; + + if (board->num_ports == 0) + return 0; + serial = pciserial_init_ports(dev, board); if (IS_ERR(serial)) diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index 4b4968a294b2..0b255cef57e0 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -56,6 +56,9 @@ struct serial_private { int line[0]; }; +static int pci_default_setup(struct serial_private*, + const struct pciserial_board*, struct uart_port*, int); + static void moan_device(const char *str, struct pci_dev *dev) { printk(KERN_WARNING @@ -752,6 +755,62 @@ pci_ni8430_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, board->reg_shift); } +static int pci_netmos_9900_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar; + + if ((priv->dev->subsystem_device & 0xff00) == 0x3000) { + /* netmos apparently orders BARs by datasheet layout, so serial + * ports get BARs 0 and 3 (or 1 and 4 for memmapped) + */ + bar = 3 * idx; + + return setup_port(priv, port, bar, 0, board->reg_shift); + } else { + return pci_default_setup(priv, board, port, idx); + } +} + +/* the 99xx series comes with a range of device IDs and a variety + * of capabilities: + * + * 9900 has varying capabilities and can cascade to sub-controllers + * (cascading should be purely internal) + * 9904 is hardwired with 4 serial ports + * 9912 and 9922 are hardwired with 2 serial ports + */ +static int pci_netmos_9900_numports(struct pci_dev *dev) +{ + unsigned int c = dev->class; + unsigned int pi; + unsigned short sub_serports; + + pi = (c & 0xff); + + if (pi == 2) { + return 1; + } else if ((pi == 0) && + (dev->device == PCI_DEVICE_ID_NETMOS_9900)) { + /* two possibilities: 0x30ps encodes number of parallel and + * serial ports, or 0x1000 indicates *something*. This is not + * immediately obvious, since the 2s1p+4s configuration seems + * to offer all functionality on functions 0..2, while still + * advertising the same function 3 as the 4s+2s1p config. + */ + sub_serports = dev->subsystem_device & 0xf; + if (sub_serports > 0) { + return sub_serports; + } else { + printk(KERN_NOTICE "NetMos/Mostech serial driver ignoring port on ambiguous config.\n"); + return 0; + } + } + + moan_device("unknown NetMos/Mostech program interface", dev); + return 0; +} static int pci_netmos_init(struct pci_dev *dev) { @@ -761,12 +820,28 @@ static int pci_netmos_init(struct pci_dev *dev) if ((dev->device == PCI_DEVICE_ID_NETMOS_9901) || (dev->device == PCI_DEVICE_ID_NETMOS_9865)) return 0; + if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && dev->subsystem_device == 0x0299) return 0; + switch (dev->device) { /* FALLTHROUGH on all */ + case PCI_DEVICE_ID_NETMOS_9904: + case PCI_DEVICE_ID_NETMOS_9912: + case PCI_DEVICE_ID_NETMOS_9922: + case PCI_DEVICE_ID_NETMOS_9900: + num_serial = pci_netmos_9900_numports(dev); + break; + + default: + if (num_serial == 0 ) { + moan_device("unknown NetMos/Mostech device", dev); + } + } + if (num_serial == 0) return -ENODEV; + return num_serial; } @@ -1417,7 +1492,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .init = pci_netmos_init, - .setup = pci_default_setup, + .setup = pci_netmos_9900_setup, }, /* * For Oxford Semiconductor Tornado based devices @@ -1644,6 +1719,7 @@ enum pci_board_num_t { pbn_ADDIDATA_PCIe_8_3906250, pbn_ce4100_1_115200, pbn_omegapci, + pbn_NETMOS9900_2s_115200, }; /* @@ -2345,6 +2421,11 @@ static struct pciserial_board pci_boards[] __devinitdata = { .base_baud = 115200, .uart_offset = 0x200, }, + [pbn_NETMOS9900_2s_115200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + }, }; static const struct pci_device_id softmodem_blacklist[] = { @@ -3826,6 +3907,27 @@ static struct pci_device_id serial_pci_tbl[] = { 0xA000, 0x1000, 0, 0, pbn_b0_1_115200 }, + /* the 9901 is a rebranded 9912 */ + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9922, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9904, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3002, + 0, 0, pbn_NETMOS9900_2s_115200 }, + /* * Best Connectivity PCI Multi I/O cards */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index a311008af5e1..034ebb4451c2 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2821,7 +2821,11 @@ #define PCI_DEVICE_ID_NETMOS_9845 0x9845 #define PCI_DEVICE_ID_NETMOS_9855 0x9855 #define PCI_DEVICE_ID_NETMOS_9865 0x9865 +#define PCI_DEVICE_ID_NETMOS_9900 0x9900 #define PCI_DEVICE_ID_NETMOS_9901 0x9901 +#define PCI_DEVICE_ID_NETMOS_9904 0x9904 +#define PCI_DEVICE_ID_NETMOS_9912 0x9912 +#define PCI_DEVICE_ID_NETMOS_9922 0x9922 #define PCI_VENDOR_ID_3COM_2 0xa727 -- cgit v1.2.3 From ae92c1f5e7b6708371365d262625ac19e67c1e79 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 24 May 2011 10:43:03 +0200 Subject: TTY: export NR_LDISC and N_* line discipline numbers to user-space Since commit (4564f9e5: consolidate line discipline number definitions) the patch moved all line discipline number from a per-architecture termios.h to a shared one: tty.h. However, prior to this consolidation work, the line discipline numbers were outside of an ifdef __KERNEL__/endif block so these numbers used to be exported to user-space. Since such numbers are kernel ABI anyway, and tty.h is already included for user- space header processing, just move these relevant defines outside of the ifdef __KERNEL__/endif block in include/linux/tty.h. CC: Maxime Bizon Signed-off-by: Florian Fainelli Acked-by: Tilman Schmidt Signed-off-by: Greg Kroah-Hartman --- include/linux/tty.h | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/tty.h b/include/linux/tty.h index d6f05292e456..44bc0c5617e1 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -5,24 +5,6 @@ * 'tty.h' defines some structures used by tty_io.c and some defines. */ -#ifdef __KERNEL__ -#include -#include -#include -#include -#include -#include -#include - -#include - - -/* - * (Note: the *_driver.minor_start values 1, 64, 128, 192 are - * hardcoded at present.) - */ -#define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */ -#define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */ #define NR_LDISCS 30 /* line disciplines */ @@ -53,6 +35,25 @@ #define N_TRACESINK 23 /* Trace data routing for MIPI P1149.7 */ #define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */ +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include + +#include + + +/* + * (Note: the *_driver.minor_start values 1, 64, 128, 192 are + * hardcoded at present.) + */ +#define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */ +#define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */ + /* * This character is the same as _POSIX_VDISABLE: it cannot be used as * a c_cc[] character, but indicates that a particular special character -- cgit v1.2.3 From f41ccd71d800c6fac18d2b4ae1830a222c6713da Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 22 May 2011 16:10:21 +0300 Subject: mac80211: Stop BA session event from device Some devices support BT/WLAN co-existence algorigthms. In order not to harm the system performance and user experience, the device requests not to allow any RX BA session and tear down existing RX BA sessions based on system constraints such as periodic BT activity that needs to limit WLAN activity (eg.SCO or A2DP). In such cases, the intention is to limit the duration of the RX PPDU and therefore prevent the peer device to use A-MPDU aggregation. Adding ieee80211_stop_rx_ba_session() callback that can be used by the driver to stop existing BA sessions. Signed-off-by: Shahar Levi Signed-off-by: John W. Linville --- include/net/mac80211.h | 17 +++++++++++++++++ net/mac80211/agg-rx.c | 15 +++++++++++++++ net/mac80211/ht.c | 6 ++++++ net/mac80211/sta_info.h | 3 +++ 4 files changed, 41 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b250c6303d6f..3b31ec95dd8e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2969,6 +2969,23 @@ void ieee80211_ready_on_channel(struct ieee80211_hw *hw); */ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw); +/** + * ieee80211_stop_rx_ba_session - callback to stop existing BA sessions + * + * in order not to harm the system performance and user experience, the device + * may request not to allow any rx ba session and tear down existing rx ba + * sessions based on system constraints such as periodic BT activity that needs + * to limit wlan activity (eg.sco or a2dp)." + * in such cases, the intention is to limit the duration of the rx ppdu and + * therefore prevent the peer device to use a-mpdu aggregation. + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @ba_rx_bitmap: Bit map of open rx ba per tid + * @addr: & to bssid mac address + */ +void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, + const u8 *addr); + /* Rate control API */ /** diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 9c0d76cdca92..89b0b2ca6db6 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -100,6 +100,21 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, mutex_unlock(&sta->ampdu_mlme.mtx); } +void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, + const u8 *addr) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct sta_info *sta = sta_info_get(sdata, addr); + int i; + + for (i = 0; i < STA_TID_NUM; i++) + if (ba_rx_bitmap & BIT(i)) + set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested); + + ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); +} +EXPORT_SYMBOL(ieee80211_stop_rx_ba_session); + /* * After accepting the AddBA Request we activated a timer, * resetting it after each frame that arrives from the originator. diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 591add22bcc0..7cfc286946c0 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -140,6 +140,12 @@ void ieee80211_ba_session_work(struct work_struct *work) sta, tid, WLAN_BACK_RECIPIENT, WLAN_REASON_QSTA_TIMEOUT, true); + if (test_and_clear_bit(tid, + sta->ampdu_mlme.tid_rx_stop_requested)) + ___ieee80211_stop_rx_ba_session( + sta, tid, WLAN_BACK_RECIPIENT, + WLAN_REASON_UNSPECIFIED, true); + tid_tx = sta->ampdu_mlme.tid_start_tx[tid]; if (tid_tx) { /* diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index c6ae8718bd57..a06d64ebc177 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -158,6 +158,8 @@ struct tid_ampdu_rx { * @work: work struct for starting/stopping aggregation * @tid_rx_timer_expired: bitmap indicating on which TIDs the * RX timer expired until the work for it runs + * @tid_rx_stop_requested: bitmap indicating which BA sessions per TID the + * driver requested to close until the work for it runs * @mtx: mutex to protect all TX data (except non-NULL assignments * to tid_tx[idx], which are protected by the sta spinlock) */ @@ -166,6 +168,7 @@ struct sta_ampdu_mlme { /* rx */ struct tid_ampdu_rx __rcu *tid_rx[STA_TID_NUM]; unsigned long tid_rx_timer_expired[BITS_TO_LONGS(STA_TID_NUM)]; + unsigned long tid_rx_stop_requested[BITS_TO_LONGS(STA_TID_NUM)]; /* tx */ struct work_struct work; struct tid_ampdu_tx __rcu *tid_tx[STA_TID_NUM]; -- cgit v1.2.3 From ea2c00095c022846dd8dfd211de05154d3e4e1b8 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Tue, 17 May 2011 15:25:37 -0700 Subject: Connector: Set the CN_NETLINK_USERS correctly The CN_NETLINK_USERS must be set to the highest valid index +1. Thanks to Evgeniy for pointing this out. Signed-off-by: K. Y. Srinivasan Acked-by: Evgeniy Polyakov Cc: stable [.39] Signed-off-by: Greg Kroah-Hartman --- include/linux/connector.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/connector.h b/include/linux/connector.h index 7c60d0942adb..f696bccd48cb 100644 --- a/include/linux/connector.h +++ b/include/linux/connector.h @@ -44,7 +44,7 @@ #define CN_VAL_DRBD 0x1 #define CN_KVP_IDX 0x9 /* HyperV KVP */ -#define CN_NETLINK_USERS 9 +#define CN_NETLINK_USERS 10 /* Highest index + 1 */ /* * Maximum connector's message size. -- cgit v1.2.3 From 85ab9ee946da58f26d8a1d7b71664748bdc0f3b0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 7 Jun 2011 12:19:14 -0700 Subject: Staging: altera: move .h file to proper place Staging drivers should be self-contained, without files in the include/ directories. So move the altera.h file back to the driver directory for now, until it moves out of the staging tree. Cc: Igor M. Liplianin Cc: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/staging/altera-stapl/altera-jtag.c | 2 +- drivers/staging/altera-stapl/altera.c | 2 +- drivers/staging/altera-stapl/altera.h | 49 ++++++++++++++++++++++++++++++ include/staging/altera.h | 49 ------------------------------ 4 files changed, 51 insertions(+), 51 deletions(-) create mode 100644 drivers/staging/altera-stapl/altera.h delete mode 100644 include/staging/altera.h (limited to 'include') diff --git a/drivers/staging/altera-stapl/altera-jtag.c b/drivers/staging/altera-stapl/altera-jtag.c index 876308858b82..8b1620b1b2d0 100644 --- a/drivers/staging/altera-stapl/altera-jtag.c +++ b/drivers/staging/altera-stapl/altera-jtag.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include "altera.h" #include "altera-exprt.h" #include "altera-jtag.h" diff --git a/drivers/staging/altera-stapl/altera.c b/drivers/staging/altera-stapl/altera.c index 05aad351b120..9cd5e76880c0 100644 --- a/drivers/staging/altera-stapl/altera.c +++ b/drivers/staging/altera-stapl/altera.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include "altera.h" #include "altera-exprt.h" #include "altera-jtag.h" diff --git a/drivers/staging/altera-stapl/altera.h b/drivers/staging/altera-stapl/altera.h new file mode 100644 index 000000000000..94c0c6181daf --- /dev/null +++ b/drivers/staging/altera-stapl/altera.h @@ -0,0 +1,49 @@ +/* + * altera.h + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * 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 _ALTERA_H_ +#define _ALTERA_H_ + +struct altera_config { + void *dev; + u8 *action; + int (*jtag_io) (void *dev, int tms, int tdi, int tdo); +}; + +#if defined(CONFIG_ALTERA_STAPL) || \ + (defined(CONFIG_ALTERA_STAPL_MODULE) && defined(MODULE)) + +extern int altera_init(struct altera_config *config, const struct firmware *fw); +#else + +static inline int altera_init(struct altera_config *config, + const struct firmware *fw) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} +#endif /* CONFIG_ALTERA_STAPL */ + +#endif /* _ALTERA_H_ */ diff --git a/include/staging/altera.h b/include/staging/altera.h deleted file mode 100644 index 94c0c6181daf..000000000000 --- a/include/staging/altera.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * altera.h - * - * altera FPGA driver - * - * Copyright (C) Altera Corporation 1998-2001 - * Copyright (C) 2010 NetUP Inc. - * Copyright (C) 2010 Igor M. Liplianin - * - * 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 _ALTERA_H_ -#define _ALTERA_H_ - -struct altera_config { - void *dev; - u8 *action; - int (*jtag_io) (void *dev, int tms, int tdi, int tdo); -}; - -#if defined(CONFIG_ALTERA_STAPL) || \ - (defined(CONFIG_ALTERA_STAPL_MODULE) && defined(MODULE)) - -extern int altera_init(struct altera_config *config, const struct firmware *fw); -#else - -static inline int altera_init(struct altera_config *config, - const struct firmware *fw) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return 0; -} -#endif /* CONFIG_ALTERA_STAPL */ - -#endif /* _ALTERA_H_ */ -- cgit v1.2.3 From 98d9f30c820d509145757e6ecbc36013aa02f7bc Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 11 Apr 2011 11:37:07 +1000 Subject: pci/of: Match PCI devices to OF nodes dynamically powerpc has two different ways of matching PCI devices to their corresponding OF node (if any) for historical reasons. The ppc64 one does a scan looking for matching bus/dev/fn, while the ppc32 one does a scan looking only for matching dev/fn on each level in order to be agnostic to busses being renumbered (which Linux does on some platforms). This removes both and instead moves the matching code to the PCI core itself. It's the most logical place to do it: when a pci_dev is created, we know the parent and thus can do a single level scan for the matching device_node (if any). The benefit is that all archs now get the matching for free. There's one hook the arch might want to provide to match a PHB bus to its device node. A default weak implementation is provided that looks for the parent device device node, but it's not entirely reliable on powerpc for various reasons so powerpc provides its own. Signed-off-by: Benjamin Herrenschmidt Acked-by: Michal Simek Acked-by: Jesse Barnes --- arch/microblaze/include/asm/pci-bridge.h | 14 +-- arch/microblaze/include/asm/prom.h | 15 ---- arch/microblaze/pci/pci_32.c | 40 ++------- arch/powerpc/include/asm/pci-bridge.h | 35 +++----- arch/powerpc/include/asm/pci.h | 3 +- arch/powerpc/include/asm/prom.h | 14 --- arch/powerpc/kernel/pci-common.c | 11 ++- arch/powerpc/kernel/pci_32.c | 150 +++---------------------------- arch/powerpc/kernel/pci_dn.c | 47 ---------- arch/powerpc/kernel/pci_of_scan.c | 9 +- arch/powerpc/platforms/powermac/pci.c | 3 +- arch/sparc/kernel/pci.c | 2 +- drivers/of/Kconfig | 8 +- drivers/of/Makefile | 1 + drivers/of/of_pci.c | 112 +++++++---------------- drivers/of/of_pci_irq.c | 92 +++++++++++++++++++ drivers/pci/Makefile | 2 + drivers/pci/hotplug/rpadlpar_core.c | 2 +- drivers/pci/of.c | 61 +++++++++++++ drivers/pci/probe.c | 7 +- include/linux/of_pci.h | 5 ++ include/linux/pci.h | 18 ++++ 22 files changed, 275 insertions(+), 376 deletions(-) create mode 100644 drivers/of/of_pci_irq.c create mode 100644 drivers/pci/of.c (limited to 'include') diff --git a/arch/microblaze/include/asm/pci-bridge.h b/arch/microblaze/include/asm/pci-bridge.h index 746df91e5796..728f8d6a59a4 100644 --- a/arch/microblaze/include/asm/pci-bridge.h +++ b/arch/microblaze/include/asm/pci-bridge.h @@ -105,19 +105,19 @@ struct pci_controller { }; #ifdef CONFIG_PCI -static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus) +static inline struct device_node *pci_device_to_OF_node(struct pci_dev *dev) { - return bus->sysdata; + return dev->dev.of_node; } static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) { - struct pci_controller *host; + return bus->dev.of_node; +} - if (bus->self) - return pci_device_to_OF_node(bus->self); - host = pci_bus_to_host(bus); - return host ? host->dn : NULL; +static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus) +{ + return bus->sysdata; } static inline int isa_vaddr_is_ioport(void __iomem *address) diff --git a/arch/microblaze/include/asm/prom.h b/arch/microblaze/include/asm/prom.h index d0890d36ef61..9bd01ecb00d6 100644 --- a/arch/microblaze/include/asm/prom.h +++ b/arch/microblaze/include/asm/prom.h @@ -29,21 +29,6 @@ extern int early_uartlite_console(void); extern int early_uart16550_console(void); -#ifdef CONFIG_PCI -/* - * PCI <-> OF matching functions - * (XXX should these be here?) - */ -struct pci_bus; -struct pci_dev; -extern int pci_device_from_OF_node(struct device_node *node, - u8 *bus, u8 *devfn); -extern struct device_node *pci_busdev_to_OF_node(struct pci_bus *bus, - int devfn); -extern struct device_node *pci_device_to_OF_node(struct pci_dev *dev); -extern void pci_create_OF_bus_map(void); -#endif - /* * OF address retreival & translation */ diff --git a/arch/microblaze/pci/pci_32.c b/arch/microblaze/pci/pci_32.c index 92728a6cfd80..2fa95069e6ba 100644 --- a/arch/microblaze/pci/pci_32.c +++ b/arch/microblaze/pci/pci_32.c @@ -210,38 +210,6 @@ static struct device_node *scan_OF_for_pci_bus(struct pci_bus *bus) return np; } -/* - * Scans the OF tree for a device node matching a PCI device - */ -struct device_node * -pci_busdev_to_OF_node(struct pci_bus *bus, int devfn) -{ - struct device_node *parent, *np; - - pr_debug("pci_busdev_to_OF_node(%d,0x%x)\n", bus->number, devfn); - parent = scan_OF_for_pci_bus(bus); - if (parent == NULL) - return NULL; - pr_debug(" parent is %s\n", parent ? parent->full_name : ""); - np = scan_OF_for_pci_dev(parent, devfn); - of_node_put(parent); - pr_debug(" result is %s\n", np ? np->full_name : ""); - - /* XXX most callers don't release the returned node - * mostly because ppc64 doesn't increase the refcount, - * we need to fix that. - */ - return np; -} -EXPORT_SYMBOL(pci_busdev_to_OF_node); - -struct device_node* -pci_device_to_OF_node(struct pci_dev *dev) -{ - return pci_busdev_to_OF_node(dev->bus, dev->devfn); -} -EXPORT_SYMBOL(pci_device_to_OF_node); - static int find_OF_pci_device_filter(struct device_node *node, void *data) { @@ -315,6 +283,13 @@ pci_create_OF_bus_map(void) } } +struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus) +{ + struct pci_controller *hose = bus->sysdata; + + return of_node_get(hose->dn); +} + static void __devinit pcibios_scan_phb(struct pci_controller *hose) { struct pci_bus *bus; @@ -332,7 +307,6 @@ static void __devinit pcibios_scan_phb(struct pci_controller *hose) hose->global_number); return; } - bus.dev->of_node = of_node_get(node); bus->secondary = hose->first_busno; hose->bus = bus; diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index b90dbf8e5cd9..3e6869476e55 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -169,18 +169,22 @@ static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus) return bus->sysdata; } -#ifndef CONFIG_PPC64 +static inline struct device_node *pci_device_to_OF_node(struct pci_dev *dev) +{ + return dev->dev.of_node; +} static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) { - struct pci_controller *host; - - if (bus->self) - return pci_device_to_OF_node(bus->self); - host = pci_bus_to_host(bus); - return host ? host->dn : NULL; + return bus->dev.of_node; } +#ifndef CONFIG_PPC64 + +extern int pci_device_from_OF_node(struct device_node *node, + u8 *bus, u8 *devfn); +extern void pci_create_OF_bus_map(void); + static inline int isa_vaddr_is_ioport(void __iomem *address) { /* No specific ISA handling on ppc32 at this stage, it @@ -223,17 +227,8 @@ struct pci_dn { /* Get the pointer to a device_node's pci_dn */ #define PCI_DN(dn) ((struct pci_dn *) (dn)->data) -extern struct device_node *fetch_dev_dn(struct pci_dev *dev); extern void * update_dn_pci_info(struct device_node *dn, void *data); -/* Get a device_node from a pci_dev. This code must be fast except - * in the case where the sysdata is incorrect and needs to be fixed - * up (this will only happen once). */ -static inline struct device_node *pci_device_to_OF_node(struct pci_dev *dev) -{ - return dev->dev.of_node ? dev->dev.of_node : fetch_dev_dn(dev); -} - static inline int pci_device_from_OF_node(struct device_node *np, u8 *bus, u8 *devfn) { @@ -244,14 +239,6 @@ static inline int pci_device_from_OF_node(struct device_node *np, return 0; } -static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) -{ - if (bus->self) - return pci_device_to_OF_node(bus->self); - else - return bus->dev.of_node; /* Must be root bus (PHB) */ -} - /** Find the bus corresponding to the indicated device node */ extern struct pci_bus *pcibios_find_pci_bus(struct device_node *dn); diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 7d7790954e02..1f522680ea17 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -179,8 +179,7 @@ extern int remove_phb_dynamic(struct pci_controller *phb); extern struct pci_dev *of_create_pci_dev(struct device_node *node, struct pci_bus *bus, int devfn); -extern void of_scan_pci_bridge(struct device_node *node, - struct pci_dev *dev); +extern void of_scan_pci_bridge(struct pci_dev *dev); extern void of_scan_bus(struct device_node *node, struct pci_bus *bus); extern void of_rescan_bus(struct device_node *node, struct pci_bus *bus); diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index c189aa5fe1f4..b823536375dc 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -22,20 +22,6 @@ #define HAVE_ARCH_DEVTREE_FIXUPS -#ifdef CONFIG_PPC32 -/* - * PCI <-> OF matching functions - * (XXX should these be here?) - */ -struct pci_bus; -struct pci_dev; -extern int pci_device_from_OF_node(struct device_node *node, - u8* bus, u8* devfn); -extern struct device_node* pci_busdev_to_OF_node(struct pci_bus *, int); -extern struct device_node* pci_device_to_OF_node(struct pci_dev *); -extern void pci_create_OF_bus_map(void); -#endif - /* * OF address retreival & translation */ diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 893af2a9cd03..a3c92770e422 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1097,9 +1097,6 @@ void __devinit pcibios_setup_bus_devices(struct pci_bus *bus) if (dev->is_added) continue; - /* Setup OF node pointer in the device */ - dev->dev.of_node = pci_device_to_OF_node(dev); - /* Fixup NUMA node as it may not be setup yet by the generic * code and is needed by the DMA init */ @@ -1685,6 +1682,13 @@ int early_find_capability(struct pci_controller *hose, int bus, int devfn, return pci_bus_find_capability(fake_pci_bus(hose, bus), devfn, cap); } +struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus) +{ + struct pci_controller *hose = bus->sysdata; + + return of_node_get(hose->dn); +} + /** * pci_scan_phb - Given a pci_controller, setup and scan the PCI bus * @hose: Pointer to the PCI host controller instance structure @@ -1705,7 +1709,6 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose) hose->global_number); return; } - bus->dev.of_node = of_node_get(node); bus->secondary = hose->first_busno; hose->bus = bus; diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c index bedb370459f2..86585508e9c1 100644 --- a/arch/powerpc/kernel/pci_32.c +++ b/arch/powerpc/kernel/pci_32.c @@ -167,150 +167,26 @@ pcibios_make_OF_bus_map(void) #endif } -typedef int (*pci_OF_scan_iterator)(struct device_node* node, void* data); - -static struct device_node* -scan_OF_pci_childs(struct device_node *parent, pci_OF_scan_iterator filter, void* data) -{ - struct device_node *node; - struct device_node* sub_node; - - for_each_child_of_node(parent, node) { - const unsigned int *class_code; - - if (filter(node, data)) { - of_node_put(node); - return node; - } - - /* For PCI<->PCI bridges or CardBus bridges, we go down - * Note: some OFs create a parent node "multifunc-device" as - * a fake root for all functions of a multi-function device, - * we go down them as well. - */ - class_code = of_get_property(node, "class-code", NULL); - if ((!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && - (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) && - strcmp(node->name, "multifunc-device")) - continue; - sub_node = scan_OF_pci_childs(node, filter, data); - if (sub_node) { - of_node_put(node); - return sub_node; - } - } - return NULL; -} - -static struct device_node *scan_OF_for_pci_dev(struct device_node *parent, - unsigned int devfn) -{ - struct device_node *np, *cnp; - const u32 *reg; - unsigned int psize; - - for_each_child_of_node(parent, np) { - reg = of_get_property(np, "reg", &psize); - if (reg && psize >= 4 && ((reg[0] >> 8) & 0xff) == devfn) - return np; - - /* Note: some OFs create a parent node "multifunc-device" as - * a fake root for all functions of a multi-function device, - * we go down them as well. */ - if (!strcmp(np->name, "multifunc-device")) { - cnp = scan_OF_for_pci_dev(np, devfn); - if (cnp) - return cnp; - } - } - return NULL; -} - - -static struct device_node *scan_OF_for_pci_bus(struct pci_bus *bus) -{ - struct device_node *parent, *np; - - /* Are we a root bus ? */ - if (bus->self == NULL || bus->parent == NULL) { - struct pci_controller *hose = pci_bus_to_host(bus); - if (hose == NULL) - return NULL; - return of_node_get(hose->dn); - } - - /* not a root bus, we need to get our parent */ - parent = scan_OF_for_pci_bus(bus->parent); - if (parent == NULL) - return NULL; - - /* now iterate for children for a match */ - np = scan_OF_for_pci_dev(parent, bus->self->devfn); - of_node_put(parent); - - return np; -} - -/* - * Scans the OF tree for a device node matching a PCI device - */ -struct device_node * -pci_busdev_to_OF_node(struct pci_bus *bus, int devfn) -{ - struct device_node *parent, *np; - - pr_debug("pci_busdev_to_OF_node(%d,0x%x)\n", bus->number, devfn); - parent = scan_OF_for_pci_bus(bus); - if (parent == NULL) - return NULL; - pr_debug(" parent is %s\n", parent ? parent->full_name : ""); - np = scan_OF_for_pci_dev(parent, devfn); - of_node_put(parent); - pr_debug(" result is %s\n", np ? np->full_name : ""); - - /* XXX most callers don't release the returned node - * mostly because ppc64 doesn't increase the refcount, - * we need to fix that. - */ - return np; -} -EXPORT_SYMBOL(pci_busdev_to_OF_node); - -struct device_node* -pci_device_to_OF_node(struct pci_dev *dev) -{ - return pci_busdev_to_OF_node(dev->bus, dev->devfn); -} -EXPORT_SYMBOL(pci_device_to_OF_node); - -static int -find_OF_pci_device_filter(struct device_node* node, void* data) -{ - return ((void *)node == data); -} /* * Returns the PCI device matching a given OF node */ -int -pci_device_from_OF_node(struct device_node* node, u8* bus, u8* devfn) +int pci_device_from_OF_node(struct device_node *node, u8 *bus, u8 *devfn) { - const unsigned int *reg; - struct pci_controller* hose; - struct pci_dev* dev = NULL; - - /* Make sure it's really a PCI device */ - hose = pci_find_hose_for_OF_device(node); - if (!hose || !hose->dn) - return -ENODEV; - if (!scan_OF_pci_childs(hose->dn, - find_OF_pci_device_filter, (void *)node)) + struct pci_dev *dev = NULL; + const __be32 *reg; + int size; + + /* Check if it might have a chance to be a PCI device */ + if (!pci_find_hose_for_OF_device(node)) return -ENODEV; - reg = of_get_property(node, "reg", NULL); - if (!reg) + + reg = of_get_property(node, "reg", &size); + if (!reg || size < 5 * sizeof(u32)) return -ENODEV; - *bus = (reg[0] >> 16) & 0xff; - *devfn = ((reg[0] >> 8) & 0xff); + + *bus = (be32_to_cpup(®[0]) >> 16) & 0xff; + *devfn = (be32_to_cpup(®[0]) >> 8) & 0xff; /* Ok, here we need some tweak. If we have already renumbered * all busses, we can't rely on the OF bus number any more. diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index 6baabc13306a..478f8d78716b 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -142,53 +142,6 @@ void __devinit pci_devs_phb_init_dynamic(struct pci_controller *phb) traverse_pci_devices(dn, update_dn_pci_info, phb); } -/* - * Traversal func that looks for a value. - * If found, the pci_dn is returned (thus terminating the traversal). - */ -static void *is_devfn_node(struct device_node *dn, void *data) -{ - int busno = ((unsigned long)data >> 8) & 0xff; - int devfn = ((unsigned long)data) & 0xff; - struct pci_dn *pci = dn->data; - - if (pci && (devfn == pci->devfn) && (busno == pci->busno)) - return dn; - return NULL; -} - -/* - * This is the "slow" path for looking up a device_node from a - * pci_dev. It will hunt for the device under its parent's - * phb and then update of_node pointer. - * - * It may also do fixups on the actual device since this happens - * on the first read/write. - * - * Note that it also must deal with devices that don't exist. - * In this case it may probe for real hardware ("just in case") - * and add a device_node to the device tree if necessary. - * - * Is this function necessary anymore now that dev->dev.of_node is - * used to store the node pointer? - * - */ -struct device_node *fetch_dev_dn(struct pci_dev *dev) -{ - struct pci_controller *phb = dev->sysdata; - struct device_node *dn; - unsigned long searchval = (dev->bus->number << 8) | dev->devfn; - - if (WARN_ON(!phb)) - return NULL; - - dn = traverse_pci_devices(phb->dn, is_devfn_node, (void *)searchval); - if (dn) - dev->dev.of_node = dn; - return dn; -} -EXPORT_SYMBOL(fetch_dev_dn); - /** * pci_devs_phb_init - Initialize phbs and pci devs under them. * diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index 1e89a72fd030..fe0a5ad6f73e 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -202,9 +202,9 @@ EXPORT_SYMBOL(of_create_pci_dev); * this routine in turn call of_scan_bus() recusively to scan for more child * devices. */ -void __devinit of_scan_pci_bridge(struct device_node *node, - struct pci_dev *dev) +void __devinit of_scan_pci_bridge(struct pci_dev *dev) { + struct device_node *node = dev->dev.of_node; struct pci_bus *bus; const u32 *busrange, *ranges; int len, i, mode; @@ -238,7 +238,6 @@ void __devinit of_scan_pci_bridge(struct device_node *node, bus->primary = dev->bus->number; bus->subordinate = busrange[1]; bus->bridge_ctl = 0; - bus->dev.of_node = of_node_get(node); /* parse ranges property */ /* PCI #address-cells == 3 and #size-cells == 2 always */ @@ -335,9 +334,7 @@ static void __devinit __of_scan_bus(struct device_node *node, list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) { - struct device_node *child = pci_device_to_OF_node(dev); - if (child) - of_scan_pci_bridge(child, dev); + of_scan_pci_bridge(dev); } } } diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index f33e08d573ce..abe8d7e2ebeb 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -235,7 +236,7 @@ static int chaos_validate_dev(struct pci_bus *bus, int devfn, int offset) if (offset >= 0x100) return PCIBIOS_BAD_REGISTER_NUMBER; - np = pci_busdev_to_OF_node(bus, devfn); + np = of_pci_find_child_device(bus->dev.of_node, devfn); if (np == NULL) return PCIBIOS_DEVICE_NOT_FOUND; diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index 713dc91020a6..e539d23dec9d 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -284,7 +284,7 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, dev->sysdata = node; dev->dev.parent = bus->bridge; dev->dev.bus = &pci_bus_type; - dev->dev.of_node = node; + dev->dev.of_node = of_node_get(node); dev->devfn = devfn; dev->multifunction = 0; /* maybe a lie? */ set_pcie_port_type(dev); diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index d06a6374ed6c..cac63c9f49ae 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -71,8 +71,14 @@ config OF_MDIO config OF_PCI def_tristate PCI - depends on PCI && (PPC || MICROBLAZE || X86) + depends on PCI help OpenFirmware PCI bus accessors +config OF_PCI_IRQ + def_tristate PCI + depends on OF_PCI && OF_IRQ + help + OpenFirmware PCI IRQ routing helpers + endmenu # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index f7861ed2f287..dccb1176be57 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_OF_NET) += of_net.o obj-$(CONFIG_OF_SPI) += of_spi.o obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o +obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index ac1ec54e4fd5..ec7b060ae952 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -1,92 +1,40 @@ #include #include -#include #include -/** - * of_irq_map_pci - Resolve the interrupt for a PCI device - * @pdev: the device whose interrupt is to be resolved - * @out_irq: structure of_irq filled by this function - * - * This function resolves the PCI interrupt for a given PCI device. If a - * device-node exists for a given pci_dev, it will use normal OF tree - * walking. If not, it will implement standard swizzling and walk up the - * PCI tree until an device-node is found, at which point it will finish - * resolving using the OF tree walking. - */ -int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) +static inline int __of_pci_pci_compare(struct device_node *node, + unsigned int devfn) { - struct device_node *dn, *ppnode; - struct pci_dev *ppdev; - u32 lspec; - __be32 lspec_be; - __be32 laddr[3]; - u8 pin; - int rc; + unsigned int size; + const __be32 *reg = of_get_property(node, "reg", &size); - /* Check if we have a device node, if yes, fallback to standard - * device tree parsing - */ - dn = pci_device_to_OF_node(pdev); - if (dn) { - rc = of_irq_map_one(dn, 0, out_irq); - if (!rc) - return rc; - } - - /* Ok, we don't, time to have fun. Let's start by building up an - * interrupt spec. we assume #interrupt-cells is 1, which is standard - * for PCI. If you do different, then don't use that routine. - */ - rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin); - if (rc != 0) - return rc; - /* No pin, exit */ - if (pin == 0) - return -ENODEV; - - /* Now we walk up the PCI tree */ - lspec = pin; - for (;;) { - /* Get the pci_dev of our parent */ - ppdev = pdev->bus->self; - - /* Ouch, it's a host bridge... */ - if (ppdev == NULL) { - ppnode = pci_bus_to_OF_node(pdev->bus); - - /* No node for host bridge ? give up */ - if (ppnode == NULL) - return -EINVAL; - } else { - /* We found a P2P bridge, check if it has a node */ - ppnode = pci_device_to_OF_node(ppdev); - } - - /* Ok, we have found a parent with a device-node, hand over to - * the OF parsing code. - * We build a unit address from the linux device to be used for - * resolution. Note that we use the linux bus number which may - * not match your firmware bus numbering. - * Fortunately, in most cases, interrupt-map-mask doesn't - * include the bus number as part of the matching. - * You should still be careful about that though if you intend - * to rely on this function (you ship a firmware that doesn't - * create device nodes for all PCI devices). - */ - if (ppnode) - break; + if (!reg || size < 5 * sizeof(__be32)) + return 0; + return ((be32_to_cpup(®[0]) >> 8) & 0xff) == devfn; +} - /* We can only get here if we hit a P2P bridge with no node, - * let's do standard swizzling and try again +struct device_node *of_pci_find_child_device(struct device_node *parent, + unsigned int devfn) +{ + struct device_node *node, *node2; + + for_each_child_of_node(parent, node) { + if (__of_pci_pci_compare(node, devfn)) + return node; + /* + * Some OFs create a parent node "multifunc-device" as + * a fake root for all functions of a multi-function + * device we go down them as well. */ - lspec = pci_swizzle_interrupt_pin(pdev, lspec); - pdev = ppdev; + if (!strcmp(node->name, "multifunc-device")) { + for_each_child_of_node(node, node2) { + if (__of_pci_pci_compare(node2, devfn)) { + of_node_put(node); + return node2; + } + } + } } - - lspec_be = cpu_to_be32(lspec); - laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); - laddr[1] = laddr[2] = cpu_to_be32(0); - return of_irq_map_raw(ppnode, &lspec_be, 1, laddr, out_irq); + return NULL; } -EXPORT_SYMBOL_GPL(of_irq_map_pci); +EXPORT_SYMBOL_GPL(of_pci_find_child_device); diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c new file mode 100644 index 000000000000..ac1ec54e4fd5 --- /dev/null +++ b/drivers/of/of_pci_irq.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include + +/** + * of_irq_map_pci - Resolve the interrupt for a PCI device + * @pdev: the device whose interrupt is to be resolved + * @out_irq: structure of_irq filled by this function + * + * This function resolves the PCI interrupt for a given PCI device. If a + * device-node exists for a given pci_dev, it will use normal OF tree + * walking. If not, it will implement standard swizzling and walk up the + * PCI tree until an device-node is found, at which point it will finish + * resolving using the OF tree walking. + */ +int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) +{ + struct device_node *dn, *ppnode; + struct pci_dev *ppdev; + u32 lspec; + __be32 lspec_be; + __be32 laddr[3]; + u8 pin; + int rc; + + /* Check if we have a device node, if yes, fallback to standard + * device tree parsing + */ + dn = pci_device_to_OF_node(pdev); + if (dn) { + rc = of_irq_map_one(dn, 0, out_irq); + if (!rc) + return rc; + } + + /* Ok, we don't, time to have fun. Let's start by building up an + * interrupt spec. we assume #interrupt-cells is 1, which is standard + * for PCI. If you do different, then don't use that routine. + */ + rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin); + if (rc != 0) + return rc; + /* No pin, exit */ + if (pin == 0) + return -ENODEV; + + /* Now we walk up the PCI tree */ + lspec = pin; + for (;;) { + /* Get the pci_dev of our parent */ + ppdev = pdev->bus->self; + + /* Ouch, it's a host bridge... */ + if (ppdev == NULL) { + ppnode = pci_bus_to_OF_node(pdev->bus); + + /* No node for host bridge ? give up */ + if (ppnode == NULL) + return -EINVAL; + } else { + /* We found a P2P bridge, check if it has a node */ + ppnode = pci_device_to_OF_node(ppdev); + } + + /* Ok, we have found a parent with a device-node, hand over to + * the OF parsing code. + * We build a unit address from the linux device to be used for + * resolution. Note that we use the linux bus number which may + * not match your firmware bus numbering. + * Fortunately, in most cases, interrupt-map-mask doesn't + * include the bus number as part of the matching. + * You should still be careful about that though if you intend + * to rely on this function (you ship a firmware that doesn't + * create device nodes for all PCI devices). + */ + if (ppnode) + break; + + /* We can only get here if we hit a P2P bridge with no node, + * let's do standard swizzling and try again + */ + lspec = pci_swizzle_interrupt_pin(pdev, lspec); + pdev = ppdev; + } + + lspec_be = cpu_to_be32(lspec); + laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); + laddr[1] = laddr[2] = cpu_to_be32(0); + return of_irq_map_raw(ppnode, &lspec_be, 1, laddr, out_irq); +} +EXPORT_SYMBOL_GPL(of_irq_map_pci); diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index c85f744270a5..f27f4a1488a1 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -70,4 +70,6 @@ obj-$(CONFIG_PCI_STUB) += pci-stub.o obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o +obj-$(CONFIG_OF) += of.o + ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index 083034710fa6..1d002b1c2bf4 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -158,7 +158,7 @@ static void dlpar_pci_add_bus(struct device_node *dn) /* Scan below the new bridge */ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) - of_scan_pci_bridge(dn, dev); + of_scan_pci_bridge(dev); /* Map IO space for child bus, which may or may not succeed */ pcibios_map_io_space(dev->subordinate); diff --git a/drivers/pci/of.c b/drivers/pci/of.c new file mode 100644 index 000000000000..c94d37ec55c8 --- /dev/null +++ b/drivers/pci/of.c @@ -0,0 +1,61 @@ +/* + * PCI <-> OF mapping helpers + * + * Copyright 2011 IBM Corp. + * + * 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 "pci.h" + +void pci_set_of_node(struct pci_dev *dev) +{ + if (!dev->bus->dev.of_node) + return; + dev->dev.of_node = of_pci_find_child_device(dev->bus->dev.of_node, + dev->devfn); +} + +void pci_release_of_node(struct pci_dev *dev) +{ + of_node_put(dev->dev.of_node); + dev->dev.of_node = NULL; +} + +void pci_set_bus_of_node(struct pci_bus *bus) +{ + if (bus->self == NULL) + bus->dev.of_node = pcibios_get_phb_of_node(bus); + else + bus->dev.of_node = of_node_get(bus->self->dev.of_node); +} + +void pci_release_bus_of_node(struct pci_bus *bus) +{ + of_node_put(bus->dev.of_node); + bus->dev.of_node = NULL; +} + +struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus) +{ + /* This should only be called for PHBs */ + if (WARN_ON(bus->self || bus->parent)) + return NULL; + + /* Look for a node pointer in either the intermediary device we + * create above the root bus or it's own parent. Normally only + * the later is populated. + */ + if (bus->bridge->of_node) + return of_node_get(bus->bridge->of_node); + if (bus->bridge->parent->of_node) + return of_node_get(bus->bridge->parent->of_node); + return NULL; +} diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 48849ffdd672..c28c7b91910e 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -52,6 +52,7 @@ static void release_pcibus_dev(struct device *dev) if (pci_bus->bridge) put_device(pci_bus->bridge); pci_bus_remove_resources(pci_bus); + pci_release_bus_of_node(pci_bus); kfree(pci_bus); } @@ -588,7 +589,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, child->self = bridge; child->bridge = get_device(&bridge->dev); - + pci_set_bus_of_node(child); pci_set_bus_speed(child); /* Set up default resource pointers and names.. */ @@ -1038,6 +1039,7 @@ static void pci_release_dev(struct device *dev) pci_dev = to_pci_dev(dev); pci_release_capabilities(pci_dev); + pci_release_of_node(pci_dev); kfree(pci_dev); } @@ -1157,6 +1159,8 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; + pci_set_of_node(dev); + if (pci_setup_device(dev)) { kfree(dev); return NULL; @@ -1409,6 +1413,7 @@ struct pci_bus * pci_create_bus(struct device *parent, goto dev_reg_err; b->bridge = get_device(dev); device_enable_async_suspend(b->bridge); + pci_set_bus_of_node(b); if (!parent) set_dev_node(b->bridge, pcibus_to_node(b)); diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index 85a27b650d76..f93e21700d3e 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -6,4 +6,9 @@ struct pci_dev; struct of_irq; int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq); + +struct device_node; +struct device_node *of_pci_find_child_device(struct device_node *parent, + unsigned int devfn); + #endif diff --git a/include/linux/pci.h b/include/linux/pci.h index c446b5ca2d38..e5086e9a9bf7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1589,5 +1589,23 @@ int pci_vpd_find_tag(const u8 *buf, unsigned int off, unsigned int len, u8 rdt); int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, unsigned int len, const char *kw); +/* PCI <-> OF binding helpers */ +#ifdef CONFIG_OF +struct device_node; +extern void pci_set_of_node(struct pci_dev *dev); +extern void pci_release_of_node(struct pci_dev *dev); +extern void pci_set_bus_of_node(struct pci_bus *bus); +extern void pci_release_bus_of_node(struct pci_bus *bus); + +/* Arch may override this (weak) */ +extern struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus); + +#else /* CONFIG_OF */ +static inline void pci_set_of_node(struct pci_dev *dev) { } +static inline void pci_release_of_node(struct pci_dev *dev) { } +static inline void pci_set_bus_of_node(struct pci_bus *bus) { } +static inline void pci_release_bus_of_node(struct pci_bus *bus) { } +#endif /* CONFIG_OF */ + #endif /* __KERNEL__ */ #endif /* LINUX_PCI_H */ -- cgit v1.2.3 From 64099d981c9916ec4a485b3ffbb89fa877fc595f Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 7 Apr 2011 13:09:47 +1000 Subject: pci/of: Consolidate pci_device_to_OF_node() All archs do more or less the same thing now, move it into a single generic place. I chose pci.h rather than of_pci.h to avoid having to change all call-sites to include the later. Signed-off-by: Benjamin Herrenschmidt Acked-by: Michal Simek Acked-by: Grant Likely Acked-by: Jesse Barnes --- arch/microblaze/include/asm/pci-bridge.h | 5 ----- arch/powerpc/include/asm/pci-bridge.h | 5 ----- arch/sparc/include/asm/pci_32.h | 3 --- arch/sparc/include/asm/pci_64.h | 3 --- arch/sparc/kernel/pci.c | 6 ------ arch/sparc/kernel/pcic.c | 8 -------- arch/x86/include/asm/prom.h | 5 ----- include/linux/pci.h | 5 +++++ 8 files changed, 5 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/arch/microblaze/include/asm/pci-bridge.h b/arch/microblaze/include/asm/pci-bridge.h index 6bddc0701a07..0d74d031e2a3 100644 --- a/arch/microblaze/include/asm/pci-bridge.h +++ b/arch/microblaze/include/asm/pci-bridge.h @@ -102,11 +102,6 @@ struct pci_controller { }; #ifdef CONFIG_PCI -static inline struct device_node *pci_device_to_OF_node(struct pci_dev *dev) -{ - return dev->dev.of_node; -} - static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) { return bus->dev.of_node; diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 3e6869476e55..578060ef99ad 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -169,11 +169,6 @@ static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus) return bus->sysdata; } -static inline struct device_node *pci_device_to_OF_node(struct pci_dev *dev) -{ - return dev->dev.of_node; -} - static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) { return bus->dev.of_node; diff --git a/arch/sparc/include/asm/pci_32.h b/arch/sparc/include/asm/pci_32.h index 332ac9ab36bc..4d0c39a931c5 100644 --- a/arch/sparc/include/asm/pci_32.h +++ b/arch/sparc/include/asm/pci_32.h @@ -42,9 +42,6 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev, } #endif -struct device_node; -extern struct device_node *pci_device_to_OF_node(struct pci_dev *pdev); - #endif /* __KERNEL__ */ /* generic pci stuff */ diff --git a/arch/sparc/include/asm/pci_64.h b/arch/sparc/include/asm/pci_64.h index 948b686ec089..2614d96141c9 100644 --- a/arch/sparc/include/asm/pci_64.h +++ b/arch/sparc/include/asm/pci_64.h @@ -91,9 +91,6 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) return PCI_IRQ_NONE; } -struct device_node; -extern struct device_node *pci_device_to_OF_node(struct pci_dev *pdev); - #define HAVE_ARCH_PCI_RESOURCE_TO_USER extern void pci_resource_to_user(const struct pci_dev *dev, int bar, const struct resource *rsrc, diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index e539d23dec9d..80a87e2a3e7c 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -1021,12 +1021,6 @@ void arch_teardown_msi_irq(unsigned int irq) } #endif /* !(CONFIG_PCI_MSI) */ -struct device_node *pci_device_to_OF_node(struct pci_dev *pdev) -{ - return pdev->dev.of_node; -} -EXPORT_SYMBOL(pci_device_to_OF_node); - static void ali_sound_dma_hack(struct pci_dev *pdev, int set_bit) { struct pci_dev *ali_isa_bridge; diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index 948601a066ff..a19f04195478 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -885,14 +885,6 @@ int pcibios_assign_resource(struct pci_dev *pdev, int resource) return -ENXIO; } -struct device_node *pci_device_to_OF_node(struct pci_dev *pdev) -{ - struct pcidev_cookie *pc = pdev->sysdata; - - return pc->prom_node; -} -EXPORT_SYMBOL(pci_device_to_OF_node); - /* * This probably belongs here rather than ioport.c because * we do not want this crud linked into SBus kernels. diff --git a/arch/x86/include/asm/prom.h b/arch/x86/include/asm/prom.h index 971e0b46446e..dd6066a56346 100644 --- a/arch/x86/include/asm/prom.h +++ b/arch/x86/include/asm/prom.h @@ -31,11 +31,6 @@ extern void x86_add_irq_domains(void); void __cpuinit x86_of_pci_init(void); void x86_dtb_init(void); -static inline struct device_node *pci_device_to_OF_node(struct pci_dev *pdev) -{ - return pdev ? pdev->dev.of_node : NULL; -} - static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) { return pci_device_to_OF_node(bus->self); diff --git a/include/linux/pci.h b/include/linux/pci.h index e5086e9a9bf7..795e6a56d8cd 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1600,6 +1600,11 @@ extern void pci_release_bus_of_node(struct pci_bus *bus); /* Arch may override this (weak) */ extern struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus); +static inline struct device_node *pci_device_to_OF_node(struct pci_dev *pdev) +{ + return pdev ? pdev->dev.of_node : NULL; +} + #else /* CONFIG_OF */ static inline void pci_set_of_node(struct pci_dev *dev) { } static inline void pci_release_of_node(struct pci_dev *dev) { } -- cgit v1.2.3 From ef3b4f8cc20e80c767e47b848fb7512770ab80d7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 11 Apr 2011 11:34:33 +1000 Subject: pci/of: Consolidate pci_bus_to_OF_node() The generic code always get the device-node in the right place now so a single implementation will work for all archs Signed-off-by: Benjamin Herrenschmidt Acked-by: Grant Likely Acked-by: Michal Simek Acked-by: Jesse Barnes --- arch/microblaze/include/asm/pci-bridge.h | 5 ----- arch/powerpc/include/asm/pci-bridge.h | 5 ----- arch/x86/include/asm/prom.h | 6 ------ include/linux/pci.h | 5 +++++ 4 files changed, 5 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/microblaze/include/asm/pci-bridge.h b/arch/microblaze/include/asm/pci-bridge.h index 0d74d031e2a3..242be57a319c 100644 --- a/arch/microblaze/include/asm/pci-bridge.h +++ b/arch/microblaze/include/asm/pci-bridge.h @@ -102,11 +102,6 @@ struct pci_controller { }; #ifdef CONFIG_PCI -static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) -{ - return bus->dev.of_node; -} - static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus) { return bus->sysdata; diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 578060ef99ad..90bd3ed48165 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -169,11 +169,6 @@ static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus) return bus->sysdata; } -static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) -{ - return bus->dev.of_node; -} - #ifndef CONFIG_PPC64 extern int pci_device_from_OF_node(struct device_node *node, diff --git a/arch/x86/include/asm/prom.h b/arch/x86/include/asm/prom.h index dd6066a56346..df1287019e6d 100644 --- a/arch/x86/include/asm/prom.h +++ b/arch/x86/include/asm/prom.h @@ -30,12 +30,6 @@ extern void add_dtb(u64 data); extern void x86_add_irq_domains(void); void __cpuinit x86_of_pci_init(void); void x86_dtb_init(void); - -static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) -{ - return pci_device_to_OF_node(bus->self); -} - #else static inline void add_dtb(u64 data) { } static inline void x86_add_irq_domains(void) { } diff --git a/include/linux/pci.h b/include/linux/pci.h index 795e6a56d8cd..2d292182dde5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1605,6 +1605,11 @@ static inline struct device_node *pci_device_to_OF_node(struct pci_dev *pdev) return pdev ? pdev->dev.of_node : NULL; } +static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) +{ + return bus ? bus->dev.of_node : NULL; +} + #else /* CONFIG_OF */ static inline void pci_set_of_node(struct pci_dev *dev) { } static inline void pci_release_of_node(struct pci_dev *dev) { } -- cgit v1.2.3 From 6e6938b6d3130305a5960c86b1a9b21e58cf6144 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Sun, 6 Jun 2010 10:38:15 -0600 Subject: writeback: introduce .tagged_writepages for the WB_SYNC_NONE sync stage sync(2) is performed in two stages: the WB_SYNC_NONE sync and the WB_SYNC_ALL sync. Identify the first stage with .tagged_writepages and do livelock prevention for it, too. Jan's commit f446daaea9 ("mm: implement writeback livelock avoidance using page tagging") is a partial fix in that it only fixed the WB_SYNC_ALL phase livelock. Although ext4 is tested to no longer livelock with commit f446daaea9, it may due to some "redirty_tail() after pages_skipped" effect which is by no means a guarantee for _all_ the file systems. Note that writeback_inodes_sb() is called by not only sync(), they are treated the same because the other callers also need livelock prevention. Impact: It changes the order in which pages/inodes are synced to disk. Now in the WB_SYNC_NONE stage, it won't proceed to write the next inode until finished with the current inode. Acked-by: Jan Kara CC: Dave Chinner Signed-off-by: Wu Fengguang --- fs/ext4/inode.c | 4 ++-- fs/fs-writeback.c | 17 +++++++++-------- include/linux/writeback.h | 1 + mm/page-writeback.c | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index a5763e3505ba..8558b6c3450a 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2741,7 +2741,7 @@ static int write_cache_pages_da(struct address_space *mapping, index = wbc->range_start >> PAGE_CACHE_SHIFT; end = wbc->range_end >> PAGE_CACHE_SHIFT; - if (wbc->sync_mode == WB_SYNC_ALL) + if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages) tag = PAGECACHE_TAG_TOWRITE; else tag = PAGECACHE_TAG_DIRTY; @@ -2973,7 +2973,7 @@ static int ext4_da_writepages(struct address_space *mapping, } retry: - if (wbc->sync_mode == WB_SYNC_ALL) + if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages) tag_pages_for_writeback(mapping, index, end); while (!ret && wbc->nr_to_write > 0) { diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 0f015a0468de..5ed2ce9a28d0 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -36,6 +36,7 @@ struct wb_writeback_work { long nr_pages; struct super_block *sb; enum writeback_sync_modes sync_mode; + unsigned int tagged_writepages:1; unsigned int for_kupdate:1; unsigned int range_cyclic:1; unsigned int for_background:1; @@ -650,6 +651,7 @@ static long wb_writeback(struct bdi_writeback *wb, { struct writeback_control wbc = { .sync_mode = work->sync_mode, + .tagged_writepages = work->tagged_writepages, .older_than_this = NULL, .for_kupdate = work->for_kupdate, .for_background = work->for_background, @@ -657,7 +659,7 @@ static long wb_writeback(struct bdi_writeback *wb, }; unsigned long oldest_jif; long wrote = 0; - long write_chunk; + long write_chunk = MAX_WRITEBACK_PAGES; struct inode *inode; if (wbc.for_kupdate) { @@ -683,9 +685,7 @@ static long wb_writeback(struct bdi_writeback *wb, * (quickly) tag currently dirty pages * (maybe slowly) sync all tagged pages */ - if (wbc.sync_mode == WB_SYNC_NONE) - write_chunk = MAX_WRITEBACK_PAGES; - else + if (wbc.sync_mode == WB_SYNC_ALL || wbc.tagged_writepages) write_chunk = LONG_MAX; wbc.wb_start = jiffies; /* livelock avoidance */ @@ -1188,10 +1188,11 @@ void writeback_inodes_sb_nr(struct super_block *sb, unsigned long nr) { DECLARE_COMPLETION_ONSTACK(done); struct wb_writeback_work work = { - .sb = sb, - .sync_mode = WB_SYNC_NONE, - .done = &done, - .nr_pages = nr, + .sb = sb, + .sync_mode = WB_SYNC_NONE, + .tagged_writepages = 1, + .done = &done, + .nr_pages = nr, }; WARN_ON(!rwsem_is_locked(&sb->s_umount)); diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 17e7ccc322a5..3f6542ca6198 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -47,6 +47,7 @@ struct writeback_control { unsigned encountered_congestion:1; /* An output: a queue is full */ unsigned for_kupdate:1; /* A kupdate writeback */ unsigned for_background:1; /* A background writeback */ + unsigned tagged_writepages:1; /* tag-and-write to avoid livelock */ unsigned for_reclaim:1; /* Invoked from the page allocator */ unsigned range_cyclic:1; /* range_start is cyclic */ unsigned more_io:1; /* more io to be dispatched */ diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 31f698862420..955fe35d01e0 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -892,12 +892,12 @@ int write_cache_pages(struct address_space *mapping, range_whole = 1; cycled = 1; /* ignore range_cyclic tests */ } - if (wbc->sync_mode == WB_SYNC_ALL) + if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages) tag = PAGECACHE_TAG_TOWRITE; else tag = PAGECACHE_TAG_DIRTY; retry: - if (wbc->sync_mode == WB_SYNC_ALL) + if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages) tag_pages_for_writeback(mapping, index, end); done_index = index; while (!done && (index <= end)) { -- cgit v1.2.3 From cb9bd1159c5fe8995e151fa7df10fa19f8c119cc Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 21 Jul 2010 22:50:57 -0600 Subject: writeback: introduce writeback_control.inodes_written The flusher works on dirty inodes in batches, and may quit prematurely if the batch of inodes happen to be metadata-only dirtied: in this case wbc->nr_to_write won't be decreased at all, which stands for "no pages written" but also mis-interpreted as "no progress". So introduce writeback_control.inodes_written to count the inodes get cleaned from VFS POV. A non-zero value means there are some progress on writeback, in which case more writeback can be tried. Acked-by: Jan Kara Acked-by: Mel Gorman Signed-off-by: Wu Fengguang --- fs/fs-writeback.c | 4 ++++ include/linux/writeback.h | 1 + 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index fe190a8b0bc8..e4504299f4a5 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -464,6 +464,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) * No need to add it back to the LRU. */ list_del_init(&inode->i_wb_list); + wbc->inodes_written++; } } inode_sync_complete(inode); @@ -725,6 +726,7 @@ static long wb_writeback(struct bdi_writeback *wb, wbc.more_io = 0; wbc.nr_to_write = write_chunk; wbc.pages_skipped = 0; + wbc.inodes_written = 0; trace_wbc_writeback_start(&wbc, wb->bdi); if (work->sb) @@ -741,6 +743,8 @@ static long wb_writeback(struct bdi_writeback *wb, */ if (wbc.nr_to_write <= 0) continue; + if (wbc.inodes_written) + continue; /* * Didn't write everything and we don't have more IO, bail */ diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 3f6542ca6198..7df9026f7129 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -34,6 +34,7 @@ struct writeback_control { long nr_to_write; /* Write this many pages, and decrement this for each page written */ long pages_skipped; /* Pages which were not written */ + long inodes_written; /* # of inodes written (at least) */ /* * For a_ops->writepages(): is start or end are non-zero then this is -- cgit v1.2.3 From f758eeabeb96f878c860e8f110f94ec8820822a9 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 21 Apr 2011 18:19:44 -0600 Subject: writeback: split inode_wb_list_lock into bdi_writeback.list_lock Split the global inode_wb_list_lock into a per-bdi_writeback list_lock, as it's currently the most contended lock in the system for metadata heavy workloads. It won't help for single-filesystem workloads for which we'll need the I/O-less balance_dirty_pages, but at least we can dedicate a cpu to spinning on each bdi now for larger systems. Based on earlier patches from Nick Piggin and Dave Chinner. It reduces lock contentions to 1/4 in this test case: 10 HDD JBOD, 100 dd on each disk, XFS, 6GB ram lock_stat version 0.3 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- class name con-bounces contentions waittime-min waittime-max waittime-total acq-bounces acquisitions holdtime-min holdtime-max holdtime-total ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- vanilla 2.6.39-rc3: inode_wb_list_lock: 42590 44433 0.12 147.74 144127.35 252274 886792 0.08 121.34 917211.23 ------------------ inode_wb_list_lock 2 [] bdev_inode_switch_bdi+0x29/0x85 inode_wb_list_lock 34 [] inode_wb_list_del+0x22/0x49 inode_wb_list_lock 12893 [] __mark_inode_dirty+0x170/0x1d0 inode_wb_list_lock 10702 [] writeback_single_inode+0x16d/0x20a ------------------ inode_wb_list_lock 2 [] bdev_inode_switch_bdi+0x29/0x85 inode_wb_list_lock 19 [] inode_wb_list_del+0x22/0x49 inode_wb_list_lock 5550 [] __mark_inode_dirty+0x170/0x1d0 inode_wb_list_lock 8511 [] writeback_sb_inodes+0x10f/0x157 2.6.39-rc3 + patch: &(&wb->list_lock)->rlock: 11383 11657 0.14 151.69 40429.51 90825 527918 0.11 145.90 556843.37 ------------------------ &(&wb->list_lock)->rlock 10 [] inode_wb_list_del+0x5f/0x86 &(&wb->list_lock)->rlock 1493 [] writeback_inodes_wb+0x3d/0x150 &(&wb->list_lock)->rlock 3652 [] writeback_sb_inodes+0x123/0x16f &(&wb->list_lock)->rlock 1412 [] writeback_single_inode+0x17f/0x223 ------------------------ &(&wb->list_lock)->rlock 3 [] bdi_lock_two+0x46/0x4b &(&wb->list_lock)->rlock 6 [] inode_wb_list_del+0x5f/0x86 &(&wb->list_lock)->rlock 2061 [] __mark_inode_dirty+0x173/0x1cf &(&wb->list_lock)->rlock 2629 [] writeback_sb_inodes+0x123/0x16f hughd@google.com: fix recursive lock when bdi_lock_two() is called with new the same as old akpm@linux-foundation.org: cleanup bdev_inode_switch_bdi() comment Signed-off-by: Christoph Hellwig Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Wu Fengguang --- fs/block_dev.c | 16 +++++--- fs/fs-writeback.c | 97 +++++++++++++++++++++++---------------------- fs/inode.c | 5 +-- include/linux/backing-dev.h | 2 + include/linux/writeback.h | 2 - mm/backing-dev.c | 21 ++++++++-- mm/filemap.c | 6 +-- mm/rmap.c | 4 +- 8 files changed, 85 insertions(+), 68 deletions(-) (limited to 'include') diff --git a/fs/block_dev.c b/fs/block_dev.c index 1a2421f908f0..3c9a03e51b62 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -44,24 +44,28 @@ inline struct block_device *I_BDEV(struct inode *inode) { return &BDEV_I(inode)->bdev; } - EXPORT_SYMBOL(I_BDEV); /* - * move the inode from it's current bdi to the a new bdi. if the inode is dirty - * we need to move it onto the dirty list of @dst so that the inode is always - * on the right list. + * Move the inode from its current bdi to a new bdi. If the inode is dirty we + * need to move it onto the dirty list of @dst so that the inode is always on + * the right list. */ static void bdev_inode_switch_bdi(struct inode *inode, struct backing_dev_info *dst) { - spin_lock(&inode_wb_list_lock); + struct backing_dev_info *old = inode->i_data.backing_dev_info; + + if (unlikely(dst == old)) /* deadlock avoidance */ + return; + bdi_lock_two(&old->wb, &dst->wb); spin_lock(&inode->i_lock); inode->i_data.backing_dev_info = dst; if (inode->i_state & I_DIRTY) list_move(&inode->i_wb_list, &dst->wb.b_dirty); spin_unlock(&inode->i_lock); - spin_unlock(&inode_wb_list_lock); + spin_unlock(&old->wb.list_lock); + spin_unlock(&dst->wb.list_lock); } static sector_t max_block(struct block_device *bdev) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 664acdb2e7ef..36a30917e0dc 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -181,12 +181,13 @@ void bdi_start_background_writeback(struct backing_dev_info *bdi) */ void inode_wb_list_del(struct inode *inode) { - spin_lock(&inode_wb_list_lock); + struct backing_dev_info *bdi = inode_to_bdi(inode); + + spin_lock(&bdi->wb.list_lock); list_del_init(&inode->i_wb_list); - spin_unlock(&inode_wb_list_lock); + spin_unlock(&bdi->wb.list_lock); } - /* * Redirty an inode: set its when-it-was dirtied timestamp and move it to the * furthest end of its superblock's dirty-inode list. @@ -196,11 +197,9 @@ void inode_wb_list_del(struct inode *inode) * the case then the inode must have been redirtied while it was being written * out and we don't reset its dirtied_when. */ -static void redirty_tail(struct inode *inode) +static void redirty_tail(struct inode *inode, struct bdi_writeback *wb) { - struct bdi_writeback *wb = &inode_to_bdi(inode)->wb; - - assert_spin_locked(&inode_wb_list_lock); + assert_spin_locked(&wb->list_lock); if (!list_empty(&wb->b_dirty)) { struct inode *tail; @@ -214,11 +213,9 @@ static void redirty_tail(struct inode *inode) /* * requeue inode for re-scanning after bdi->b_io list is exhausted. */ -static void requeue_io(struct inode *inode) +static void requeue_io(struct inode *inode, struct bdi_writeback *wb) { - struct bdi_writeback *wb = &inode_to_bdi(inode)->wb; - - assert_spin_locked(&inode_wb_list_lock); + assert_spin_locked(&wb->list_lock); list_move(&inode->i_wb_list, &wb->b_more_io); } @@ -226,7 +223,7 @@ static void inode_sync_complete(struct inode *inode) { /* * Prevent speculative execution through - * spin_unlock(&inode_wb_list_lock); + * spin_unlock(&wb->list_lock); */ smp_mb(); @@ -302,7 +299,7 @@ static void move_expired_inodes(struct list_head *delaying_queue, */ static void queue_io(struct bdi_writeback *wb, unsigned long *older_than_this) { - assert_spin_locked(&inode_wb_list_lock); + assert_spin_locked(&wb->list_lock); list_splice_init(&wb->b_more_io, &wb->b_io); move_expired_inodes(&wb->b_dirty, &wb->b_io, older_than_this); } @@ -317,7 +314,8 @@ static int write_inode(struct inode *inode, struct writeback_control *wbc) /* * Wait for writeback on an inode to complete. */ -static void inode_wait_for_writeback(struct inode *inode) +static void inode_wait_for_writeback(struct inode *inode, + struct bdi_writeback *wb) { DEFINE_WAIT_BIT(wq, &inode->i_state, __I_SYNC); wait_queue_head_t *wqh; @@ -325,15 +323,15 @@ static void inode_wait_for_writeback(struct inode *inode) wqh = bit_waitqueue(&inode->i_state, __I_SYNC); while (inode->i_state & I_SYNC) { spin_unlock(&inode->i_lock); - spin_unlock(&inode_wb_list_lock); + spin_unlock(&wb->list_lock); __wait_on_bit(wqh, &wq, inode_wait, TASK_UNINTERRUPTIBLE); - spin_lock(&inode_wb_list_lock); + spin_lock(&wb->list_lock); spin_lock(&inode->i_lock); } } /* - * Write out an inode's dirty pages. Called under inode_wb_list_lock and + * Write out an inode's dirty pages. Called under wb->list_lock and * inode->i_lock. Either the caller has an active reference on the inode or * the inode has I_WILL_FREE set. * @@ -344,13 +342,14 @@ static void inode_wait_for_writeback(struct inode *inode) * livelocks, etc. */ static int -writeback_single_inode(struct inode *inode, struct writeback_control *wbc) +writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, + struct writeback_control *wbc) { struct address_space *mapping = inode->i_mapping; unsigned dirty; int ret; - assert_spin_locked(&inode_wb_list_lock); + assert_spin_locked(&wb->list_lock); assert_spin_locked(&inode->i_lock); if (!atomic_read(&inode->i_count)) @@ -368,14 +367,14 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) * completed a full scan of b_io. */ if (wbc->sync_mode != WB_SYNC_ALL) { - requeue_io(inode); + requeue_io(inode, wb); return 0; } /* * It's a data-integrity sync. We must wait. */ - inode_wait_for_writeback(inode); + inode_wait_for_writeback(inode, wb); } BUG_ON(inode->i_state & I_SYNC); @@ -384,7 +383,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) inode->i_state |= I_SYNC; inode->i_state &= ~I_DIRTY_PAGES; spin_unlock(&inode->i_lock); - spin_unlock(&inode_wb_list_lock); + spin_unlock(&wb->list_lock); ret = do_writepages(mapping, wbc); @@ -415,7 +414,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) ret = err; } - spin_lock(&inode_wb_list_lock); + spin_lock(&wb->list_lock); spin_lock(&inode->i_lock); inode->i_state &= ~I_SYNC; if (!(inode->i_state & I_FREEING)) { @@ -438,7 +437,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) /* * slice used up: queue for next turn */ - requeue_io(inode); + requeue_io(inode, wb); } else { /* * Writeback blocked by something other than @@ -447,7 +446,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) * retrying writeback of the dirty page/inode * that cannot be performed immediately. */ - redirty_tail(inode); + redirty_tail(inode, wb); } } else if (inode->i_state & I_DIRTY) { /* @@ -456,7 +455,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) * submission or metadata updates after data IO * completion. */ - redirty_tail(inode); + redirty_tail(inode, wb); } else { /* * The inode is clean. At this point we either have @@ -521,7 +520,7 @@ static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb, * superblock, move all inodes not belonging * to it back onto the dirty list. */ - redirty_tail(inode); + redirty_tail(inode, wb); continue; } @@ -541,7 +540,7 @@ static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb, spin_lock(&inode->i_lock); if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) { spin_unlock(&inode->i_lock); - requeue_io(inode); + requeue_io(inode, wb); continue; } @@ -557,19 +556,19 @@ static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb, __iget(inode); pages_skipped = wbc->pages_skipped; - writeback_single_inode(inode, wbc); + writeback_single_inode(inode, wb, wbc); if (wbc->pages_skipped != pages_skipped) { /* * writeback is not making progress due to locked * buffers. Skip this inode for now. */ - redirty_tail(inode); + redirty_tail(inode, wb); } spin_unlock(&inode->i_lock); - spin_unlock(&inode_wb_list_lock); + spin_unlock(&wb->list_lock); iput(inode); cond_resched(); - spin_lock(&inode_wb_list_lock); + spin_lock(&wb->list_lock); if (wbc->nr_to_write <= 0) { wbc->more_io = 1; return 1; @@ -588,7 +587,7 @@ void writeback_inodes_wb(struct bdi_writeback *wb, if (!wbc->wb_start) wbc->wb_start = jiffies; /* livelock avoidance */ - spin_lock(&inode_wb_list_lock); + spin_lock(&wb->list_lock); if (list_empty(&wb->b_io)) queue_io(wb, wbc->older_than_this); @@ -598,7 +597,7 @@ void writeback_inodes_wb(struct bdi_writeback *wb, struct super_block *sb = inode->i_sb; if (!pin_sb_for_writeback(sb)) { - requeue_io(inode); + requeue_io(inode, wb); continue; } ret = writeback_sb_inodes(sb, wb, wbc, false); @@ -607,7 +606,7 @@ void writeback_inodes_wb(struct bdi_writeback *wb, if (ret) break; } - spin_unlock(&inode_wb_list_lock); + spin_unlock(&wb->list_lock); /* Leave any unwritten inodes on b_io */ } @@ -616,11 +615,11 @@ static void __writeback_inodes_sb(struct super_block *sb, { WARN_ON(!rwsem_is_locked(&sb->s_umount)); - spin_lock(&inode_wb_list_lock); + spin_lock(&wb->list_lock); if (list_empty(&wb->b_io)) queue_io(wb, wbc->older_than_this); writeback_sb_inodes(sb, wb, wbc, true); - spin_unlock(&inode_wb_list_lock); + spin_unlock(&wb->list_lock); } /* @@ -762,15 +761,15 @@ static long wb_writeback(struct bdi_writeback *wb, * become available for writeback. Otherwise * we'll just busyloop. */ - spin_lock(&inode_wb_list_lock); + spin_lock(&wb->list_lock); if (!list_empty(&wb->b_more_io)) { inode = wb_inode(wb->b_more_io.prev); trace_wbc_writeback_wait(&wbc, wb->bdi); spin_lock(&inode->i_lock); - inode_wait_for_writeback(inode); + inode_wait_for_writeback(inode, wb); spin_unlock(&inode->i_lock); } - spin_unlock(&inode_wb_list_lock); + spin_unlock(&wb->list_lock); } return wrote; @@ -1104,10 +1103,10 @@ void __mark_inode_dirty(struct inode *inode, int flags) } spin_unlock(&inode->i_lock); - spin_lock(&inode_wb_list_lock); + spin_lock(&bdi->wb.list_lock); inode->dirtied_when = jiffies; list_move(&inode->i_wb_list, &bdi->wb.b_dirty); - spin_unlock(&inode_wb_list_lock); + spin_unlock(&bdi->wb.list_lock); if (wakeup_bdi) bdi_wakeup_thread_delayed(bdi); @@ -1309,6 +1308,7 @@ EXPORT_SYMBOL(sync_inodes_sb); */ int write_inode_now(struct inode *inode, int sync) { + struct bdi_writeback *wb = &inode_to_bdi(inode)->wb; int ret; struct writeback_control wbc = { .nr_to_write = LONG_MAX, @@ -1321,11 +1321,11 @@ int write_inode_now(struct inode *inode, int sync) wbc.nr_to_write = 0; might_sleep(); - spin_lock(&inode_wb_list_lock); + spin_lock(&wb->list_lock); spin_lock(&inode->i_lock); - ret = writeback_single_inode(inode, &wbc); + ret = writeback_single_inode(inode, wb, &wbc); spin_unlock(&inode->i_lock); - spin_unlock(&inode_wb_list_lock); + spin_unlock(&wb->list_lock); if (sync) inode_sync_wait(inode); return ret; @@ -1345,13 +1345,14 @@ EXPORT_SYMBOL(write_inode_now); */ int sync_inode(struct inode *inode, struct writeback_control *wbc) { + struct bdi_writeback *wb = &inode_to_bdi(inode)->wb; int ret; - spin_lock(&inode_wb_list_lock); + spin_lock(&wb->list_lock); spin_lock(&inode->i_lock); - ret = writeback_single_inode(inode, wbc); + ret = writeback_single_inode(inode, wb, wbc); spin_unlock(&inode->i_lock); - spin_unlock(&inode_wb_list_lock); + spin_unlock(&wb->list_lock); return ret; } EXPORT_SYMBOL(sync_inode); diff --git a/fs/inode.c b/fs/inode.c index 0f7e88a7803f..4be128cbc754 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -37,7 +37,7 @@ * inode_lru, inode->i_lru * inode_sb_list_lock protects: * sb->s_inodes, inode->i_sb_list - * inode_wb_list_lock protects: + * bdi->wb.list_lock protects: * bdi->wb.b_{dirty,io,more_io}, inode->i_wb_list * inode_hash_lock protects: * inode_hashtable, inode->i_hash @@ -48,7 +48,7 @@ * inode->i_lock * inode_lru_lock * - * inode_wb_list_lock + * bdi->wb.list_lock * inode->i_lock * * inode_hash_lock @@ -68,7 +68,6 @@ static LIST_HEAD(inode_lru); static DEFINE_SPINLOCK(inode_lru_lock); __cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_sb_list_lock); -__cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_wb_list_lock); /* * iprune_sem provides exclusion between the icache shrinking and the diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 96f4094b706d..47feb2c4706a 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -57,6 +57,7 @@ struct bdi_writeback { struct list_head b_dirty; /* dirty inodes */ struct list_head b_io; /* parked for writeback */ struct list_head b_more_io; /* parked for more writeback */ + spinlock_t list_lock; /* protects the b_* lists */ }; struct backing_dev_info { @@ -106,6 +107,7 @@ int bdi_writeback_thread(void *data); int bdi_has_dirty_io(struct backing_dev_info *bdi); void bdi_arm_supers_timer(void); void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi); +void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2); extern spinlock_t bdi_lock; extern struct list_head bdi_list; diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 7df9026f7129..c2d957fb38d3 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -9,8 +9,6 @@ struct backing_dev_info; -extern spinlock_t inode_wb_list_lock; - /* * fs/fs-writeback.c */ diff --git a/mm/backing-dev.c b/mm/backing-dev.c index f032e6e1e09a..5f6553ef1ba7 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -45,6 +45,17 @@ static struct timer_list sync_supers_timer; static int bdi_sync_supers(void *); static void sync_supers_timer_fn(unsigned long); +void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2) +{ + if (wb1 < wb2) { + spin_lock(&wb1->list_lock); + spin_lock_nested(&wb2->list_lock, 1); + } else { + spin_lock(&wb2->list_lock); + spin_lock_nested(&wb1->list_lock, 1); + } +} + #ifdef CONFIG_DEBUG_FS #include #include @@ -67,14 +78,14 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) struct inode *inode; nr_dirty = nr_io = nr_more_io = 0; - spin_lock(&inode_wb_list_lock); + spin_lock(&wb->list_lock); list_for_each_entry(inode, &wb->b_dirty, i_wb_list) nr_dirty++; list_for_each_entry(inode, &wb->b_io, i_wb_list) nr_io++; list_for_each_entry(inode, &wb->b_more_io, i_wb_list) nr_more_io++; - spin_unlock(&inode_wb_list_lock); + spin_unlock(&wb->list_lock); global_dirty_limits(&background_thresh, &dirty_thresh); bdi_thresh = bdi_dirty_limit(bdi, dirty_thresh); @@ -628,6 +639,7 @@ static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi) INIT_LIST_HEAD(&wb->b_dirty); INIT_LIST_HEAD(&wb->b_io); INIT_LIST_HEAD(&wb->b_more_io); + spin_lock_init(&wb->list_lock); setup_timer(&wb->wakeup_timer, wakeup_timer_fn, (unsigned long)bdi); } @@ -676,11 +688,12 @@ void bdi_destroy(struct backing_dev_info *bdi) if (bdi_has_dirty_io(bdi)) { struct bdi_writeback *dst = &default_backing_dev_info.wb; - spin_lock(&inode_wb_list_lock); + bdi_lock_two(&bdi->wb, dst); list_splice(&bdi->wb.b_dirty, &dst->b_dirty); list_splice(&bdi->wb.b_io, &dst->b_io); list_splice(&bdi->wb.b_more_io, &dst->b_more_io); - spin_unlock(&inode_wb_list_lock); + spin_unlock(&bdi->wb.list_lock); + spin_unlock(&dst->list_lock); } bdi_unregister(bdi); diff --git a/mm/filemap.c b/mm/filemap.c index d7b10578a64b..1e492c3dd6f8 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -81,7 +81,7 @@ * ->i_mutex * ->i_alloc_sem (various) * - * inode_wb_list_lock + * bdi->wb.list_lock * sb_lock (fs/fs-writeback.c) * ->mapping->tree_lock (__sync_single_inode) * @@ -99,9 +99,9 @@ * ->zone.lru_lock (check_pte_range->isolate_lru_page) * ->private_lock (page_remove_rmap->set_page_dirty) * ->tree_lock (page_remove_rmap->set_page_dirty) - * inode_wb_list_lock (page_remove_rmap->set_page_dirty) + * bdi.wb->list_lock (page_remove_rmap->set_page_dirty) * ->inode->i_lock (page_remove_rmap->set_page_dirty) - * inode_wb_list_lock (zap_pte_range->set_page_dirty) + * bdi.wb->list_lock (zap_pte_range->set_page_dirty) * ->inode->i_lock (zap_pte_range->set_page_dirty) * ->private_lock (zap_pte_range->__set_page_dirty_buffers) * diff --git a/mm/rmap.c b/mm/rmap.c index 0eb463ea88dd..d04e36a7cc9f 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -32,11 +32,11 @@ * mmlist_lock (in mmput, drain_mmlist and others) * mapping->private_lock (in __set_page_dirty_buffers) * inode->i_lock (in set_page_dirty's __mark_inode_dirty) - * inode_wb_list_lock (in set_page_dirty's __mark_inode_dirty) + * bdi.wb->list_lock (in set_page_dirty's __mark_inode_dirty) * sb_lock (within inode_lock in fs/fs-writeback.c) * mapping->tree_lock (widely used, in set_page_dirty, * in arch-dependent flush_dcache_mmap_lock, - * within inode_wb_list_lock in __sync_single_inode) + * within bdi.wb->list_lock in __sync_single_inode) * * (code doesn't rely on that order so it could be switched around) * ->tasklist_lock -- cgit v1.2.3 From e185dda89d69cde142b48059413a03561f41f78a Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Sat, 23 Apr 2011 11:26:07 -0600 Subject: writeback: avoid extra sync work at enqueue time This removes writeback_control.wb_start and does more straightforward sync livelock prevention by setting .older_than_this to prevent extra inodes from being enqueued in the first place. Acked-by: Jan Kara Signed-off-by: Wu Fengguang --- fs/fs-writeback.c | 16 +++------------- include/linux/writeback.h | 3 --- 2 files changed, 3 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 565b1fd15be6..d0553f33fb50 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -544,15 +544,6 @@ static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb, continue; } - /* - * Was this inode dirtied after sync_sb_inodes was called? - * This keeps sync from extra jobs and livelock. - */ - if (inode_dirtied_after(inode, wbc->wb_start)) { - spin_unlock(&inode->i_lock); - return 1; - } - __iget(inode); pages_skipped = wbc->pages_skipped; @@ -585,9 +576,6 @@ static void __writeback_inodes_wb(struct bdi_writeback *wb, { int ret = 0; - if (!wbc->wb_start) - wbc->wb_start = jiffies; /* livelock avoidance */ - while (!list_empty(&wb->b_io)) { struct inode *inode = wb_inode(wb->b_io.prev); struct super_block *sb = inode->i_sb; @@ -686,7 +674,9 @@ static long wb_writeback(struct bdi_writeback *wb, if (wbc.sync_mode == WB_SYNC_ALL || wbc.tagged_writepages) write_chunk = LONG_MAX; - wbc.wb_start = jiffies; /* livelock avoidance */ + oldest_jif = jiffies; + wbc.older_than_this = &oldest_jif; + spin_lock(&wb->list_lock); for (;;) { /* diff --git a/include/linux/writeback.h b/include/linux/writeback.h index c2d957fb38d3..d8e96a480850 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -26,9 +26,6 @@ struct writeback_control { enum writeback_sync_modes sync_mode; unsigned long *older_than_this; /* If !NULL, only write back inodes older than this */ - unsigned long wb_start; /* Time writeback_inodes_wb was - called. This is needed to avoid - extra jobs and livelock */ long nr_to_write; /* Write this many pages, and decrement this for each page written */ long pages_skipped; /* Pages which were not written */ -- cgit v1.2.3 From b7a2441f9966fe3e1be960a876ab52e6029ea005 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 21 Jul 2010 22:19:51 -0600 Subject: writeback: remove writeback_control.more_io When wbc.more_io was first introduced, it indicates whether there are at least one superblock whose s_more_io contains more IO work. Now with the per-bdi writeback, it can be replaced with a simple b_more_io test. Acked-by: Jan Kara Acked-by: Mel Gorman Reviewed-by: Minchan Kim Signed-off-by: Wu Fengguang --- fs/fs-writeback.c | 9 ++------- include/linux/writeback.h | 1 - include/trace/events/ext4.h | 6 ++---- include/trace/events/writeback.h | 5 +---- 4 files changed, 5 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index d0553f33fb50..f43c479feee9 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -560,12 +560,8 @@ static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb, iput(inode); cond_resched(); spin_lock(&wb->list_lock); - if (wbc->nr_to_write <= 0) { - wbc->more_io = 1; + if (wbc->nr_to_write <= 0) return 1; - } - if (!list_empty(&wb->b_more_io)) - wbc->more_io = 1; } /* b_io is empty */ return 1; @@ -708,7 +704,6 @@ static long wb_writeback(struct bdi_writeback *wb, wbc.older_than_this = &oldest_jif; } - wbc.more_io = 0; wbc.nr_to_write = write_chunk; wbc.pages_skipped = 0; wbc.inodes_written = 0; @@ -740,7 +735,7 @@ static long wb_writeback(struct bdi_writeback *wb, /* * No more inodes for IO, bail */ - if (!wbc.more_io) + if (list_empty(&wb->b_more_io)) break; /* * Nothing written. Wait for some inode to diff --git a/include/linux/writeback.h b/include/linux/writeback.h index d8e96a480850..8797b20dd22b 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -46,7 +46,6 @@ struct writeback_control { unsigned tagged_writepages:1; /* tag-and-write to avoid livelock */ unsigned for_reclaim:1; /* Invoked from the page allocator */ unsigned range_cyclic:1; /* range_start is cyclic */ - unsigned more_io:1; /* more io to be dispatched */ }; /* diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index e09592d2f916..b225d0d8c87f 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -404,7 +404,6 @@ TRACE_EVENT(ext4_da_writepages_result, __field( int, pages_written ) __field( long, pages_skipped ) __field( int, sync_mode ) - __field( char, more_io ) __field( pgoff_t, writeback_index ) ), @@ -415,16 +414,15 @@ TRACE_EVENT(ext4_da_writepages_result, __entry->pages_written = pages_written; __entry->pages_skipped = wbc->pages_skipped; __entry->sync_mode = wbc->sync_mode; - __entry->more_io = wbc->more_io; __entry->writeback_index = inode->i_mapping->writeback_index; ), TP_printk("dev %d,%d ino %lu ret %d pages_written %d pages_skipped %ld " - " more_io %d sync_mode %d writeback_index %lu", + "sync_mode %d writeback_index %lu", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, __entry->ret, __entry->pages_written, __entry->pages_skipped, - __entry->more_io, __entry->sync_mode, + __entry->sync_mode, (unsigned long) __entry->writeback_index) ); diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 4e249b927eaa..b2cfac5f3313 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -101,7 +101,6 @@ DECLARE_EVENT_CLASS(wbc_class, __field(int, for_background) __field(int, for_reclaim) __field(int, range_cyclic) - __field(int, more_io) __field(unsigned long, older_than_this) __field(long, range_start) __field(long, range_end) @@ -116,7 +115,6 @@ DECLARE_EVENT_CLASS(wbc_class, __entry->for_background = wbc->for_background; __entry->for_reclaim = wbc->for_reclaim; __entry->range_cyclic = wbc->range_cyclic; - __entry->more_io = wbc->more_io; __entry->older_than_this = wbc->older_than_this ? *wbc->older_than_this : 0; __entry->range_start = (long)wbc->range_start; @@ -124,7 +122,7 @@ DECLARE_EVENT_CLASS(wbc_class, ), TP_printk("bdi %s: towrt=%ld skip=%ld mode=%d kupd=%d " - "bgrd=%d reclm=%d cyclic=%d more=%d older=0x%lx " + "bgrd=%d reclm=%d cyclic=%d older=0x%lx " "start=0x%lx end=0x%lx", __entry->name, __entry->nr_to_write, @@ -134,7 +132,6 @@ DECLARE_EVENT_CLASS(wbc_class, __entry->for_background, __entry->for_reclaim, __entry->range_cyclic, - __entry->more_io, __entry->older_than_this, __entry->range_start, __entry->range_end) -- cgit v1.2.3 From 846d5a091b0506b75489577cde27f39b37a192a4 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Thu, 5 May 2011 21:10:38 -0600 Subject: writeback: remove .nonblocking and .encountered_congestion Remove two unused struct writeback_control fields: .encountered_congestion (completely unused) .nonblocking (never set, checked/showed in XFS,NFS/btrfs) The .for_background check in nfs_write_inode() is also removed btw, as .for_background implies WB_SYNC_NONE. Reviewed-by: Jan Kara Proposed-by: Christoph Hellwig Signed-off-by: Wu Fengguang --- fs/nfs/write.c | 3 +-- fs/xfs/linux-2.6/xfs_aops.c | 2 +- include/linux/writeback.h | 2 -- include/trace/events/btrfs.h | 6 ++---- 4 files changed, 4 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/fs/nfs/write.c b/fs/nfs/write.c index e268e3b23497..dd6a6cee39a7 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1564,8 +1564,7 @@ int nfs_write_inode(struct inode *inode, struct writeback_control *wbc) int status; bool sync = true; - if (wbc->sync_mode == WB_SYNC_NONE || wbc->nonblocking || - wbc->for_background) + if (wbc->sync_mode == WB_SYNC_NONE) sync = false; status = pnfs_layoutcommit_inode(inode, sync); diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 79ce38be15a1..7559861481aa 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -970,7 +970,7 @@ xfs_vm_writepage( offset = page_offset(page); type = IO_OVERWRITE; - if (wbc->sync_mode == WB_SYNC_NONE && wbc->nonblocking) + if (wbc->sync_mode == WB_SYNC_NONE) nonblocking = 1; do { diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 8797b20dd22b..2f1b512bd6e0 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -39,8 +39,6 @@ struct writeback_control { loff_t range_start; loff_t range_end; - unsigned nonblocking:1; /* Don't get stuck on request queues */ - unsigned encountered_congestion:1; /* An output: a queue is full */ unsigned for_kupdate:1; /* A kupdate writeback */ unsigned for_background:1; /* A background writeback */ unsigned tagged_writepages:1; /* tag-and-write to avoid livelock */ diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 4114129f0794..b31702ac15be 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -284,7 +284,6 @@ DECLARE_EVENT_CLASS(btrfs__writepage, __field( long, pages_skipped ) __field( loff_t, range_start ) __field( loff_t, range_end ) - __field( char, nonblocking ) __field( char, for_kupdate ) __field( char, for_reclaim ) __field( char, range_cyclic ) @@ -299,7 +298,6 @@ DECLARE_EVENT_CLASS(btrfs__writepage, __entry->pages_skipped = wbc->pages_skipped; __entry->range_start = wbc->range_start; __entry->range_end = wbc->range_end; - __entry->nonblocking = wbc->nonblocking; __entry->for_kupdate = wbc->for_kupdate; __entry->for_reclaim = wbc->for_reclaim; __entry->range_cyclic = wbc->range_cyclic; @@ -310,13 +308,13 @@ DECLARE_EVENT_CLASS(btrfs__writepage, TP_printk("root = %llu(%s), ino = %lu, page_index = %lu, " "nr_to_write = %ld, pages_skipped = %ld, range_start = %llu, " - "range_end = %llu, nonblocking = %d, for_kupdate = %d, " + "range_end = %llu, for_kupdate = %d, " "for_reclaim = %d, range_cyclic = %d, writeback_index = %lu", show_root_type(__entry->root_objectid), (unsigned long)__entry->ino, __entry->index, __entry->nr_to_write, __entry->pages_skipped, __entry->range_start, __entry->range_end, - __entry->nonblocking, __entry->for_kupdate, + __entry->for_kupdate, __entry->for_reclaim, __entry->range_cyclic, (unsigned long)__entry->writeback_index) ); -- cgit v1.2.3 From 251d6a471c831e22880b3c146bb4556ddfb1dc82 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 1 Dec 2010 17:33:37 -0600 Subject: writeback: trace event writeback_single_inode It is valuable to know how the dirty inodes are iterated and their IO size. "writeback_single_inode: bdi 8:0: ino=134246746 state=I_DIRTY_SYNC|I_SYNC age=414 index=0 to_write=1024 wrote=0" - "state" reflects inode->i_state at the end of writeback_single_inode() - "index" reflects mapping->writeback_index after the ->writepages() call - "to_write" is the wbc->nr_to_write at entrance of writeback_single_inode() - "wrote" is the number of pages actually written v2: add trace event writeback_single_inode_requeue as proposed by Dave. CC: Dave Chinner Signed-off-by: Wu Fengguang --- fs/fs-writeback.c | 4 +++ include/trace/events/writeback.h | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) (limited to 'include') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index f43c479feee9..5185fad48b62 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -346,6 +346,7 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, struct writeback_control *wbc) { struct address_space *mapping = inode->i_mapping; + long nr_to_write = wbc->nr_to_write; unsigned dirty; int ret; @@ -368,6 +369,8 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, */ if (wbc->sync_mode != WB_SYNC_ALL) { requeue_io(inode, wb); + trace_writeback_single_inode_requeue(inode, wbc, + nr_to_write); return 0; } @@ -467,6 +470,7 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, } } inode_sync_complete(inode); + trace_writeback_single_inode(inode, wbc, nr_to_write); return ret; } diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index b2cfac5f3313..898277bc89b4 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -8,6 +8,19 @@ #include #include +#define show_inode_state(state) \ + __print_flags(state, "|", \ + {I_DIRTY_SYNC, "I_DIRTY_SYNC"}, \ + {I_DIRTY_DATASYNC, "I_DIRTY_DATASYNC"}, \ + {I_DIRTY_PAGES, "I_DIRTY_PAGES"}, \ + {I_NEW, "I_NEW"}, \ + {I_WILL_FREE, "I_WILL_FREE"}, \ + {I_FREEING, "I_FREEING"}, \ + {I_CLEAR, "I_CLEAR"}, \ + {I_SYNC, "I_SYNC"}, \ + {I_REFERENCED, "I_REFERENCED"} \ + ) + struct wb_writeback_work; DECLARE_EVENT_CLASS(writeback_work_class, @@ -184,6 +197,63 @@ DEFINE_EVENT(writeback_congest_waited_template, writeback_wait_iff_congested, TP_ARGS(usec_timeout, usec_delayed) ); +DECLARE_EVENT_CLASS(writeback_single_inode_template, + + TP_PROTO(struct inode *inode, + struct writeback_control *wbc, + unsigned long nr_to_write + ), + + TP_ARGS(inode, wbc, nr_to_write), + + TP_STRUCT__entry( + __array(char, name, 32) + __field(unsigned long, ino) + __field(unsigned long, state) + __field(unsigned long, age) + __field(unsigned long, writeback_index) + __field(long, nr_to_write) + __field(unsigned long, wrote) + ), + + TP_fast_assign( + strncpy(__entry->name, + dev_name(inode->i_mapping->backing_dev_info->dev), 32); + __entry->ino = inode->i_ino; + __entry->state = inode->i_state; + __entry->age = (jiffies - inode->dirtied_when) * + 1000 / HZ; + __entry->writeback_index = inode->i_mapping->writeback_index; + __entry->nr_to_write = nr_to_write; + __entry->wrote = nr_to_write - wbc->nr_to_write; + ), + + TP_printk("bdi %s: ino=%lu state=%s age=%lu " + "index=%lu to_write=%ld wrote=%lu", + __entry->name, + __entry->ino, + show_inode_state(__entry->state), + __entry->age, + __entry->writeback_index, + __entry->nr_to_write, + __entry->wrote + ) +); + +DEFINE_EVENT(writeback_single_inode_template, writeback_single_inode_requeue, + TP_PROTO(struct inode *inode, + struct writeback_control *wbc, + unsigned long nr_to_write), + TP_ARGS(inode, wbc, nr_to_write) +); + +DEFINE_EVENT(writeback_single_inode_template, writeback_single_inode, + TP_PROTO(struct inode *inode, + struct writeback_control *wbc, + unsigned long nr_to_write), + TP_ARGS(inode, wbc, nr_to_write) +); + #endif /* _TRACE_WRITEBACK_H */ /* This part must be outside protection */ -- cgit v1.2.3 From e84d0a4f8e39a73003a6ec9a11b07702745f4c1f Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Sat, 23 Apr 2011 12:27:27 -0600 Subject: writeback: trace event writeback_queue_io Note that it adds a little overheads to account the moved/enqueued inodes from b_dirty to b_io. The "moved" accounting may be later used to limit the number of inodes that can be moved in one shot, in order to keep spinlock hold time under control. Signed-off-by: Wu Fengguang --- fs/fs-writeback.c | 14 ++++++++++---- include/trace/events/writeback.h | 25 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 5185fad48b62..6caa98247a5b 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -248,15 +248,16 @@ static bool inode_dirtied_after(struct inode *inode, unsigned long t) /* * Move expired dirty inodes from @delaying_queue to @dispatch_queue. */ -static void move_expired_inodes(struct list_head *delaying_queue, +static int move_expired_inodes(struct list_head *delaying_queue, struct list_head *dispatch_queue, - unsigned long *older_than_this) + unsigned long *older_than_this) { LIST_HEAD(tmp); struct list_head *pos, *node; struct super_block *sb = NULL; struct inode *inode; int do_sb_sort = 0; + int moved = 0; while (!list_empty(delaying_queue)) { inode = wb_inode(delaying_queue->prev); @@ -267,12 +268,13 @@ static void move_expired_inodes(struct list_head *delaying_queue, do_sb_sort = 1; sb = inode->i_sb; list_move(&inode->i_wb_list, &tmp); + moved++; } /* just one sb in list, splice to dispatch_queue and we're done */ if (!do_sb_sort) { list_splice(&tmp, dispatch_queue); - return; + goto out; } /* Move inodes from one superblock together */ @@ -284,6 +286,8 @@ static void move_expired_inodes(struct list_head *delaying_queue, list_move(&inode->i_wb_list, dispatch_queue); } } +out: + return moved; } /* @@ -299,9 +303,11 @@ static void move_expired_inodes(struct list_head *delaying_queue, */ static void queue_io(struct bdi_writeback *wb, unsigned long *older_than_this) { + int moved; assert_spin_locked(&wb->list_lock); list_splice_init(&wb->b_more_io, &wb->b_io); - move_expired_inodes(&wb->b_dirty, &wb->b_io, older_than_this); + moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, older_than_this); + trace_writeback_queue_io(wb, older_than_this, moved); } static int write_inode(struct inode *inode, struct writeback_control *wbc) diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 898277bc89b4..205d14919ef2 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -162,6 +162,31 @@ DEFINE_WBC_EVENT(wbc_balance_dirty_written); DEFINE_WBC_EVENT(wbc_balance_dirty_wait); DEFINE_WBC_EVENT(wbc_writepage); +TRACE_EVENT(writeback_queue_io, + TP_PROTO(struct bdi_writeback *wb, + unsigned long *older_than_this, + int moved), + TP_ARGS(wb, older_than_this, moved), + TP_STRUCT__entry( + __array(char, name, 32) + __field(unsigned long, older) + __field(long, age) + __field(int, moved) + ), + TP_fast_assign( + strncpy(__entry->name, dev_name(wb->bdi->dev), 32); + __entry->older = older_than_this ? *older_than_this : 0; + __entry->age = older_than_this ? + (jiffies - *older_than_this) * 1000 / HZ : -1; + __entry->moved = moved; + ), + TP_printk("bdi %s: older=%lu age=%ld enqueue=%d", + __entry->name, + __entry->older, /* older_than_this in jiffies */ + __entry->age, /* older_than_this in relative milliseconds */ + __entry->moved) +); + DECLARE_EVENT_CLASS(writeback_congest_waited_template, TP_PROTO(unsigned int usec_timeout, unsigned int usec_delayed), -- cgit v1.2.3 From debf9507166eede1e676d27d3298cdfb27399cb4 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 8 Jun 2011 18:19:37 +0900 Subject: serial: sh-sci: Generalize overrun handling. This consolidates all of the broken out overrun handling and ensures that we have sensible defaults per-port type, in addition to making sure that overruns are flagged appropriately in the error mask for parts that haven't explicitly disabled support for it. Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.c | 51 ++++++++++++++++++++++++++++++++++++++------ drivers/tty/serial/sh-sci.h | 52 ++------------------------------------------- include/linux/serial_sci.h | 30 ++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 280c02af0eae..bb27885ea2e5 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -563,13 +563,19 @@ static int sci_handle_errors(struct uart_port *port) int copied = 0; unsigned short status = sci_in(port, SCxSR); struct tty_struct *tty = port->state->port.tty; + struct sci_port *s = to_sci_port(port); - if (status & SCxSR_ORER(port)) { - /* overrun error */ - if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) - copied++; + /* + * Handle overruns, if supported. + */ + if (s->cfg->overrun_bit != SCIx_NOT_SUPPORTED) { + if (status & (1 << s->cfg->overrun_bit)) { + /* overrun error */ + if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) + copied++; - dev_notice(port->dev, "overrun error"); + dev_notice(port->dev, "overrun error"); + } } if (status & SCxSR_FER(port)) { @@ -617,12 +623,19 @@ static int sci_handle_errors(struct uart_port *port) static int sci_handle_fifo_overrun(struct uart_port *port) { struct tty_struct *tty = port->state->port.tty; + struct sci_port *s = to_sci_port(port); int copied = 0; + /* + * XXX: Technically not limited to non-SCIFs, it's simply the + * SCLSR check that is for the moment SCIF-specific. This + * probably wants to be revisited for SCIFA/B as well as for + * factoring in SCI overrun detection. + */ if (port->type != PORT_SCIF) return 0; - if ((sci_in(port, SCLSR) & SCIF_ORER) != 0) { + if ((sci_in(port, SCLSR) & (1 << s->cfg->overrun_bit))) { sci_out(port, SCLSR, 0); tty_insert_flip_char(tty, 0, TTY_OVERRUN); @@ -1755,6 +1768,32 @@ static int __devinit sci_init_single(struct platform_device *dev, sci_port->break_timer.function = sci_break_timer; init_timer(&sci_port->break_timer); + /* + * Establish some sensible defaults for the error detection. + */ + if (!p->error_mask) + p->error_mask = (p->type == PORT_SCI) ? + SCI_DEFAULT_ERROR_MASK : SCIF_DEFAULT_ERROR_MASK; + + /* + * Establish sensible defaults for the overrun detection, unless + * the part has explicitly disabled support for it. + */ + if (p->overrun_bit != SCIx_NOT_SUPPORTED) { + if (p->type == PORT_SCI) + p->overrun_bit = 5; + else if (p->scbrr_algo_id == SCBRR_ALGO_4) + p->overrun_bit = 9; + else + p->overrun_bit = 0; + + /* + * Make the error mask inclusive of overrun detection, if + * supported. + */ + p->error_mask |= (1 << p->overrun_bit); + } + sci_port->cfg = p; port->mapbase = p->mapbase; diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index ed1c09c0454a..caab353a98b5 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -19,11 +19,9 @@ defined(CONFIG_ARCH_SH7372) # define PORT_PTCR 0xA405011EUL # define PORT_PVCR 0xA4050122UL -# define SCIF_ORER 0x0200 /* overrun error bit */ #elif defined(CONFIG_SH_RTS7751R2D) # define SCSPTR1 0xFFE0001C /* 8 bit SCIF */ # define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ @@ -32,15 +30,12 @@ defined(CONFIG_CPU_SUBTYPE_SH7751R) # define SCSPTR1 0xffe0001c /* 8 bit SCI */ # define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7760) # define SCSPTR0 0xfe600024 /* 16 bit SCIF */ # define SCSPTR1 0xfe610024 /* 16 bit SCIF */ # define SCSPTR2 0xfe620024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) # define SCSPTR0 0xA4400000 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ # define PACR 0xa4050100 # define PBCR 0xa4050102 #elif defined(CONFIG_CPU_SUBTYPE_SH7343) @@ -48,35 +43,24 @@ #elif defined(CONFIG_CPU_SUBTYPE_SH7722) # define PWDR 0xA4050166 # define PSCR 0xA405011E -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7366) # define SCPDR0 0xA405013E /* 16 bit SCIF0 PSDR */ # define SCSPTR0 SCPDR0 -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7723) # define SCSPTR0 0xa4050160 -# define SCIF_ORER 0x0001 /* overrun error bit */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7724) -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH4_202) # define SCSPTR2 0xffe80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7757) # define SCSPTR0 0xfe4b0020 -# define SCIF_ORER 0x0001 #elif defined(CONFIG_CPU_SUBTYPE_SH7763) # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7770) # define SCSPTR0 0xff923020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7780) # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* Overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \ defined(CONFIG_CPU_SUBTYPE_SH7786) # define SCSPTR0 0xffea0024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* Overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \ defined(CONFIG_CPU_SUBTYPE_SH7203) || \ defined(CONFIG_CPU_SUBTYPE_SH7206) || \ @@ -84,36 +68,12 @@ # define SCSPTR0 0xfffe8020 /* 16 bit SCIF */ #elif defined(CONFIG_CPU_SUBTYPE_SH7619) # define SCSPTR0 0xf8400020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ #elif defined(CONFIG_CPU_SUBTYPE_SHX3) # define SCSPTR0 0xffc30020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* Overrun error bit */ #else # error CPU subtype not defined #endif -/* SCxSR SCI */ -#define SCI_TDRE 0x80 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_RDRF 0x40 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_ORER 0x20 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_FER 0x10 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_PER 0x08 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_TEND 0x04 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_MPB 0x02 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_MPBT 0x01 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ - -#define SCI_ERRORS ( SCI_PER | SCI_FER | SCI_ORER) - -/* SCxSR SCIF */ -#define SCIF_ER 0x0080 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_TEND 0x0040 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_TDFE 0x0020 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_BRK 0x0010 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_FER 0x0008 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_PER 0x0004 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_RDF 0x0002 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_DR 0x0001 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ - #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ @@ -121,35 +81,27 @@ defined(CONFIG_ARCH_SH7367) || \ defined(CONFIG_ARCH_SH7377) || \ defined(CONFIG_ARCH_SH7372) -# define SCIF_ORER 0x0200 -# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER) # define SCIF_RFDC_MASK 0x007f # define SCIF_TXROOM_MAX 64 #elif defined(CONFIG_CPU_SUBTYPE_SH7763) -# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK ) # define SCIF_RFDC_MASK 0x007f # define SCIF_TXROOM_MAX 64 /* SH7763 SCIF2 support */ # define SCIF2_RFDC_MASK 0x001f # define SCIF2_TXROOM_MAX 16 #else -# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) # define SCIF_RFDC_MASK 0x001f # define SCIF_TXROOM_MAX 16 #endif -#ifndef SCIF_ORER -#define SCIF_ORER 0x0000 -#endif - #define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) -#define SCxSR_ERRORS(port) (((port)->type == PORT_SCI) ? SCI_ERRORS : SCIF_ERRORS) #define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) #define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE) #define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER) #define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER) #define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK) -#define SCxSR_ORER(port) (((port)->type == PORT_SCI) ? SCI_ORER : SCIF_ORER) + +#define SCxSR_ERRORS(port) (to_sci_port(port)->cfg->error_mask) #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index a2afc9fbe186..5fac3bccfd87 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -8,6 +8,8 @@ * Generic header for SuperH SCI(F) (used by sh/sh64/h8300 and related parts) */ +#define SCIx_NOT_SUPPORTED (-1) + enum { SCBRR_ALGO_1, /* ((clk + 16 * bps) / (16 * bps) - 1) */ SCBRR_ALGO_2, /* ((clk + 16 * bps) / (32 * bps) - 1) */ @@ -25,6 +27,28 @@ enum { #define SCSCR_CKE1 (1 << 1) #define SCSCR_CKE0 (1 << 0) +/* SCxSR SCI */ +#define SCI_TDRE 0x80 +#define SCI_RDRF 0x40 +#define SCI_ORER 0x20 +#define SCI_FER 0x10 +#define SCI_PER 0x08 +#define SCI_TEND 0x04 + +#define SCI_DEFAULT_ERROR_MASK (SCI_PER | SCI_FER) + +/* SCxSR SCIF */ +#define SCIF_ER 0x0080 +#define SCIF_TEND 0x0040 +#define SCIF_TDFE 0x0020 +#define SCIF_BRK 0x0010 +#define SCIF_FER 0x0008 +#define SCIF_PER 0x0004 +#define SCIF_RDF 0x0002 +#define SCIF_DR 0x0001 + +#define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) + /* Offsets into the sci_port->irqs array */ enum { SCIx_ERI_IRQ, @@ -56,6 +80,12 @@ struct plat_sci_port { unsigned int scbrr_algo_id; /* SCBRR calculation algo */ unsigned int scscr; /* SCSCR initialization */ + /* + * Platform overrides if necessary, defaults otherwise. + */ + int overrun_bit; + unsigned int error_mask; + struct device *dma_dev; unsigned int dma_slave_tx; -- cgit v1.2.3 From 514820eb982eb85677ed2ecef9710e90e24fbdab Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 8 Jun 2011 18:51:32 +0900 Subject: serial: sh-sci: Consolidate RXD pin handling. Non-SCI parts do not have the special port reg necessary for cases where the RX and SCI pins are muxed and need to be manually polled, so these like always fall back on the normal FIFO processing paths. SH7760 is in a class in and of itself with regards to mapping its SIM card interface via the SCI port class despite not having any of the RXD lines wired up and so implicitly behaving more like a SCIF in this regard. Out of the other CPUs, some support the port check via the same block while others do it through an external SuperI/O, so it's not even possible to perform the check relative to the ioremapped cookie offset, so the separate read semantics are preserved here, too. Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh3/setup-sh770x.c | 1 + arch/sh/kernel/cpu/sh4/setup-sh7750.c | 1 + drivers/tty/serial/sh-sci.c | 13 +++++++++++++ drivers/tty/serial/sh-sci.h | 29 ----------------------------- include/linux/serial_sci.h | 2 ++ 5 files changed, 17 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c index 4551ad647c2c..6d549792f791 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh770x.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c @@ -108,6 +108,7 @@ static struct platform_device rtc_device = { static struct plat_sci_port scif0_platform_data = { .mapbase = 0xfffffe80, + .port_reg = 0xa4000136, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_TE | SCSCR_RE, .scbrr_algo_id = SCBRR_ALGO_2, diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c index e53b4b38bd11..8ea26e791187 100644 --- a/arch/sh/kernel/cpu/sh4/setup-sh7750.c +++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c @@ -38,6 +38,7 @@ static struct platform_device rtc_device = { static struct plat_sci_port sci_platform_data = { .mapbase = 0xffe00000, + .port_reg = 0xffe0001C .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_TE | SCSCR_RE, .scbrr_algo_id = SCBRR_ALGO_2, diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index bb27885ea2e5..3248ddaa889d 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -363,6 +363,19 @@ static int sci_rxfill(struct uart_port *port) return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; } +/* + * SCI helper for checking the state of the muxed port/RXD pins. + */ +static inline int sci_rxd_in(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + + if (s->cfg->port_reg <= 0) + return 1; + + return !!__raw_readb(s->cfg->port_reg); +} + /* ********************************************************************** * * the interrupt related routines * * ********************************************************************** */ diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index caab353a98b5..1c20f7f9ba4f 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -287,32 +287,3 @@ SCIF_FNS(SCLSR, 0, 0, 0x24, 16) #endif #define sci_in(port, reg) sci_##reg##_in(port) #define sci_out(port, reg, value) sci_##reg##_out(port, value) - -#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ - defined(CONFIG_CPU_SUBTYPE_SH7707) || \ - defined(CONFIG_CPU_SUBTYPE_SH7708) || \ - defined(CONFIG_CPU_SUBTYPE_SH7709) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xfffffe80) - return __raw_readb(SCPDR)&0x01 ? 1 : 0; /* SCI */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ - defined(CONFIG_CPU_SUBTYPE_SH7091) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000) - return __raw_readb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */ - return 1; -} -#else /* default case for non-SCI processors */ -static inline int sci_rxd_in(struct uart_port *port) -{ - return 1; -} -#endif diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 5fac3bccfd87..ecefec7c0b67 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -86,6 +86,8 @@ struct plat_sci_port { int overrun_bit; unsigned int error_mask; + int port_reg; + struct device *dma_dev; unsigned int dma_slave_tx; -- cgit v1.2.3 From 4519de9a0478d8de438f8b80ab2e94668ef63ab4 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 28 Apr 2011 17:55:53 -0300 Subject: Bluetooth: Create __l2cap_chan_close() This is actually __l2cap_sock_close() renamed to __l2cap_chan_close(). At a first look it may not make sense, but with the further cleanups that will come it will. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 5 +-- net/bluetooth/l2cap_core.c | 97 ++++++++++++++++++++++++++++++++++++++++--- net/bluetooth/l2cap_sock.c | 85 +------------------------------------ 3 files changed, 94 insertions(+), 93 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d09c9b1118e3..ed75e654ea61 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -446,7 +446,6 @@ extern int disable_ertm; int l2cap_init_sockets(void); void l2cap_cleanup_sockets(void); -void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data); void __l2cap_connect_rsp_defer(struct l2cap_chan *chan); int __l2cap_wait_ack(struct sock *sk); @@ -463,14 +462,12 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); void l2cap_sock_set_timer(struct sock *sk, long timeout); void l2cap_sock_clear_timer(struct sock *sk); -void __l2cap_sock_close(struct sock *sk, int reason); void l2cap_sock_kill(struct sock *sk); void l2cap_sock_init(struct sock *sk, struct sock *parent); struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); -void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err); struct l2cap_chan *l2cap_chan_create(struct sock *sk); -void l2cap_chan_del(struct l2cap_chan *chan, int err); +void __l2cap_chan_close(struct l2cap_chan *chan, int reason); void l2cap_chan_destroy(struct l2cap_chan *chan); int l2cap_chan_connect(struct l2cap_chan *chan); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index af1bc3085c44..14c760c8ffe7 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -69,7 +69,11 @@ static void l2cap_busy_work(struct work_struct *work); static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data); +static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, + void *data); static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data); +static void l2cap_send_disconn_req(struct l2cap_conn *conn, + struct l2cap_chan *chan, int err); static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb); @@ -271,7 +275,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) /* Delete channel. * Must be called on the locked socket. */ -void l2cap_chan_del(struct l2cap_chan *chan, int err) +static void l2cap_chan_del(struct l2cap_chan *chan, int err) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; @@ -327,6 +331,87 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) } } +/* Must be called on unlocked socket. */ +static void l2cap_chan_close(struct sock *sk) +{ + l2cap_sock_clear_timer(sk); + lock_sock(sk); + __l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET); + release_sock(sk); + l2cap_sock_kill(sk); +} + +static void l2cap_chan_cleanup_listen(struct sock *parent) +{ + struct sock *sk; + + BT_DBG("parent %p", parent); + + /* Close not yet accepted channels */ + while ((sk = bt_accept_dequeue(parent, NULL))) + l2cap_chan_close(sk); + + parent->sk_state = BT_CLOSED; + sock_set_flag(parent, SOCK_ZAPPED); +} + +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, sk->sk_state, sk->sk_socket); + + switch (sk->sk_state) { + case BT_LISTEN: + l2cap_chan_cleanup_listen(sk); + break; + + case BT_CONNECTED: + case BT_CONFIG: + if ((sk->sk_type == SOCK_SEQPACKET || + sk->sk_type == SOCK_STREAM) && + conn->hcon->type == ACL_LINK) { + l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_send_disconn_req(conn, chan, reason); + } else + l2cap_chan_del(chan, reason); + break; + + case BT_CONNECT2: + if ((sk->sk_type == SOCK_SEQPACKET || + sk->sk_type == SOCK_STREAM) && + conn->hcon->type == ACL_LINK) { + struct l2cap_conn_rsp rsp; + __u16 result; + + if (bt_sk(sk)->defer_setup) + result = L2CAP_CR_SEC_BLOCK; + else + result = L2CAP_CR_BAD_PSM; + + rsp.scid = cpu_to_le16(chan->dcid); + rsp.dcid = cpu_to_le16(chan->scid); + rsp.result = cpu_to_le16(result); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, + sizeof(rsp), &rsp); + } + + l2cap_chan_del(chan, reason); + break; + + case BT_CONNECT: + case BT_DISCONN: + l2cap_chan_del(chan, reason); + break; + + default: + sock_set_flag(sk, SOCK_ZAPPED); + break; + } +} + static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) { struct sock *sk = chan->sk; @@ -393,7 +478,7 @@ u8 l2cap_get_ident(struct l2cap_conn *conn) return id; } -void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) +static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) { struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); u8 flags; @@ -533,7 +618,7 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) } } -void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err) +static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err) { struct sock *sk; struct l2cap_disconn_req req; @@ -591,10 +676,10 @@ static void l2cap_conn_start(struct l2cap_conn *conn) conn->feat_mask) && chan->conf_state & L2CAP_CONF_STATE2_DEVICE) { - /* __l2cap_sock_close() calls list_del(chan) + /* __l2cap_chan_close() calls list_del(chan) * so release the lock */ read_unlock_bh(&conn->chan_lock); - __l2cap_sock_close(sk, ECONNRESET); + __l2cap_chan_close(chan, ECONNRESET); read_lock_bh(&conn->chan_lock); bh_unlock_sock(sk); continue; @@ -3943,7 +4028,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) l2cap_sock_clear_timer(sk); l2cap_sock_set_timer(sk, HZ * 5); } else if (chan->sec_level == BT_SECURITY_HIGH) - __l2cap_sock_close(sk, ECONNREFUSED); + __l2cap_chan_close(chan, ECONNREFUSED); } else { if (chan->sec_level == BT_SECURITY_MEDIUM) l2cap_sock_clear_timer(sk); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 18dc9888d8c2..290130ca1c4a 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -58,7 +58,7 @@ static void l2cap_sock_timeout(unsigned long arg) else reason = ETIMEDOUT; - __l2cap_sock_close(sk, reason); + __l2cap_chan_close(l2cap_pi(sk)->chan, reason); bh_unlock_sock(sk); @@ -813,87 +813,6 @@ void l2cap_sock_kill(struct sock *sk) sock_put(sk); } -/* Must be called on unlocked socket. */ -static void l2cap_sock_close(struct sock *sk) -{ - l2cap_sock_clear_timer(sk); - lock_sock(sk); - __l2cap_sock_close(sk, ECONNRESET); - release_sock(sk); - l2cap_sock_kill(sk); -} - -static void l2cap_sock_cleanup_listen(struct sock *parent) -{ - struct sock *sk; - - BT_DBG("parent %p", parent); - - /* Close not yet accepted channels */ - while ((sk = bt_accept_dequeue(parent, NULL))) - l2cap_sock_close(sk); - - parent->sk_state = BT_CLOSED; - sock_set_flag(parent, SOCK_ZAPPED); -} - -void __l2cap_sock_close(struct sock *sk, int reason) -{ - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - struct l2cap_conn *conn = chan->conn; - - BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); - - switch (sk->sk_state) { - case BT_LISTEN: - l2cap_sock_cleanup_listen(sk); - break; - - case BT_CONNECTED: - case BT_CONFIG: - if ((sk->sk_type == SOCK_SEQPACKET || - sk->sk_type == SOCK_STREAM) && - conn->hcon->type == ACL_LINK) { - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); - l2cap_send_disconn_req(conn, chan, reason); - } else - l2cap_chan_del(chan, reason); - break; - - case BT_CONNECT2: - if ((sk->sk_type == SOCK_SEQPACKET || - sk->sk_type == SOCK_STREAM) && - conn->hcon->type == ACL_LINK) { - struct l2cap_conn_rsp rsp; - __u16 result; - - if (bt_sk(sk)->defer_setup) - result = L2CAP_CR_SEC_BLOCK; - else - result = L2CAP_CR_BAD_PSM; - - rsp.scid = cpu_to_le16(chan->dcid); - rsp.dcid = cpu_to_le16(chan->scid); - rsp.result = cpu_to_le16(result); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); - } - - l2cap_chan_del(chan, reason); - break; - - case BT_CONNECT: - case BT_DISCONN: - l2cap_chan_del(chan, reason); - break; - - default: - sock_set_flag(sk, SOCK_ZAPPED); - break; - } -} - static int l2cap_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; @@ -912,7 +831,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) sk->sk_shutdown = SHUTDOWN_MASK; l2cap_sock_clear_timer(sk); - __l2cap_sock_close(sk, 0); + __l2cap_chan_close(chan, 0); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) err = bt_sock_wait_state(sk, BT_CLOSED, -- cgit v1.2.3 From 9a91a04a95d30a18909e2aec9d7b17b4c86088a7 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 28 Apr 2011 18:50:17 -0300 Subject: Bluetooth: Create l2cap_chan_send() This move all the sending logic to l2cap_core.c, but we still have a socket dependence there, struct msghdr. It will be removed in some of the further commits. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 9 +---- net/bluetooth/l2cap_core.c | 80 +++++++++++++++++++++++++++++++++++++++++ net/bluetooth/l2cap_sock.c | 83 ++----------------------------------------- 3 files changed, 84 insertions(+), 88 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index ed75e654ea61..dc721cad7f23 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -449,14 +449,6 @@ void l2cap_cleanup_sockets(void); void __l2cap_connect_rsp_defer(struct l2cap_chan *chan); int __l2cap_wait_ack(struct sock *sk); -struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len); -struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len); -struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u16 control, u16 sdulen); -int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len); -void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb); -void l2cap_streaming_send(struct l2cap_chan *chan); -int l2cap_ertm_send(struct l2cap_chan *chan); - int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); @@ -470,5 +462,6 @@ 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); int l2cap_chan_connect(struct l2cap_chan *chan); +int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 14c760c8ffe7..e65f63130113 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1535,6 +1535,86 @@ int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t le return size; } +int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +{ + struct sock *sk = chan->sk; + struct sk_buff *skb; + u16 control; + int err; + + /* Connectionless channel */ + if (sk->sk_type == SOCK_DGRAM) { + skb = l2cap_create_connless_pdu(chan, msg, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + l2cap_do_send(chan, skb); + return len; + } + + switch (chan->mode) { + case L2CAP_MODE_BASIC: + /* Check outgoing MTU */ + if (len > chan->omtu) + return -EMSGSIZE; + + /* Create a basic PDU */ + skb = l2cap_create_basic_pdu(chan, msg, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + l2cap_do_send(chan, skb); + err = len; + break; + + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + /* Entire SDU fits into one PDU */ + if (len <= chan->remote_mps) { + control = L2CAP_SDU_UNSEGMENTED; + skb = l2cap_create_iframe_pdu(chan, msg, len, control, + 0); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + __skb_queue_tail(&chan->tx_q, skb); + + if (chan->tx_send_head == NULL) + chan->tx_send_head = skb; + + } else { + /* Segment SDU into multiples PDUs */ + err = l2cap_sar_segment_sdu(chan, msg, len); + if (err < 0) + return err; + } + + if (chan->mode == L2CAP_MODE_STREAMING) { + l2cap_streaming_send(chan); + err = len; + break; + } + + if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && + (chan->conn_state & L2CAP_CONN_WAIT_F)) { + err = len; + break; + } + + err = l2cap_ertm_send(chan); + if (err >= 0) + err = len; + + break; + + default: + BT_DBG("bad state %1.1x", chan->mode); + err = -EBADFD; + } + + return err; +} + static void l2cap_chan_ready(struct sock *sk) { struct sock *parent = bt_sk(sk)->parent; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 290130ca1c4a..0ecf214bd30e 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -673,8 +673,6 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; - struct sk_buff *skb; - u16 control; int err; BT_DBG("sock %p, sk %p", sock, sk); @@ -689,87 +687,12 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms lock_sock(sk); if (sk->sk_state != BT_CONNECTED) { - err = -ENOTCONN; - goto done; + release_sock(sk); + return -ENOTCONN; } - /* Connectionless channel */ - if (sk->sk_type == SOCK_DGRAM) { - skb = l2cap_create_connless_pdu(chan, msg, len); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - } else { - l2cap_do_send(chan, skb); - err = len; - } - goto done; - } + err = l2cap_chan_send(chan, msg, len); - switch (chan->mode) { - case L2CAP_MODE_BASIC: - /* Check outgoing MTU */ - if (len > chan->omtu) { - err = -EMSGSIZE; - goto done; - } - - /* Create a basic PDU */ - skb = l2cap_create_basic_pdu(chan, msg, len); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - goto done; - } - - l2cap_do_send(chan, skb); - err = len; - break; - - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - /* Entire SDU fits into one PDU */ - if (len <= chan->remote_mps) { - control = L2CAP_SDU_UNSEGMENTED; - skb = l2cap_create_iframe_pdu(chan, msg, len, control, - 0); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - goto done; - } - __skb_queue_tail(&chan->tx_q, skb); - - if (chan->tx_send_head == NULL) - chan->tx_send_head = skb; - - } else { - /* Segment SDU into multiples PDUs */ - err = l2cap_sar_segment_sdu(chan, msg, len); - if (err < 0) - goto done; - } - - if (chan->mode == L2CAP_MODE_STREAMING) { - l2cap_streaming_send(chan); - err = len; - break; - } - - if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && - (chan->conn_state & L2CAP_CONN_WAIT_F)) { - err = len; - break; - } - err = l2cap_ertm_send(chan); - - if (err >= 0) - err = len; - break; - - default: - BT_DBG("bad state %1.1x", chan->mode); - err = -EBADFD; - } - -done: release_sock(sk); return err; } -- cgit v1.2.3 From 715ec005cd10c5d53109ffe513e4d403644e3e48 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 2 May 2011 17:13:55 -0300 Subject: Bluetooth: Add chan->chan_type struct member chan_type says if our chan is raw(direclty access to HCI), connection less or connection oriented. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 5 +++++ net/bluetooth/l2cap_core.c | 30 +++++++++++------------------- net/bluetooth/l2cap_sock.c | 29 ++++++++++++++++++++++------- 3 files changed, 38 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index dc721cad7f23..094a7ac16ffe 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -295,6 +295,7 @@ struct l2cap_chan { __u16 omtu; __u16 flush_to; __u8 mode; + __u8 chan_type; __le16 sport; @@ -384,6 +385,10 @@ struct l2cap_conn { #define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x04 #define L2CAP_INFO_FEAT_MASK_REQ_DONE 0x08 +#define L2CAP_CHAN_RAW 1 +#define L2CAP_CHAN_CONN_LESS 2 +#define L2CAP_CHAN_CONN_ORIENTED 3 + /* ----- L2CAP socket info ----- */ #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e65f63130113..f5e50bf7ace0 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -245,7 +245,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->conn = conn; - if (sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) { + if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) { if (conn->hcon->type == LE_LINK) { /* LE connection */ chan->omtu = L2CAP_LE_DEFAULT_MTU; @@ -256,7 +256,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->scid = l2cap_alloc_cid(conn); chan->omtu = L2CAP_DEFAULT_MTU; } - } else if (sk->sk_type == SOCK_DGRAM) { + } else if (chan->chan_type == L2CAP_CHAN_CONN_LESS) { /* Connectionless socket */ chan->scid = L2CAP_CID_CONN_LESS; chan->dcid = L2CAP_CID_CONN_LESS; @@ -369,8 +369,7 @@ void __l2cap_chan_close(struct l2cap_chan *chan, int reason) case BT_CONNECTED: case BT_CONFIG: - if ((sk->sk_type == SOCK_SEQPACKET || - sk->sk_type == SOCK_STREAM) && + if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { l2cap_sock_set_timer(sk, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, chan, reason); @@ -379,8 +378,7 @@ void __l2cap_chan_close(struct l2cap_chan *chan, int reason) break; case BT_CONNECT2: - if ((sk->sk_type == SOCK_SEQPACKET || - sk->sk_type == SOCK_STREAM) && + if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { struct l2cap_conn_rsp rsp; __u16 result; @@ -414,9 +412,7 @@ void __l2cap_chan_close(struct l2cap_chan *chan, int reason) static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) { - struct sock *sk = chan->sk; - - if (sk->sk_type == SOCK_RAW) { + if (chan->chan_type == L2CAP_CHAN_RAW) { switch (chan->sec_level) { case BT_SECURITY_HIGH: return HCI_AT_DEDICATED_BONDING_MITM; @@ -657,8 +653,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) bh_lock_sock(sk); - if (sk->sk_type != SOCK_SEQPACKET && - sk->sk_type != SOCK_STREAM) { + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { bh_unlock_sock(sk); continue; } @@ -852,8 +847,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) sk->sk_state_change(sk); } - if (sk->sk_type != SOCK_SEQPACKET && - sk->sk_type != SOCK_STREAM) { + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { l2cap_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); @@ -1056,8 +1050,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan) l2cap_sock_set_timer(sk, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { - if (sk->sk_type != SOCK_SEQPACKET && - sk->sk_type != SOCK_STREAM) { + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { l2cap_sock_clear_timer(sk); if (l2cap_check_security(chan)) sk->sk_state = BT_CONNECTED; @@ -1537,13 +1530,12 @@ int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t le int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { - struct sock *sk = chan->sk; struct sk_buff *skb; u16 control; int err; /* Connectionless channel */ - if (sk->sk_type == SOCK_DGRAM) { + if (chan->chan_type == L2CAP_CHAN_CONN_LESS) { skb = l2cap_create_connless_pdu(chan, msg, len); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -1650,7 +1642,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) read_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { struct sock *sk = chan->sk; - if (sk->sk_type != SOCK_RAW) + if (chan->chan_type != L2CAP_CHAN_RAW) continue; /* Don't send frame to the socket it came from */ @@ -4100,7 +4092,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) { struct sock *sk = chan->sk; - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM) + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) return; if (encrypt == 0x00) { diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 0ecf214bd30e..2fcdf5eacb4d 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -162,7 +162,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al lock_sock(sk); - if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) + if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(la.l2_psm || la.l2_cid)) { err = -EINVAL; goto done; @@ -204,8 +204,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al } /* PSM must be odd and lsb of upper byte must be 0 */ - if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && - sk->sk_type != SOCK_RAW && !la.l2_cid) { + if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && !la.l2_cid && + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; goto done; } @@ -453,8 +453,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM - && sk->sk_type != SOCK_RAW) { + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } @@ -599,8 +599,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM - && sk->sk_type != SOCK_RAW) { + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } @@ -806,6 +806,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) sk->sk_type = parent->sk_type; bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup; + chan->chan_type = pchan->chan_type; chan->imtu = pchan->imtu; chan->omtu = pchan->omtu; chan->conf_state = pchan->conf_state; @@ -818,6 +819,20 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->force_reliable = pchan->force_reliable; chan->flushable = pchan->flushable; } else { + + switch (sk->sk_type) { + case SOCK_RAW: + chan->chan_type = L2CAP_CHAN_RAW; + break; + case SOCK_DGRAM: + chan->chan_type = L2CAP_CHAN_CONN_LESS; + break; + case SOCK_SEQPACKET: + case SOCK_STREAM: + chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; + break; + } + chan->imtu = L2CAP_DEFAULT_MTU; chan->omtu = 0; if (!disable_ertm && sk->sk_type == SOCK_STREAM) { -- cgit v1.2.3 From ab07801d28985090ac38047b5a4d8952a7e1689f Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 2 May 2011 18:25:01 -0300 Subject: Bluetooth: create channel timer to replace sk_timer The new timer does not belong to struct sock, tought it still touch some sock things, but this will be sorted out soon. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 5 ++- net/bluetooth/l2cap_core.c | 100 +++++++++++++++++++++++++++++++----------- net/bluetooth/l2cap_sock.c | 50 +-------------------- 3 files changed, 79 insertions(+), 76 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 094a7ac16ffe..efb1fc4f3a03 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -340,6 +340,7 @@ struct l2cap_chan { __u8 remote_max_tx; __u16 remote_mps; + struct timer_list chan_timer; struct timer_list retrans_timer; struct timer_list monitor_timer; struct timer_list ack_timer; @@ -457,12 +458,12 @@ int __l2cap_wait_ack(struct sock *sk); int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); -void l2cap_sock_set_timer(struct sock *sk, long timeout); -void l2cap_sock_clear_timer(struct sock *sk); void l2cap_sock_kill(struct sock *sk); void l2cap_sock_init(struct sock *sk, struct sock *parent); struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); + +void l2cap_chan_clear_timer(struct l2cap_chan *chan); 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); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f5e50bf7ace0..fce48f9b85bf 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -208,6 +208,56 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) return 0; } +static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout) +{ + BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->sk->sk_state, + timeout); + if (!mod_timer(&chan->chan_timer, jiffies + timeout)) + sock_hold(chan->sk); +} + +void l2cap_chan_clear_timer(struct l2cap_chan *chan) +{ + BT_DBG("chan %p state %d", chan, chan->sk->sk_state); + + if (timer_pending(&chan->chan_timer) && del_timer(&chan->chan_timer)) + __sock_put(chan->sk); +} + +static void l2cap_chan_timeout(unsigned long arg) +{ + struct l2cap_chan *chan = (struct l2cap_chan *) arg; + struct sock *sk = chan->sk; + int reason; + + BT_DBG("chan %p state %d", chan, sk->sk_state); + + bh_lock_sock(sk); + + if (sock_owned_by_user(sk)) { + /* sk is owned by user. Try again later */ + l2cap_chan_set_timer(chan, HZ / 5); + bh_unlock_sock(sk); + sock_put(sk); + return; + } + + if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) + reason = ECONNREFUSED; + else if (sk->sk_state == BT_CONNECT && + chan->sec_level != BT_SECURITY_SDP) + reason = ECONNREFUSED; + else + reason = ETIMEDOUT; + + __l2cap_chan_close(chan, reason); + + bh_unlock_sock(sk); + + l2cap_sock_kill(sk); + sock_put(sk); +} + struct l2cap_chan *l2cap_chan_create(struct sock *sk) { struct l2cap_chan *chan; @@ -222,6 +272,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk) list_add(&chan->global_l, &chan_list); write_unlock_bh(&chan_list_lock); + setup_timer(&chan->chan_timer, l2cap_chan_timeout, (unsigned long) chan); + return chan; } @@ -281,7 +333,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) struct l2cap_conn *conn = chan->conn; struct sock *parent = bt_sk(sk)->parent; - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); BT_DBG("chan %p, conn %p, err %d", chan, conn, err); @@ -334,7 +386,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) /* Must be called on unlocked socket. */ static void l2cap_chan_close(struct sock *sk) { - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(l2cap_pi(sk)->chan); lock_sock(sk); __l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET); release_sock(sk); @@ -371,7 +423,7 @@ void __l2cap_chan_close(struct l2cap_chan *chan, int reason) case BT_CONFIG: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_chan_set_timer(chan, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, chan, reason); } else l2cap_chan_del(chan, reason); @@ -814,7 +866,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) __l2cap_chan_add(conn, chan); - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_chan_set_timer(chan, sk->sk_sndtimeo); sk->sk_state = BT_CONNECTED; parent->sk_data_ready(parent, 0); @@ -842,13 +894,13 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) bh_lock_sock(sk); if (conn->hcon->type == LE_LINK) { - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); } if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); } else if (sk->sk_state == BT_CONNECT) @@ -1047,11 +1099,11 @@ int l2cap_chan_connect(struct l2cap_chan *chan) l2cap_chan_add(conn, chan); sk->sk_state = BT_CONNECT; - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_chan_set_timer(chan, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); if (l2cap_check_security(chan)) sk->sk_state = BT_CONNECTED; } else @@ -1615,7 +1667,7 @@ static void l2cap_chan_ready(struct sock *sk) BT_DBG("sk %p, parent %p", sk, parent); chan->conf_state = 0; - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); if (!parent) { /* Outgoing channel. @@ -2317,7 +2369,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd dcid = chan->scid; - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_chan_set_timer(chan, sk->sk_sndtimeo); chan->ident = cmd->ident; @@ -2434,8 +2486,8 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { sk->sk_state = BT_DISCONN; - l2cap_sock_clear_timer(sk); - l2cap_sock_set_timer(sk, HZ / 5); + l2cap_chan_clear_timer(chan); + l2cap_chan_set_timer(chan, HZ / 5); break; } @@ -2608,7 +2660,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr default: sk->sk_err = ECONNRESET; - l2cap_sock_set_timer(sk, HZ * 5); + l2cap_chan_set_timer(chan, HZ * 5); l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } @@ -2664,8 +2716,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { sk->sk_state = BT_DISCONN; - l2cap_sock_clear_timer(sk); - l2cap_sock_set_timer(sk, HZ / 5); + l2cap_chan_clear_timer(chan); + l2cap_chan_set_timer(chan, HZ / 5); bh_unlock_sock(sk); return 0; } @@ -2698,8 +2750,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { sk->sk_state = BT_DISCONN; - l2cap_sock_clear_timer(sk); - l2cap_sock_set_timer(sk, HZ / 5); + l2cap_chan_clear_timer(chan); + l2cap_chan_set_timer(chan, HZ / 5); bh_unlock_sock(sk); return 0; } @@ -4090,20 +4142,18 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) { - struct sock *sk = chan->sk; - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) return; if (encrypt == 0x00) { if (chan->sec_level == BT_SECURITY_MEDIUM) { - l2cap_sock_clear_timer(sk); - l2cap_sock_set_timer(sk, HZ * 5); + l2cap_chan_clear_timer(chan); + l2cap_chan_set_timer(chan, HZ * 5); } else if (chan->sec_level == BT_SECURITY_HIGH) __l2cap_chan_close(chan, ECONNREFUSED); } else { if (chan->sec_level == BT_SECURITY_MEDIUM) - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); } } @@ -4148,8 +4198,8 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); } else { - l2cap_sock_clear_timer(sk); - l2cap_sock_set_timer(sk, HZ / 10); + l2cap_chan_clear_timer(chan); + l2cap_chan_set_timer(chan, HZ / 10); } } else if (sk->sk_state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; @@ -4160,7 +4210,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) result = L2CAP_CR_SUCCESS; } else { sk->sk_state = BT_DISCONN; - l2cap_sock_set_timer(sk, HZ / 10); + l2cap_chan_set_timer(chan, HZ / 10); result = L2CAP_CR_SEC_BLOCK; } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 2fcdf5eacb4d..89433a439bd8 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -32,52 +32,6 @@ static const struct proto_ops l2cap_sock_ops; -/* ---- L2CAP timers ---- */ -static void l2cap_sock_timeout(unsigned long arg) -{ - struct sock *sk = (struct sock *) arg; - int reason; - - BT_DBG("sock %p state %d", sk, sk->sk_state); - - bh_lock_sock(sk); - - if (sock_owned_by_user(sk)) { - /* sk is owned by user. Try again later */ - l2cap_sock_set_timer(sk, HZ / 5); - bh_unlock_sock(sk); - sock_put(sk); - return; - } - - if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) - reason = ECONNREFUSED; - else if (sk->sk_state == BT_CONNECT && - l2cap_pi(sk)->chan->sec_level != BT_SECURITY_SDP) - reason = ECONNREFUSED; - else - reason = ETIMEDOUT; - - __l2cap_chan_close(l2cap_pi(sk)->chan, reason); - - bh_unlock_sock(sk); - - l2cap_sock_kill(sk); - sock_put(sk); -} - -void l2cap_sock_set_timer(struct sock *sk, long timeout) -{ - BT_DBG("sk %p state %d timeout %ld", sk, sk->sk_state, timeout); - sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout); -} - -void l2cap_sock_clear_timer(struct sock *sk) -{ - BT_DBG("sock %p state %d", sk, sk->sk_state); - sk_stop_timer(sk, &sk->sk_timer); -} - static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { struct sock *sk = sock->sk; @@ -753,7 +707,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) err = __l2cap_wait_ack(sk); sk->sk_shutdown = SHUTDOWN_MASK; - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); __l2cap_chan_close(chan, 0); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) @@ -879,8 +833,6 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, g sk->sk_protocol = proto; sk->sk_state = BT_OPEN; - setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk); - return sk; } -- cgit v1.2.3 From 500698d3fd987f6c405d6d5f68fdf535a539e71e Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 4 May 2011 19:35:27 -0300 Subject: Bluetooth: Remove export of l2cap_chan_clear_timer() The call to l2cap_chan_clear_timer() is not really needed in l2cap_sock.c. This patch also adds a call to l2cap_chan_clear_timer() to the only place in __l2cap_sock_close() that wasn't calling it. It's safe call it there because l2cap_chan_clear_timer() check first for timer_peding(). Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 1 - net/bluetooth/l2cap_core.c | 3 ++- net/bluetooth/l2cap_sock.c | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index efb1fc4f3a03..1c89c7f03f86 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -463,7 +463,6 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent); struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); -void l2cap_chan_clear_timer(struct l2cap_chan *chan); 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); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fce48f9b85bf..248f68a9694b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -216,7 +216,7 @@ static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout) sock_hold(chan->sk); } -void l2cap_chan_clear_timer(struct l2cap_chan *chan) +static void l2cap_chan_clear_timer(struct l2cap_chan *chan) { BT_DBG("chan %p state %d", chan, chan->sk->sk_state); @@ -423,6 +423,7 @@ void __l2cap_chan_close(struct l2cap_chan *chan, int reason) case BT_CONFIG: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { + l2cap_chan_clear_timer(chan); l2cap_chan_set_timer(chan, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, chan, reason); } else diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 89433a439bd8..7d87d1f664cc 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -707,7 +707,6 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) err = __l2cap_wait_ack(sk); sk->sk_shutdown = SHUTDOWN_MASK; - l2cap_chan_clear_timer(chan); __l2cap_chan_close(chan, 0); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) -- cgit v1.2.3 From 0f8527249646bbe75d036fe8b9b84d662ece90a9 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 4 May 2011 19:42:50 -0300 Subject: Bluetooth: Rename __l2cap_chan_close() to l2cap_chan_close() To make it consistent with the rest of the API. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/l2cap_core.c | 29 ++++++++++++----------------- net/bluetooth/l2cap_sock.c | 2 +- 3 files changed, 14 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1c89c7f03f86..a2dcbfff1c51 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -464,7 +464,7 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); struct l2cap_chan *l2cap_chan_create(struct sock *sk); -void __l2cap_chan_close(struct l2cap_chan *chan, int reason); +void l2cap_chan_close(struct l2cap_chan *chan, int reason); void l2cap_chan_destroy(struct l2cap_chan *chan); int l2cap_chan_connect(struct l2cap_chan *chan); int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 248f68a9694b..c630a0e8c5f9 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -250,7 +250,7 @@ static void l2cap_chan_timeout(unsigned long arg) else reason = ETIMEDOUT; - __l2cap_chan_close(chan, reason); + l2cap_chan_close(chan, reason); bh_unlock_sock(sk); @@ -383,16 +383,6 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) } } -/* Must be called on unlocked socket. */ -static void l2cap_chan_close(struct sock *sk) -{ - l2cap_chan_clear_timer(l2cap_pi(sk)->chan); - lock_sock(sk); - __l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET); - release_sock(sk); - l2cap_sock_kill(sk); -} - static void l2cap_chan_cleanup_listen(struct sock *parent) { struct sock *sk; @@ -400,14 +390,19 @@ static void l2cap_chan_cleanup_listen(struct sock *parent) BT_DBG("parent %p", parent); /* Close not yet accepted channels */ - while ((sk = bt_accept_dequeue(parent, NULL))) - l2cap_chan_close(sk); + while ((sk = bt_accept_dequeue(parent, NULL))) { + l2cap_chan_clear_timer(l2cap_pi(sk)->chan); + lock_sock(sk); + l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET); + release_sock(sk); + l2cap_sock_kill(sk); + } parent->sk_state = BT_CLOSED; sock_set_flag(parent, SOCK_ZAPPED); } -void __l2cap_chan_close(struct l2cap_chan *chan, int reason) +void l2cap_chan_close(struct l2cap_chan *chan, int reason) { struct l2cap_conn *conn = chan->conn; struct sock *sk = chan->sk; @@ -724,10 +719,10 @@ static void l2cap_conn_start(struct l2cap_conn *conn) conn->feat_mask) && chan->conf_state & L2CAP_CONF_STATE2_DEVICE) { - /* __l2cap_chan_close() calls list_del(chan) + /* l2cap_chan_close() calls list_del(chan) * so release the lock */ read_unlock_bh(&conn->chan_lock); - __l2cap_chan_close(chan, ECONNRESET); + l2cap_chan_close(chan, ECONNRESET); read_lock_bh(&conn->chan_lock); bh_unlock_sock(sk); continue; @@ -4151,7 +4146,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) l2cap_chan_clear_timer(chan); l2cap_chan_set_timer(chan, HZ * 5); } else if (chan->sec_level == BT_SECURITY_HIGH) - __l2cap_chan_close(chan, ECONNREFUSED); + l2cap_chan_close(chan, ECONNREFUSED); } else { if (chan->sec_level == BT_SECURITY_MEDIUM) l2cap_chan_clear_timer(chan); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 7d87d1f664cc..b79fb7561836 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -707,7 +707,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) err = __l2cap_wait_ack(sk); sk->sk_shutdown = SHUTDOWN_MASK; - __l2cap_chan_close(chan, 0); + l2cap_chan_close(chan, 0); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) err = bt_sock_wait_state(sk, BT_CLOSED, -- cgit v1.2.3 From 57a56fd41b1264d639175726414ae7e510ec683b Mon Sep 17 00:00:00 2001 From: Anderson Briglia Date: Thu, 26 May 2011 16:23:49 -0300 Subject: Bluetooth: Add advertising report meta event structs This patch adds definitions and a new struct for Advertising Report Event from LE and Dual Mode controllers. Signed-off-by: Anderson Briglia Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 0c20227e57f6..9317798fabcd 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1029,6 +1029,25 @@ struct hci_ev_le_conn_complete { __u8 clk_accurancy; } __packed; +/* Advertising report event types */ +#define ADV_IND 0x00 +#define ADV_DIRECT_IND 0x01 +#define ADV_SCAN_IND 0x02 +#define ADV_NONCONN_IND 0x03 +#define ADV_SCAN_RSP 0x04 + +#define ADDR_LE_DEV_PUBLIC 0x00 +#define ADDR_LE_DEV_RANDOM 0x01 + +#define HCI_EV_LE_ADVERTISING_REPORT 0x02 +struct hci_ev_le_advertising_info { + __u8 evt_type; + __u8 bdaddr_type; + bdaddr_t bdaddr; + __u8 length; + __u8 data[0]; +} __packed; + /* Internal events generated by Bluetooth stack */ #define HCI_EV_STACK_INTERNAL 0xfd struct hci_ev_stack_internal { -- cgit v1.2.3 From 76c8686f8871f1bcb2dc8b4c5311cd0e2f73d4cd Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 26 May 2011 16:23:50 -0300 Subject: Bluetooth: LE advertising cache This patch implements the LE advertising cache. It stores sensitive information (bdaddr and bdaddr_type so far) gathered from LE advertising report events. Only advertising entries from connectables devices are added to the cache. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 13 ++++++++ net/bluetooth/hci_core.c | 64 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6c994c004d15..10dfb85ad6a1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -89,6 +89,12 @@ struct oob_data { u8 randomizer[16]; }; +struct adv_entry { + struct list_head list; + bdaddr_t bdaddr; + u8 bdaddr_type; +}; + #define NUM_REASSEMBLY 4 struct hci_dev { struct list_head list; @@ -181,6 +187,8 @@ struct hci_dev { struct list_head remote_oob_data; + struct list_head adv_entries; + struct hci_dev_stats stat; struct sk_buff_head driver_init; @@ -527,6 +535,11 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, u8 *randomizer); int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_adv_entries_clear(struct hci_dev *hdev); +struct adv_entry *hci_find_adv_entry(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_add_adv_entry(struct hci_dev *hdev, + struct hci_ev_le_advertising_info *ev); + void hci_del_off_timer(struct hci_dev *hdev); void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 815269b07f20..cc40f221f5e7 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1202,6 +1202,67 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, return 0; } +int hci_adv_entries_clear(struct hci_dev *hdev) +{ + struct adv_entry *entry, *tmp; + + list_for_each_entry_safe(entry, tmp, &hdev->adv_entries, list) { + list_del(&entry->list); + kfree(entry); + } + + BT_DBG("%s adv cache cleared", hdev->name); + + return 0; +} + +struct adv_entry *hci_find_adv_entry(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct adv_entry *entry; + + list_for_each_entry(entry, &hdev->adv_entries, list) + if (bacmp(bdaddr, &entry->bdaddr) == 0) + return entry; + + return NULL; +} + +static inline int is_connectable_adv(u8 evt_type) +{ + if (evt_type == ADV_IND || evt_type == ADV_DIRECT_IND) + return 1; + + return 0; +} + +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)) + return -EINVAL; + + /* Only new entries should be added to adv_entries. So, if + * bdaddr was found, don't add it. */ + if (hci_find_adv_entry(hdev, &ev->bdaddr)) + return 0; + + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return -ENOMEM; + + bacpy(&entry->bdaddr, &ev->bdaddr); + entry->bdaddr_type = ev->bdaddr_type; + + list_add(&entry->list, &hdev->adv_entries); + + BT_DBG("%s adv entry added: address %s type %u", hdev->name, + batostr(&entry->bdaddr), entry->bdaddr_type); + + return 0; +} + /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -1268,6 +1329,8 @@ int hci_register_dev(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->remote_oob_data); + INIT_LIST_HEAD(&hdev->adv_entries); + INIT_WORK(&hdev->power_on, hci_power_on); INIT_WORK(&hdev->power_off, hci_power_off); setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev); @@ -1348,6 +1411,7 @@ int hci_unregister_dev(struct hci_dev *hdev) hci_uuids_clear(hdev); hci_link_keys_clear(hdev); hci_remote_oob_data_clear(hdev); + hci_adv_entries_clear(hdev); hci_dev_unlock_bh(hdev); __hci_dev_put(hdev); -- cgit v1.2.3 From eb9d91f5ae9c14583c49223e49f7e88f77b84749 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 26 May 2011 16:23:52 -0300 Subject: Bluetooth: Clear advertising cache before scanning The LE advertising cache should be cleared before performing a LE scanning. This will force the cache to contain only fresh advertising entries. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 6 ++++++ net/bluetooth/hci_event.c | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 9317798fabcd..bd285c6a5509 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -710,6 +710,12 @@ struct hci_rp_le_read_buffer_size { __u8 le_max_pkt; } __packed; +#define HCI_OP_LE_SET_SCAN_ENABLE 0x200c +struct hci_cp_le_set_scan_enable { + __u8 enable; + __u8 filter_dup; +} __packed; + #define HCI_OP_LE_CREATE_CONN 0x200d struct hci_cp_le_create_conn { __le16 scan_interval; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index db74958d3ad9..056f7b2fa02f 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -839,6 +839,29 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, rp->randomizer, rp->status); } +static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_cp_le_set_scan_enable *cp; + __u8 status = *((__u8 *) skb->data); + + 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; + + hci_dev_lock(hdev); + + if (cp->enable == 0x01) + hci_adv_entries_clear(hdev); + + hci_dev_unlock(hdev); +} + static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%x", hdev->name, status); @@ -1814,6 +1837,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_user_confirm_neg_reply(hdev, skb); break; + case HCI_OP_LE_SET_SCAN_ENABLE: + hci_cc_le_set_scan_enable(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; -- cgit v1.2.3 From 3581508571b513ed2e66d71f9708d6be907460fd Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 26 May 2011 16:23:53 -0300 Subject: Bluetooth: Advertising entries lifetime This patch adds a timer to clear 'adv_entries' after three minutes. After some amount of time, the advertising entries cached during the last LE scan should be considered expired and they should be removed from the advertising cache. It was chosen a three minutes timeout as an initial attempt. This value might change in future. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_core.c | 14 ++++++++++++++ net/bluetooth/hci_event.c | 6 +++++- 3 files changed, 21 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 10dfb85ad6a1..af4b0ed173a8 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -188,6 +188,7 @@ struct hci_dev { struct list_head remote_oob_data; struct list_head adv_entries; + struct timer_list adv_timer; struct hci_dev_stats stat; @@ -535,6 +536,7 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, u8 *randomizer); int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); +#define ADV_CLEAR_TIMEOUT (3*60*HZ) /* Three minutes */ int hci_adv_entries_clear(struct hci_dev *hdev); struct adv_entry *hci_find_adv_entry(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_add_adv_entry(struct hci_dev *hdev, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index cc40f221f5e7..ff6b784c58c5 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1202,6 +1202,17 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, return 0; } +static void hci_clear_adv_cache(unsigned long arg) +{ + struct hci_dev *hdev = (void *) arg; + + hci_dev_lock(hdev); + + hci_adv_entries_clear(hdev); + + hci_dev_unlock(hdev); +} + int hci_adv_entries_clear(struct hci_dev *hdev) { struct adv_entry *entry, *tmp; @@ -1330,6 +1341,8 @@ int hci_register_dev(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->remote_oob_data); INIT_LIST_HEAD(&hdev->adv_entries); + setup_timer(&hdev->adv_timer, hci_clear_adv_cache, + (unsigned long) hdev); INIT_WORK(&hdev->power_on, hci_power_on); INIT_WORK(&hdev->power_off, hci_power_off); @@ -1403,6 +1416,7 @@ int hci_unregister_dev(struct hci_dev *hdev) hci_unregister_sysfs(hdev); hci_del_off_timer(hdev); + del_timer(&hdev->adv_timer); destroy_workqueue(hdev->workqueue); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 056f7b2fa02f..a90200cac11d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -856,8 +856,12 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, hci_dev_lock(hdev); - if (cp->enable == 0x01) + if (cp->enable == 0x01) { + del_timer(&hdev->adv_timer); hci_adv_entries_clear(hdev); + } else if (cp->enable == 0x00) { + mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT); + } hci_dev_unlock(hdev); } -- cgit v1.2.3 From 19f8def031bfa50c579149b200bfeeb919727b27 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Tue, 31 May 2011 15:49:25 +0200 Subject: Bluetooth: Fix auth_complete_evt for legacy units Legacy devices don't re-authenticate the link properly if a link key already exists. Thus, don't update sec_level for this case even if hci_auth_complete_evt indicates success. Otherwise the sec_level will not reflect a real security on the link. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 2 ++ net/bluetooth/hci_event.c | 12 ++++++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index af4b0ed173a8..0ac820dc35f7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -322,6 +322,7 @@ void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data); /* ----- HCI Connections ----- */ enum { HCI_CONN_AUTH_PEND, + HCI_CONN_REAUTH_PEND, HCI_CONN_ENCRYPT_PEND, HCI_CONN_RSWITCH_PEND, HCI_CONN_MODE_CHANGE_PEND, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3163330cd4f1..e67540216cd4 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -548,6 +548,8 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) 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); } return 0; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a90200cac11d..33120b48cbc5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1489,13 +1489,21 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status) { - conn->link_mode |= HCI_LM_AUTH; - conn->sec_level = conn->pending_sec_level; + if (!(conn->ssp_mode > 0 && hdev->ssp_mode > 0) && + test_bit(HCI_CONN_REAUTH_PEND, + &conn->pend)) { + BT_INFO("re-auth of legacy device is not" + "possible."); + } else { + conn->link_mode |= HCI_LM_AUTH; + conn->sec_level = conn->pending_sec_level; + } } else { mgmt_auth_failed(hdev->id, &conn->dst, ev->status); } clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); + clear_bit(HCI_CONN_REAUTH_PEND, &conn->pend); if (conn->state == BT_CONFIG) { if (!ev->status && hdev->ssp_mode > 0 && -- cgit v1.2.3 From 29b7988a23daf79c15d587ef9e98e64715aa1ea8 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 31 May 2011 14:20:54 -0300 Subject: Bluetooth: Add 'dst_type' field to struct hci_conn This patch adds a new field (dst_type) to the struct hci_conn which holds the type of the destination address (bdaddr_t dst). This approach is needed in order to use the struct hci_conn as an abstraction of LE connections in HCI Layer. For non-LE this field is ignored. This patch also set properly the 'dst_type' field after initializing LE hci_conn structures. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0ac820dc35f7..5be150229574 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -224,6 +224,7 @@ struct hci_conn { spinlock_t lock; bdaddr_t dst; + __u8 dst_type; __u16 handle; __u16 state; __u8 mode; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 6fc766e107aa..afee4ac1008b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1234,10 +1234,12 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) } else { if (!conn) { conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr); - if (conn) + if (conn) { + conn->dst_type = cp->peer_addr_type; conn->out = 1; - else + } else { BT_ERR("No memory for new connection"); + } } } @@ -2694,6 +2696,8 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff hci_dev_unlock(hdev); return; } + + conn->dst_type = ev->bdaddr_type; } if (ev->status) { -- cgit v1.2.3 From 14b12d0b98f87162b7e9e93dde66d1af97886567 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 23 May 2011 18:06:04 -0700 Subject: Bluetooth: Add BT_POWER L2CAP socket option. Add BT_POWER socket option used to control the power characteristics of the underlying ACL link. When the remote end has put the link in sniff mode and the host stack wants to send data we need need to explicitly exit sniff mode to work well with certain devices (For example, A2DP on Plantronics Voyager 855). However, this causes problems with HID devices. Hence, moving into active mode when sending data, irrespective of who set the sniff mode has been made as a socket option. By default, we will move into active mode. HID devices can set the L2CAP socket option to prevent this from happening. Currently, this has been implemented for L2CAP sockets. This has been tested with incoming and outgoing L2CAP sockets for HID and A2DP. Based on discussions on linux-bluetooth and patches submitted by Andrei Emeltchenko. Signed-off-by: Jaikumar Ganesh Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/bluetooth.h | 8 ++++++++ include/net/bluetooth/hci_core.h | 2 +- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/hci_conn.c | 9 ++++++--- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/l2cap_core.c | 5 +++++ net/bluetooth/l2cap_sock.c | 36 ++++++++++++++++++++++++++++++++++++ 7 files changed, 59 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 43750439c521..af930a3a66be 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -69,6 +69,13 @@ struct bt_security { #define BT_FLUSHABLE_OFF 0 #define BT_FLUSHABLE_ON 1 +#define BT_POWER 9 +struct bt_power { + __u8 force_active; +}; +#define BT_POWER_FORCE_ACTIVE_OFF 0 +#define BT_POWER_FORCE_ACTIVE_ON 1 + #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg) #define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg) #define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg) @@ -150,6 +157,7 @@ struct bt_skb_cb { __u8 retries; __u8 sar; unsigned short channel; + __u8 force_active; }; #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb)) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5be150229574..818eadbc3f7f 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -438,7 +438,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type); int hci_conn_change_link_key(struct hci_conn *conn); int hci_conn_switch_role(struct hci_conn *conn, __u8 role); -void hci_conn_enter_active_mode(struct hci_conn *conn); +void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active); void hci_conn_enter_sniff_mode(struct hci_conn *conn); void hci_conn_hold_device(struct hci_conn *conn); diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index a2dcbfff1c51..0529d278e068 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -303,6 +303,7 @@ struct l2cap_chan { __u8 role_switch; __u8 force_reliable; __u8 flushable; + __u8 force_active; __u8 ident; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ce67d0ff486f..0408a93570d6 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -507,7 +507,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; - hci_conn_enter_active_mode(acl); + hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON); if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->pend)) { /* defer SCO setup until mode change completed */ @@ -688,7 +688,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role) EXPORT_SYMBOL(hci_conn_switch_role); /* Enter active mode */ -void hci_conn_enter_active_mode(struct hci_conn *conn) +void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) { struct hci_dev *hdev = conn->hdev; @@ -697,7 +697,10 @@ void hci_conn_enter_active_mode(struct hci_conn *conn) if (test_bit(HCI_RAW, &hdev->flags)) return; - if (conn->mode != HCI_CM_SNIFF || !conn->power_save) + if (conn->mode != HCI_CM_SNIFF) + goto timer; + + if (!conn->power_save && !force_active) goto timer; if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ff6b784c58c5..e14e8a1cb04e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1969,7 +1969,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev) while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); - hci_conn_enter_active_mode(conn); + hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); hci_send_frame(skb); hdev->acl_last_tx = jiffies; @@ -2108,7 +2108,7 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) if (conn) { register struct hci_proto *hp; - hci_conn_enter_active_mode(conn); + hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); /* Send to upper protocol */ hp = hci_proto[HCI_PROTO_L2CAP]; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bb6be6377891..6908a835a7a3 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -537,6 +537,8 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, else flags = ACL_START; + bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON; + hci_send_acl(conn->hcon, skb, flags); } @@ -590,6 +592,8 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) else flags = ACL_START; + bt_cb(skb)->force_active = chan->force_active; + hci_send_acl(chan->conn->hcon, skb, flags); } @@ -1215,6 +1219,7 @@ void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) else flags = ACL_START; + bt_cb(skb)->force_active = chan->force_active; hci_send_acl(hcon, skb, flags); } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index b79fb7561836..bec3e043b254 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -390,6 +390,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct bt_security sec; + struct bt_power pwr; int len, err = 0; BT_DBG("sk %p", sk); @@ -438,6 +439,21 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; + case BT_POWER: + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM + && sk->sk_type != SOCK_RAW) { + err = -EINVAL; + break; + } + + pwr.force_active = chan->force_active; + + len = min_t(unsigned int, len, sizeof(pwr)); + if (copy_to_user(optval, (char *) &pwr, len)) + err = -EFAULT; + + break; + default: err = -ENOPROTOOPT; break; @@ -538,6 +554,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct bt_security sec; + struct bt_power pwr; int len, err = 0; u32 opt; @@ -614,6 +631,23 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch chan->flushable = opt; break; + case BT_POWER: + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && + chan->chan_type != L2CAP_CHAN_RAW) { + err = -EINVAL; + break; + } + + pwr.force_active = BT_POWER_FORCE_ACTIVE_ON; + + len = min_t(unsigned int, sizeof(pwr), optlen); + if (copy_from_user((char *) &pwr, optval, len)) { + err = -EFAULT; + break; + } + chan->force_active = pwr.force_active; + break; + default: err = -ENOPROTOOPT; break; @@ -771,6 +805,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->role_switch = pchan->role_switch; chan->force_reliable = pchan->force_reliable; chan->flushable = pchan->flushable; + chan->force_active = pchan->force_active; } else { switch (sk->sk_type) { @@ -801,6 +836,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->role_switch = 0; chan->force_reliable = 0; chan->flushable = BT_FLUSHABLE_OFF; + chan->force_active = BT_POWER_FORCE_ACTIVE_ON; } /* Default config options */ -- cgit v1.2.3 From 13e12d14e2dccc7995b8f15a5678a338ab4e6a8c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 8 Jun 2011 15:18:19 -0700 Subject: vfs: reorganize 'struct inode' layout a bit This tries to make the 'struct inode' accesses denser in the data cache by moving a commonly accessed field (i_security) closer to other fields that are accessed often. It also makes 'i_state' just an 'unsigned int' rather than 'unsigned long', since we only use a few bits of that field, and moves it next to the existing 'i_flags' so that we potentially get better structure layout (although depending on config options, i_flags may already have packed in the same word as i_lock, so this improves packing only for the case of spinlock debugging) Out 'struct inode' is still way too big, and we should probably move some other fields around too (the acl fields in particular) for better data cache access density. Other fields (like the inode hash) are likely to be entirely irrelevant under most loads. Signed-off-by: Linus Torvalds --- include/linux/fs.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index 646a1836152a..1c777878f1ea 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -744,9 +744,13 @@ struct inode { spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ unsigned int i_flags; + unsigned int i_state; +#ifdef CONFIG_SECURITY + void *i_security; +#endif struct mutex i_mutex; - unsigned long i_state; + unsigned long dirtied_when; /* jiffies of first dirtying */ struct hlist_node i_hash; @@ -798,9 +802,6 @@ struct inode { atomic_t i_readcount; /* struct files open RO */ #endif atomic_t i_writecount; -#ifdef CONFIG_SECURITY - void *i_security; -#endif #ifdef CONFIG_FS_POSIX_ACL struct posix_acl *i_acl; struct posix_acl *i_default_acl; -- cgit v1.2.3 From bff55273f98dea0ceb78e28eb69462fe5f72ef3d Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 8 Jun 2011 12:35:08 +0000 Subject: v2 ethtool: remove support for ETHTOOL_GRXNTUPLE This change is meant to remove all support for displaying an ntuple as strings via ETHTOOL_GRXNTUPLE. The reason for this change is due to the fact that multiple issues have been found including: - Multiple buffer overruns for strings being displayed. - Incorrect filters displayed, cleared filters with ring of -2 are displayed - Setting get_rx_ntuple displays no rules if defined. - Endianess wrong on displayed values. - Hard limit of 1024 filters makes display functionality extremely limited The only driver that had supported this interface was ixgbe. Since it no longer uses the interface and due to the issues mentioned above I am submitting this patch to remove it. v2: Updated based on comments from Ben Hutchings - Left ETH_SS_NTUPLE_FILTERS in code but commented on it being deprecated - Removed ethtool_rx_ntuple_list and ethtool_rx_ntuple_flow_spec_container - Left ETHTOOL_GRXNTUPLE but commented it as deprecated Also cleaned up set_rx_ntuple since there is no flow spec container to maintain we can drop all the code for the alloc and free of it and just return ops->set_rx_ntuple(). Signed-off-by: Alexander Duyck Acked-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/ethtool.h | 19 +-- include/linux/netdevice.h | 3 - net/core/dev.c | 5 - net/core/ethtool.c | 309 +--------------------------------------------- 4 files changed, 3 insertions(+), 333 deletions(-) (limited to 'include') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index c6a850ab2ec5..dfd34934213d 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -287,7 +287,7 @@ enum ethtool_stringset { ETH_SS_TEST = 0, ETH_SS_STATS, ETH_SS_PRIV_FLAGS, - ETH_SS_NTUPLE_FILTERS, + ETH_SS_NTUPLE_FILTERS, /* Do not use, GRXNTUPLE is now deprecated */ ETH_SS_FEATURES, }; @@ -714,18 +714,6 @@ enum ethtool_sfeatures_retval_bits { /* needed by dev_disable_lro() */ extern int __ethtool_set_flags(struct net_device *dev, u32 flags); -struct ethtool_rx_ntuple_flow_spec_container { - struct ethtool_rx_ntuple_flow_spec fs; - struct list_head list; -}; - -struct ethtool_rx_ntuple_list { -#define ETHTOOL_MAX_NTUPLE_LIST_ENTRY 1024 -#define ETHTOOL_MAX_NTUPLE_STRING_PER_ENTRY 14 - struct list_head list; - unsigned int count; -}; - /** * enum ethtool_phys_id_state - indicator state for physical identification * @ETHTOOL_ID_INACTIVE: Physical ID indicator should be deactivated @@ -758,7 +746,6 @@ u32 ethtool_op_get_ufo(struct net_device *dev); int ethtool_op_set_ufo(struct net_device *dev, u32 data); u32 ethtool_op_get_flags(struct net_device *dev); int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported); -void ethtool_ntuple_flush(struct net_device *dev); bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported); /** @@ -865,7 +852,6 @@ bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported); * error code or zero. * @set_rx_ntuple: Set an RX n-tuple rule. Returns a negative error code * or zero. - * @get_rx_ntuple: Deprecated. * @get_rxfh_indir: Get the contents of the RX flow hash indirection table. * Returns a negative error code or zero. * @set_rxfh_indir: Set the contents of the RX flow hash indirection table. @@ -944,7 +930,6 @@ struct ethtool_ops { int (*reset)(struct net_device *, u32 *); int (*set_rx_ntuple)(struct net_device *, struct ethtool_rx_ntuple *); - int (*get_rx_ntuple)(struct net_device *, u32 stringset, void *); int (*get_rxfh_indir)(struct net_device *, struct ethtool_rxfh_indir *); int (*set_rxfh_indir)(struct net_device *, @@ -1017,7 +1002,7 @@ struct ethtool_ops { #define ETHTOOL_FLASHDEV 0x00000033 /* Flash firmware to device */ #define ETHTOOL_RESET 0x00000034 /* Reset hardware */ #define ETHTOOL_SRXNTUPLE 0x00000035 /* Add an n-tuple filter to device */ -#define ETHTOOL_GRXNTUPLE 0x00000036 /* Get n-tuple filters from device */ +#define ETHTOOL_GRXNTUPLE 0x00000036 /* deprecated */ #define ETHTOOL_GSSET_INFO 0x00000037 /* Get string set info */ #define ETHTOOL_GRXFHINDIR 0x00000038 /* Get RX flow hash indir'n table */ #define ETHTOOL_SRXFHINDIR 0x00000039 /* Set RX flow hash indir'n table */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 336288567c78..6469fa942d1d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1348,9 +1348,6 @@ struct net_device { /* max exchange id for FCoE LRO by ddp */ unsigned int fcoe_ddp_xid; #endif - /* n-tuple filter list attached to this device */ - struct ethtool_rx_ntuple_list ethtool_ntuple_list; - /* phy device may attach itself for hardware timestamping */ struct phy_device *phydev; diff --git a/net/core/dev.c b/net/core/dev.c index 939307891e71..b3f52d2f56d7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5867,8 +5867,6 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, dev->gso_max_size = GSO_MAX_SIZE; - INIT_LIST_HEAD(&dev->ethtool_ntuple_list.list); - dev->ethtool_ntuple_list.count = 0; INIT_LIST_HEAD(&dev->napi_list); INIT_LIST_HEAD(&dev->unreg_list); INIT_LIST_HEAD(&dev->link_watch_list); @@ -5932,9 +5930,6 @@ void free_netdev(struct net_device *dev) /* Flush device addresses */ dev_addr_flush(dev); - /* Clear ethtool n-tuple list */ - ethtool_ntuple_flush(dev); - list_for_each_entry_safe(p, n, &dev->napi_list, dev_list) netif_napi_del(p); diff --git a/net/core/ethtool.c b/net/core/ethtool.c index fd14116ad7f0..b7c12a63d0ce 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -169,18 +169,6 @@ int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported) } EXPORT_SYMBOL(ethtool_op_set_flags); -void ethtool_ntuple_flush(struct net_device *dev) -{ - struct ethtool_rx_ntuple_flow_spec_container *fsc, *f; - - list_for_each_entry_safe(fsc, f, &dev->ethtool_ntuple_list.list, list) { - list_del(&fsc->list); - kfree(fsc); - } - dev->ethtool_ntuple_list.count = 0; -} -EXPORT_SYMBOL(ethtool_ntuple_flush); - /* Handlers for each ethtool command */ #define ETHTOOL_DEV_FEATURE_WORDS 1 @@ -865,34 +853,6 @@ out: return ret; } -static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list, - struct ethtool_rx_ntuple_flow_spec *spec, - struct ethtool_rx_ntuple_flow_spec_container *fsc) -{ - - /* don't add filters forever */ - if (list->count >= ETHTOOL_MAX_NTUPLE_LIST_ENTRY) { - /* free the container */ - kfree(fsc); - return; - } - - /* Copy the whole filter over */ - fsc->fs.flow_type = spec->flow_type; - memcpy(&fsc->fs.h_u, &spec->h_u, sizeof(spec->h_u)); - memcpy(&fsc->fs.m_u, &spec->m_u, sizeof(spec->m_u)); - - fsc->fs.vlan_tag = spec->vlan_tag; - fsc->fs.vlan_tag_mask = spec->vlan_tag_mask; - fsc->fs.data = spec->data; - fsc->fs.data_mask = spec->data_mask; - fsc->fs.action = spec->action; - - /* add to the list */ - list_add_tail_rcu(&fsc->list, &list->list); - list->count++; -} - /* * ethtool does not (or did not) set masks for flow parameters that are * not specified, so if both value and mask are 0 then this must be @@ -930,8 +890,6 @@ static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev, { struct ethtool_rx_ntuple cmd; const struct ethtool_ops *ops = dev->ethtool_ops; - struct ethtool_rx_ntuple_flow_spec_container *fsc = NULL; - int ret; if (!ops->set_rx_ntuple) return -EOPNOTSUPP; @@ -944,269 +902,7 @@ static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev, rx_ntuple_fix_masks(&cmd.fs); - /* - * Cache filter in dev struct for GET operation only if - * the underlying driver doesn't have its own GET operation, and - * only if the filter was added successfully. First make sure we - * can allocate the filter, then continue if successful. - */ - if (!ops->get_rx_ntuple) { - fsc = kmalloc(sizeof(*fsc), GFP_ATOMIC); - if (!fsc) - return -ENOMEM; - } - - ret = ops->set_rx_ntuple(dev, &cmd); - if (ret) { - kfree(fsc); - return ret; - } - - if (!ops->get_rx_ntuple) - __rx_ntuple_filter_add(&dev->ethtool_ntuple_list, &cmd.fs, fsc); - - return ret; -} - -static int ethtool_get_rx_ntuple(struct net_device *dev, void __user *useraddr) -{ - struct ethtool_gstrings gstrings; - const struct ethtool_ops *ops = dev->ethtool_ops; - struct ethtool_rx_ntuple_flow_spec_container *fsc; - u8 *data; - char *p; - int ret, i, num_strings = 0; - - if (!ops->get_sset_count) - return -EOPNOTSUPP; - - if (copy_from_user(&gstrings, useraddr, sizeof(gstrings))) - return -EFAULT; - - ret = ops->get_sset_count(dev, gstrings.string_set); - if (ret < 0) - return ret; - - gstrings.len = ret; - - data = kzalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER); - if (!data) - return -ENOMEM; - - if (ops->get_rx_ntuple) { - /* driver-specific filter grab */ - ret = ops->get_rx_ntuple(dev, gstrings.string_set, data); - goto copy; - } - - /* default ethtool filter grab */ - i = 0; - p = (char *)data; - list_for_each_entry(fsc, &dev->ethtool_ntuple_list.list, list) { - sprintf(p, "Filter %d:\n", i); - p += ETH_GSTRING_LEN; - num_strings++; - - switch (fsc->fs.flow_type) { - case TCP_V4_FLOW: - sprintf(p, "\tFlow Type: TCP\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case UDP_V4_FLOW: - sprintf(p, "\tFlow Type: UDP\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case SCTP_V4_FLOW: - sprintf(p, "\tFlow Type: SCTP\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case AH_ESP_V4_FLOW: - sprintf(p, "\tFlow Type: AH ESP\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case ESP_V4_FLOW: - sprintf(p, "\tFlow Type: ESP\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case IP_USER_FLOW: - sprintf(p, "\tFlow Type: Raw IP\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case IPV4_FLOW: - sprintf(p, "\tFlow Type: IPv4\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - default: - sprintf(p, "\tFlow Type: Unknown\n"); - p += ETH_GSTRING_LEN; - num_strings++; - goto unknown_filter; - } - - /* now the rest of the filters */ - switch (fsc->fs.flow_type) { - case TCP_V4_FLOW: - case UDP_V4_FLOW: - case SCTP_V4_FLOW: - sprintf(p, "\tSrc IP addr: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tSrc IP mask: 0x%x\n", - fsc->fs.m_u.tcp_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP addr: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP mask: 0x%x\n", - fsc->fs.m_u.tcp_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tSrc Port: %d, mask: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.psrc, - fsc->fs.m_u.tcp_ip4_spec.psrc); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest Port: %d, mask: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.pdst, - fsc->fs.m_u.tcp_ip4_spec.pdst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tTOS: %d, mask: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.tos, - fsc->fs.m_u.tcp_ip4_spec.tos); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case AH_ESP_V4_FLOW: - case ESP_V4_FLOW: - sprintf(p, "\tSrc IP addr: 0x%x\n", - fsc->fs.h_u.ah_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tSrc IP mask: 0x%x\n", - fsc->fs.m_u.ah_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP addr: 0x%x\n", - fsc->fs.h_u.ah_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP mask: 0x%x\n", - fsc->fs.m_u.ah_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tSPI: %d, mask: 0x%x\n", - fsc->fs.h_u.ah_ip4_spec.spi, - fsc->fs.m_u.ah_ip4_spec.spi); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tTOS: %d, mask: 0x%x\n", - fsc->fs.h_u.ah_ip4_spec.tos, - fsc->fs.m_u.ah_ip4_spec.tos); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case IP_USER_FLOW: - sprintf(p, "\tSrc IP addr: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tSrc IP mask: 0x%x\n", - fsc->fs.m_u.usr_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP addr: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP mask: 0x%x\n", - fsc->fs.m_u.usr_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case IPV4_FLOW: - sprintf(p, "\tSrc IP addr: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tSrc IP mask: 0x%x\n", - fsc->fs.m_u.usr_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP addr: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP mask: 0x%x\n", - fsc->fs.m_u.usr_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tL4 bytes: 0x%x, mask: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.l4_4_bytes, - fsc->fs.m_u.usr_ip4_spec.l4_4_bytes); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tTOS: %d, mask: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.tos, - fsc->fs.m_u.usr_ip4_spec.tos); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tIP Version: %d, mask: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip_ver, - fsc->fs.m_u.usr_ip4_spec.ip_ver); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tProtocol: %d, mask: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.proto, - fsc->fs.m_u.usr_ip4_spec.proto); - p += ETH_GSTRING_LEN; - num_strings++; - break; - } - sprintf(p, "\tVLAN: %d, mask: 0x%x\n", - fsc->fs.vlan_tag, fsc->fs.vlan_tag_mask); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tUser-defined: 0x%Lx\n", fsc->fs.data); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tUser-defined mask: 0x%Lx\n", fsc->fs.data_mask); - p += ETH_GSTRING_LEN; - num_strings++; - if (fsc->fs.action == ETHTOOL_RXNTUPLE_ACTION_DROP) - sprintf(p, "\tAction: Drop\n"); - else - sprintf(p, "\tAction: Direct to queue %d\n", - fsc->fs.action); - p += ETH_GSTRING_LEN; - num_strings++; -unknown_filter: - i++; - } -copy: - /* indicate to userspace how many strings we actually have */ - gstrings.len = num_strings; - ret = -EFAULT; - if (copy_to_user(useraddr, &gstrings, sizeof(gstrings))) - goto out; - useraddr += sizeof(gstrings); - if (copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN)) - goto out; - ret = 0; - -out: - kfree(data); - return ret; + return ops->set_rx_ntuple(dev, &cmd); } static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) @@ -2101,9 +1797,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SRXNTUPLE: rc = ethtool_set_rx_ntuple(dev, useraddr); break; - case ETHTOOL_GRXNTUPLE: - rc = ethtool_get_rx_ntuple(dev, useraddr); - break; case ETHTOOL_GSSET_INFO: rc = ethtool_get_sset_info(dev, useraddr); break; -- cgit v1.2.3 From 9ad7c049f0f79c418e293b1b68cf10d68f54fcdb Mon Sep 17 00:00:00 2001 From: Jerry Chu Date: Wed, 8 Jun 2011 11:08:38 +0000 Subject: tcp: RFC2988bis + taking RTT sample from 3WHS for the passive open side This patch lowers the default initRTO from 3secs to 1sec per RFC2988bis. It falls back to 3secs if the SYN or SYN-ACK packet has been retransmitted, AND the TCP timestamp option is not on. It also adds support to take RTT sample during 3WHS on the passive open side, just like its active open counterpart, and uses it, if valid, to seed the initRTO for the data transmission phase. The patch also resets ssthresh to its initial default at the beginning of the data transmission phase, and reduces cwnd to 1 if there has been MORE THAN ONE retransmission during 3WHS per RFC5681. Signed-off-by: H.K. Jerry Chu Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 + include/net/tcp.h | 11 +++++++++-- net/ipv4/syncookies.c | 1 + net/ipv4/tcp_input.c | 46 +++++++++++++++++++++++++--------------------- net/ipv4/tcp_ipv4.c | 11 ++++++++--- net/ipv4/tcp_minisocks.c | 6 +++++- net/ipv6/syncookies.c | 1 + net/ipv6/tcp_ipv6.c | 5 +++++ 8 files changed, 55 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index e64f4c67d0ef..531ede8006d9 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -282,6 +282,7 @@ struct tcp_request_sock { #endif u32 rcv_isn; u32 snt_isn; + u32 snt_synack; /* synack sent time */ }; static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req) diff --git a/include/net/tcp.h b/include/net/tcp.h index cda30ea354a2..149a415d1e0a 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -122,7 +122,13 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); #endif #define TCP_RTO_MAX ((unsigned)(120*HZ)) #define TCP_RTO_MIN ((unsigned)(HZ/5)) -#define TCP_TIMEOUT_INIT ((unsigned)(3*HZ)) /* RFC 1122 initial RTO value */ +#define TCP_TIMEOUT_INIT ((unsigned)(1*HZ)) /* RFC2988bis initial RTO value */ +#define TCP_TIMEOUT_FALLBACK ((unsigned)(3*HZ)) /* RFC 1122 initial RTO value, now + * used as a fallback RTO for the + * initial data transmission if no + * valid RTT sample has been acquired, + * most likely due to retrans in 3WHS. + */ #define TCP_RESOURCE_PROBE_INTERVAL ((unsigned)(HZ/2U)) /* Maximal interval between probes * for local resources. @@ -295,7 +301,7 @@ static inline void tcp_synq_overflow(struct sock *sk) static inline int tcp_synq_no_recent_overflow(const struct sock *sk) { unsigned long last_overflow = tcp_sk(sk)->rx_opt.ts_recent_stamp; - return time_after(jiffies, last_overflow + TCP_TIMEOUT_INIT); + return time_after(jiffies, last_overflow + TCP_TIMEOUT_FALLBACK); } extern struct proto tcp_prot; @@ -508,6 +514,7 @@ extern void tcp_initialize_rcv_mss(struct sock *sk); extern int tcp_mtu_to_mss(struct sock *sk, int pmtu); extern int tcp_mss_to_mtu(struct sock *sk, int mss); extern void tcp_mtup_init(struct sock *sk); +extern void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt); static inline void tcp_bound_rto(const struct sock *sk) { diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 26461492a847..92bb9434b338 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -316,6 +316,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, ireq->wscale_ok = tcp_opt.wscale_ok; ireq->tstamp_ok = tcp_opt.saw_tstamp; req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0; + treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0; /* We throwed the options of the initial SYN away, so we hope * the ACK carries the same options again (see RFC1122 4.2.3.8) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index bef9f04c22ba..ea0d2183df4b 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -880,6 +880,11 @@ static void tcp_init_metrics(struct sock *sk) tp->snd_ssthresh = dst_metric(dst, RTAX_SSTHRESH); if (tp->snd_ssthresh > tp->snd_cwnd_clamp) tp->snd_ssthresh = tp->snd_cwnd_clamp; + } else { + /* ssthresh may have been reduced unnecessarily during. + * 3WHS. Restore it back to its initial default. + */ + tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; } if (dst_metric(dst, RTAX_REORDERING) && tp->reordering != dst_metric(dst, RTAX_REORDERING)) { @@ -887,10 +892,7 @@ static void tcp_init_metrics(struct sock *sk) tp->reordering = dst_metric(dst, RTAX_REORDERING); } - if (dst_metric(dst, RTAX_RTT) == 0) - goto reset; - - if (!tp->srtt && dst_metric_rtt(dst, RTAX_RTT) < (TCP_TIMEOUT_INIT << 3)) + if (dst_metric(dst, RTAX_RTT) == 0 || tp->srtt == 0) goto reset; /* Initial rtt is determined from SYN,SYN-ACK. @@ -916,19 +918,26 @@ static void tcp_init_metrics(struct sock *sk) tp->mdev_max = tp->rttvar = max(tp->mdev, tcp_rto_min(sk)); } tcp_set_rto(sk); - if (inet_csk(sk)->icsk_rto < TCP_TIMEOUT_INIT && !tp->rx_opt.saw_tstamp) { reset: - /* Play conservative. If timestamps are not - * supported, TCP will fail to recalculate correct - * rtt, if initial rto is too small. FORGET ALL AND RESET! + if (tp->srtt == 0) { + /* RFC2988bis: We've failed to get a valid RTT sample from + * 3WHS. This is most likely due to retransmission, + * including spurious one. Reset the RTO back to 3secs + * from the more aggressive 1sec to avoid more spurious + * retransmission. */ - if (!tp->rx_opt.saw_tstamp && tp->srtt) { - tp->srtt = 0; - tp->mdev = tp->mdev_max = tp->rttvar = TCP_TIMEOUT_INIT; - inet_csk(sk)->icsk_rto = TCP_TIMEOUT_INIT; - } + tp->mdev = tp->mdev_max = tp->rttvar = TCP_TIMEOUT_FALLBACK; + inet_csk(sk)->icsk_rto = TCP_TIMEOUT_FALLBACK; } - tp->snd_cwnd = tcp_init_cwnd(tp, dst); + /* Cut cwnd down to 1 per RFC5681 if SYN or SYN-ACK has been + * retransmitted. In light of RFC2988bis' more aggressive 1sec + * initRTO, we only reset cwnd when more than 1 SYN/SYN-ACK + * retransmission has occurred. + */ + if (tp->total_retrans > 1) + tp->snd_cwnd = 1; + else + tp->snd_cwnd = tcp_init_cwnd(tp, dst); tp->snd_cwnd_stamp = tcp_time_stamp; } @@ -3112,12 +3121,13 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, int flag) tcp_xmit_retransmit_queue(sk); } -static void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt) +void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt) { tcp_rtt_estimator(sk, seq_rtt); tcp_set_rto(sk); inet_csk(sk)->icsk_backoff = 0; } +EXPORT_SYMBOL(tcp_valid_rtt_meas); /* Read draft-ietf-tcplw-high-performance before mucking * with this code. (Supersedes RFC1323) @@ -5806,12 +5816,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tp->rx_opt.snd_wscale; tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); - /* tcp_ack considers this ACK as duplicate - * and does not calculate rtt. - * Force it here. - */ - tcp_ack_update_rtt(sk, 0, 0); - if (tp->rx_opt.tstamp_ok) tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a7d6671e33b8..617dee3ccfb1 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -429,8 +429,8 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) break; icsk->icsk_backoff--; - inet_csk(sk)->icsk_rto = __tcp_set_rto(tp) << - icsk->icsk_backoff; + inet_csk(sk)->icsk_rto = (tp->srtt ? __tcp_set_rto(tp) : + TCP_TIMEOUT_INIT) << icsk->icsk_backoff; tcp_bound_rto(sk); skb = tcp_write_queue_head(sk); @@ -1384,6 +1384,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) isn = tcp_v4_init_sequence(skb); } tcp_rsk(req)->snt_isn = isn; + tcp_rsk(req)->snt_synack = tcp_time_stamp; if (tcp_v4_send_synack(sk, dst, req, (struct request_values *)&tmp_ext) || @@ -1458,6 +1459,10 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newtp->advmss = tcp_sk(sk)->rx_opt.user_mss; tcp_initialize_rcv_mss(newsk); + if (tcp_rsk(req)->snt_synack) + tcp_valid_rtt_meas(newsk, + tcp_time_stamp - tcp_rsk(req)->snt_synack); + newtp->total_retrans = req->retrans; #ifdef CONFIG_TCP_MD5SIG /* Copy over the MD5 key from the original socket */ @@ -1854,7 +1859,7 @@ static int tcp_v4_init_sock(struct sock *sk) * algorithms that we must have the following bandaid to talk * efficiently to them. -DaveM */ - tp->snd_cwnd = 2; + tp->snd_cwnd = TCP_INIT_CWND; /* See draft-stevens-tcpca-spec-01 for discussion of the * initialization of these values. diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 80b1f80759ab..d2fe4e06b472 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -486,7 +486,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, * algorithms that we must have the following bandaid to talk * efficiently to them. -DaveM */ - newtp->snd_cwnd = 2; + newtp->snd_cwnd = TCP_INIT_CWND; newtp->snd_cwnd_cnt = 0; newtp->bytes_acked = 0; @@ -720,6 +720,10 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDEFERACCEPTDROP); return NULL; } + if (tmp_opt.saw_tstamp && tmp_opt.rcv_tsecr) + tcp_rsk(req)->snt_synack = tmp_opt.rcv_tsecr; + else if (req->retrans) /* don't take RTT sample if retrans && ~TS */ + tcp_rsk(req)->snt_synack = 0; /* OK, ACK is valid, create big socket and * feed this segment to it. It will repeat all diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 8b9644a8b697..89d5bf806222 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -223,6 +223,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ireq->wscale_ok = tcp_opt.wscale_ok; ireq->tstamp_ok = tcp_opt.saw_tstamp; req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0; + treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0; treq->rcv_isn = ntohl(th->seq) - 1; treq->snt_isn = cookie; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index d1fd28711ba5..a1ef61a889c3 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1341,6 +1341,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) } have_isn: tcp_rsk(req)->snt_isn = isn; + tcp_rsk(req)->snt_synack = tcp_time_stamp; security_inet_conn_request(sk, skb, req); @@ -1509,6 +1510,10 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, tcp_sync_mss(newsk, dst_mtu(dst)); newtp->advmss = dst_metric_advmss(dst); tcp_initialize_rcv_mss(newsk); + if (tcp_rsk(req)->snt_synack) + tcp_valid_rtt_meas(newsk, + tcp_time_stamp - tcp_rsk(req)->snt_synack); + newtp->total_retrans = req->retrans; newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6; newinet->inet_rcv_saddr = LOOPBACK4_IPV6; -- cgit v1.2.3 From 4b9d9be839fdb7dcd7ce7619a623fd9015a50cda Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Jun 2011 13:35:34 +0000 Subject: inetpeer: remove unused list Andi Kleen and Tim Chen reported huge contention on inetpeer unused_peers.lock, on memcached workload on a 40 core machine, with disabled route cache. It appears we constantly flip peers refcnt between 0 and 1 values, and we must insert/remove peers from unused_peers.list, holding a contended spinlock. Remove this list completely and perform a garbage collection on-the-fly, at lookup time, using the expired nodes we met during the tree traversal. This removes a lot of code, makes locking more standard, and obsoletes two sysctls (inet_peer_gc_mintime and inet_peer_gc_maxtime). This also removes two pointers in inet_peer structure. There is still a false sharing effect because refcnt is in first cache line of object [were the links and keys used by lookups are located], we might move it at the end of inet_peer structure to let this first cache line mostly read by cpus. Signed-off-by: Eric Dumazet CC: Andi Kleen CC: Tim Chen Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 10 -- include/net/inetpeer.h | 2 +- include/net/ip.h | 2 - net/ipv4/inetpeer.c | 280 +++++++++------------------------ net/ipv4/sysctl_net_ipv4.c | 14 -- 5 files changed, 74 insertions(+), 234 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index d3d653a5f9b9..3dcb26cd33f6 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -106,16 +106,6 @@ inet_peer_maxttl - INTEGER when the number of entries in the pool is very small). Measured in seconds. -inet_peer_gc_mintime - INTEGER - Minimum interval between garbage collection passes. This interval is - in effect under high memory pressure on the pool. - Measured in seconds. - -inet_peer_gc_maxtime - INTEGER - Minimum interval between garbage collection passes. This interval is - in effect under low (or absent) memory pressure on the pool. - Measured in seconds. - TCP variables: somaxconn - INTEGER diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 8a159cc3d68b..1f0966f91760 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -32,7 +32,6 @@ struct inet_peer { struct inet_peer __rcu *avl_left, *avl_right; struct inetpeer_addr daddr; __u32 avl_height; - struct list_head unused; __u32 dtime; /* the time of last use of not * referenced entries */ atomic_t refcnt; @@ -56,6 +55,7 @@ struct inet_peer { struct inetpeer_addr_base redirect_learned; }; struct rcu_head rcu; + struct inet_peer *gc_next; }; }; diff --git a/include/net/ip.h b/include/net/ip.h index 66dd49149208..e9ea7c73f8c1 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -228,8 +228,6 @@ extern struct ctl_path net_ipv4_ctl_path[]; extern int inet_peer_threshold; extern int inet_peer_minttl; extern int inet_peer_maxttl; -extern int inet_peer_gc_mintime; -extern int inet_peer_gc_maxtime; /* From ip_output.c */ extern int sysctl_ip_dynaddr; diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index ce616d92cc54..dafbf2c98b28 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -54,15 +54,11 @@ * 1. Nodes may appear in the tree only with the pool lock held. * 2. Nodes may disappear from the tree only with the pool lock held * AND reference count being 0. - * 3. Nodes appears and disappears from unused node list only under - * "inet_peer_unused_lock". - * 4. Global variable peer_total is modified under the pool lock. - * 5. struct inet_peer fields modification: + * 3. Global variable peer_total is modified under the pool lock. + * 4. struct inet_peer fields modification: * avl_left, avl_right, avl_parent, avl_height: pool lock - * unused: unused node list lock * refcnt: atomically against modifications on other CPU; * usually under some other lock to prevent node disappearing - * dtime: unused node list lock * daddr: unchangeable * ip_id_count: atomic value (no lock needed) */ @@ -104,19 +100,6 @@ int inet_peer_threshold __read_mostly = 65536 + 128; /* start to throw entries m * aggressively at this stage */ int inet_peer_minttl __read_mostly = 120 * HZ; /* TTL under high load: 120 sec */ int inet_peer_maxttl __read_mostly = 10 * 60 * HZ; /* usual time to live: 10 min */ -int inet_peer_gc_mintime __read_mostly = 10 * HZ; -int inet_peer_gc_maxtime __read_mostly = 120 * HZ; - -static struct { - struct list_head list; - spinlock_t lock; -} unused_peers = { - .list = LIST_HEAD_INIT(unused_peers.list), - .lock = __SPIN_LOCK_UNLOCKED(unused_peers.lock), -}; - -static void peer_check_expire(unsigned long dummy); -static DEFINE_TIMER(peer_periodic_timer, peer_check_expire, 0, 0); /* Called from ip_output.c:ip_init */ @@ -142,21 +125,6 @@ void __init inet_initpeers(void) 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); - /* All the timers, started at system startup tend - to synchronize. Perturb it a bit. - */ - peer_periodic_timer.expires = jiffies - + net_random() % inet_peer_gc_maxtime - + inet_peer_gc_maxtime; - add_timer(&peer_periodic_timer); -} - -/* Called with or without local BH being disabled. */ -static void unlink_from_unused(struct inet_peer *p) -{ - spin_lock_bh(&unused_peers.lock); - list_del_init(&p->unused); - spin_unlock_bh(&unused_peers.lock); } static int addr_compare(const struct inetpeer_addr *a, @@ -203,20 +171,6 @@ static int addr_compare(const struct inetpeer_addr *a, u; \ }) -static bool atomic_add_unless_return(atomic_t *ptr, int a, int u, int *newv) -{ - int cur, old = atomic_read(ptr); - - while (old != u) { - *newv = old + a; - cur = atomic_cmpxchg(ptr, old, *newv); - if (cur == old) - return true; - old = cur; - } - return false; -} - /* * Called with rcu_read_lock() * Because we hold no lock against a writer, its quite possible we fall @@ -225,8 +179,7 @@ static bool atomic_add_unless_return(atomic_t *ptr, int a, int u, int *newv) * We exit from this function if number of links exceeds PEER_MAXDEPTH */ static struct inet_peer *lookup_rcu(const struct inetpeer_addr *daddr, - struct inet_peer_base *base, - int *newrefcnt) + struct inet_peer_base *base) { struct inet_peer *u = rcu_dereference(base->root); int count = 0; @@ -235,11 +188,9 @@ static struct inet_peer *lookup_rcu(const struct inetpeer_addr *daddr, int cmp = addr_compare(daddr, &u->daddr); if (cmp == 0) { /* Before taking a reference, check if this entry was - * deleted, unlink_from_pool() sets refcnt=-1 to make - * distinction between an unused entry (refcnt=0) and - * a freed one. + * deleted (refcnt=-1) */ - if (!atomic_add_unless_return(&u->refcnt, 1, -1, newrefcnt)) + if (!atomic_add_unless(&u->refcnt, 1, -1)) u = NULL; return u; } @@ -366,137 +317,96 @@ static void inetpeer_free_rcu(struct rcu_head *head) kmem_cache_free(peer_cachep, container_of(head, struct inet_peer, rcu)); } -/* May be called with local BH enabled. */ static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base, struct inet_peer __rcu **stack[PEER_MAXDEPTH]) { - int do_free; - - do_free = 0; - - write_seqlock_bh(&base->lock); - /* Check the reference counter. It was artificially incremented by 1 - * in cleanup() function to prevent sudden disappearing. If we can - * atomically (because of lockless readers) take this last reference, - * it's safe to remove the node and free it later. - * We use refcnt=-1 to alert lockless readers this entry is deleted. - */ - if (atomic_cmpxchg(&p->refcnt, 1, -1) == 1) { - struct inet_peer __rcu ***stackptr, ***delp; - if (lookup(&p->daddr, stack, base) != p) - BUG(); - delp = stackptr - 1; /* *delp[0] == p */ - if (p->avl_left == peer_avl_empty_rcu) { - *delp[0] = p->avl_right; - --stackptr; - } else { - /* look for a node to insert instead of p */ - struct inet_peer *t; - t = lookup_rightempty(p, base); - BUG_ON(rcu_deref_locked(*stackptr[-1], base) != t); - **--stackptr = t->avl_left; - /* t is removed, t->daddr > x->daddr for any - * x in p->avl_left subtree. - * Put t in the old place of p. */ - RCU_INIT_POINTER(*delp[0], t); - t->avl_left = p->avl_left; - t->avl_right = p->avl_right; - t->avl_height = p->avl_height; - BUG_ON(delp[1] != &p->avl_left); - delp[1] = &t->avl_left; /* was &p->avl_left */ - } - peer_avl_rebalance(stack, stackptr, base); - base->total--; - do_free = 1; + struct inet_peer __rcu ***stackptr, ***delp; + + if (lookup(&p->daddr, stack, base) != p) + BUG(); + delp = stackptr - 1; /* *delp[0] == p */ + if (p->avl_left == peer_avl_empty_rcu) { + *delp[0] = p->avl_right; + --stackptr; + } else { + /* look for a node to insert instead of p */ + struct inet_peer *t; + t = lookup_rightempty(p, base); + BUG_ON(rcu_deref_locked(*stackptr[-1], base) != t); + **--stackptr = t->avl_left; + /* t is removed, t->daddr > x->daddr for any + * x in p->avl_left subtree. + * Put t in the old place of p. */ + RCU_INIT_POINTER(*delp[0], t); + t->avl_left = p->avl_left; + t->avl_right = p->avl_right; + t->avl_height = p->avl_height; + BUG_ON(delp[1] != &p->avl_left); + delp[1] = &t->avl_left; /* was &p->avl_left */ } - write_sequnlock_bh(&base->lock); - - if (do_free) - call_rcu(&p->rcu, inetpeer_free_rcu); - else - /* The node is used again. Decrease the reference counter - * back. The loop "cleanup -> unlink_from_unused - * -> unlink_from_pool -> putpeer -> link_to_unused - * -> cleanup (for the same node)" - * doesn't really exist because the entry will have a - * recent deletion time and will not be cleaned again soon. - */ - inet_putpeer(p); + peer_avl_rebalance(stack, stackptr, base); + base->total--; + call_rcu(&p->rcu, inetpeer_free_rcu); } static struct inet_peer_base *family_to_base(int family) { - return (family == AF_INET ? &v4_peers : &v6_peers); + return family == AF_INET ? &v4_peers : &v6_peers; } -static struct inet_peer_base *peer_to_base(struct inet_peer *p) +/* perform garbage collect on all items stacked during a lookup */ +static int inet_peer_gc(struct inet_peer_base *base, + struct inet_peer __rcu **stack[PEER_MAXDEPTH], + struct inet_peer __rcu ***stackptr) { - return family_to_base(p->daddr.family); -} - -/* May be called with local BH enabled. */ -static int cleanup_once(unsigned long ttl, struct inet_peer __rcu **stack[PEER_MAXDEPTH]) -{ - struct inet_peer *p = NULL; - - /* Remove the first entry from the list of unused nodes. */ - spin_lock_bh(&unused_peers.lock); - if (!list_empty(&unused_peers.list)) { - __u32 delta; + struct inet_peer *p, *gchead = NULL; + __u32 delta, ttl; + int cnt = 0; - p = list_first_entry(&unused_peers.list, struct inet_peer, unused); + if (base->total >= inet_peer_threshold) + ttl = 0; /* be aggressive */ + else + ttl = inet_peer_maxttl + - (inet_peer_maxttl - inet_peer_minttl) / HZ * + base->total / inet_peer_threshold * HZ; + stackptr--; /* last stack slot is peer_avl_empty */ + while (stackptr > stack) { + stackptr--; + p = rcu_deref_locked(**stackptr, base); delta = (__u32)jiffies - p->dtime; - - if (delta < ttl) { - /* Do not prune fresh entries. */ - spin_unlock_bh(&unused_peers.lock); - return -1; + if (atomic_read(&p->refcnt) == 0 && delta >= ttl && + atomic_cmpxchg(&p->refcnt, 0, -1) == 0) { + p->gc_next = gchead; + gchead = p; } - - list_del_init(&p->unused); - - /* Grab an extra reference to prevent node disappearing - * before unlink_from_pool() call. */ - atomic_inc(&p->refcnt); } - spin_unlock_bh(&unused_peers.lock); - - if (p == NULL) - /* It means that the total number of USED entries has - * grown over inet_peer_threshold. It shouldn't really - * happen because of entry limits in route cache. */ - return -1; - - unlink_from_pool(p, peer_to_base(p), stack); - return 0; + while ((p = gchead) != NULL) { + gchead = p->gc_next; + cnt++; + unlink_from_pool(p, base, stack); + } + return cnt; } -/* Called with or without local BH being disabled. */ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) { struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr; struct inet_peer_base *base = family_to_base(daddr->family); struct inet_peer *p; unsigned int sequence; - int invalidated, newrefcnt = 0; + int invalidated, gccnt = 0; - /* Look up for the address quickly, lockless. + /* Attempt a lockless lookup first. * Because of a concurrent writer, we might not find an existing entry. */ rcu_read_lock(); sequence = read_seqbegin(&base->lock); - p = lookup_rcu(daddr, base, &newrefcnt); + p = lookup_rcu(daddr, base); invalidated = read_seqretry(&base->lock, sequence); rcu_read_unlock(); - if (p) { -found: /* The existing node has been found. - * Remove the entry from unused list if it was there. - */ - if (newrefcnt == 1) - unlink_from_unused(p); + if (p) return p; - } /* If no writer did a change during our lookup, we can return early. */ if (!create && !invalidated) @@ -506,11 +416,17 @@ found: /* The existing node has been found. * At least, nodes should be hot in our cache. */ write_seqlock_bh(&base->lock); +relookup: p = lookup(daddr, stack, base); if (p != peer_avl_empty) { - newrefcnt = atomic_inc_return(&p->refcnt); + atomic_inc(&p->refcnt); write_sequnlock_bh(&base->lock); - goto found; + return p; + } + if (!gccnt) { + gccnt = inet_peer_gc(base, stack, stackptr); + if (gccnt && create) + goto relookup; } p = create ? kmem_cache_alloc(peer_cachep, GFP_ATOMIC) : NULL; if (p) { @@ -525,7 +441,6 @@ found: /* The existing node has been found. p->pmtu_expires = 0; p->pmtu_orig = 0; memset(&p->redirect_learned, 0, sizeof(p->redirect_learned)); - INIT_LIST_HEAD(&p->unused); /* Link the node. */ @@ -534,63 +449,14 @@ found: /* The existing node has been found. } write_sequnlock_bh(&base->lock); - if (base->total >= inet_peer_threshold) - /* Remove one less-recently-used entry. */ - cleanup_once(0, stack); - return p; } - -static int compute_total(void) -{ - return v4_peers.total + v6_peers.total; -} EXPORT_SYMBOL_GPL(inet_getpeer); -/* Called with local BH disabled. */ -static void peer_check_expire(unsigned long dummy) -{ - unsigned long now = jiffies; - int ttl, total; - struct inet_peer __rcu **stack[PEER_MAXDEPTH]; - - total = compute_total(); - if (total >= inet_peer_threshold) - ttl = inet_peer_minttl; - else - ttl = inet_peer_maxttl - - (inet_peer_maxttl - inet_peer_minttl) / HZ * - total / inet_peer_threshold * HZ; - while (!cleanup_once(ttl, stack)) { - if (jiffies != now) - break; - } - - /* Trigger the timer after inet_peer_gc_mintime .. inet_peer_gc_maxtime - * interval depending on the total number of entries (more entries, - * less interval). */ - total = compute_total(); - if (total >= inet_peer_threshold) - peer_periodic_timer.expires = jiffies + inet_peer_gc_mintime; - else - peer_periodic_timer.expires = jiffies - + inet_peer_gc_maxtime - - (inet_peer_gc_maxtime - inet_peer_gc_mintime) / HZ * - total / inet_peer_threshold * HZ; - add_timer(&peer_periodic_timer); -} - void inet_putpeer(struct inet_peer *p) { - local_bh_disable(); - - if (atomic_dec_and_lock(&p->refcnt, &unused_peers.lock)) { - list_add_tail(&p->unused, &unused_peers.list); - p->dtime = (__u32)jiffies; - spin_unlock(&unused_peers.lock); - } - - local_bh_enable(); + p->dtime = (__u32)jiffies; + atomic_dec(&p->refcnt); } EXPORT_SYMBOL_GPL(inet_putpeer); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 57d0752e239a..69fd7201129a 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -397,20 +397,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, - { - .procname = "inet_peer_gc_mintime", - .data = &inet_peer_gc_mintime, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { - .procname = "inet_peer_gc_maxtime", - .data = &inet_peer_gc_maxtime, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, { .procname = "tcp_orphan_retries", .data = &sysctl_tcp_orphan_retries, -- cgit v1.2.3 From 2b77bdde97ae8241dcc23110a4e837acfbc83438 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Jun 2011 23:31:27 -0700 Subject: inetpeer: lower false sharing effect Profiles show false sharing in addr_compare() because refcnt/dtime changes dirty the first inet_peer cache line, where are lying the keys used at lookup time. If many cpus are calling inet_getpeer() and inet_putpeer(), or need frag ids, addr_compare() is in 2nd position in "perf top". Before patch, my udpflood bench (16 threads) on my 2x4x2 machine : 5784.00 9.7% csum_partial_copy_generic [kernel] 3356.00 5.6% addr_compare [kernel] 2638.00 4.4% fib_table_lookup [kernel] 2625.00 4.4% ip_fragment [kernel] 1934.00 3.2% neigh_lookup [kernel] 1617.00 2.7% udp_sendmsg [kernel] 1608.00 2.7% __ip_route_output_key [kernel] 1480.00 2.5% __ip_append_data [kernel] 1396.00 2.3% kfree [kernel] 1195.00 2.0% kmem_cache_free [kernel] 1157.00 1.9% inet_getpeer [kernel] 1121.00 1.9% neigh_resolve_output [kernel] 1012.00 1.7% dev_queue_xmit [kernel] # time ./udpflood.sh real 0m44.511s user 0m20.020s sys 11m22.780s # time ./udpflood.sh real 0m44.099s user 0m20.140s sys 11m15.870s After patch, no more addr_compare() in profiles : 4171.00 10.7% csum_partial_copy_generic [kernel] 1787.00 4.6% fib_table_lookup [kernel] 1756.00 4.5% ip_fragment [kernel] 1234.00 3.2% udp_sendmsg [kernel] 1191.00 3.0% neigh_lookup [kernel] 1118.00 2.9% __ip_append_data [kernel] 1022.00 2.6% kfree [kernel] 993.00 2.5% __ip_route_output_key [kernel] 841.00 2.2% neigh_resolve_output [kernel] 816.00 2.1% kmem_cache_free [kernel] 658.00 1.7% ia32_sysenter_target [kernel] 632.00 1.6% kmem_cache_alloc_node [kernel] # time ./udpflood.sh real 0m41.587s user 0m19.190s sys 10m36.370s # time ./udpflood.sh real 0m41.486s user 0m19.290s sys 10m33.650s Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inetpeer.h | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 1f0966f91760..39d123081e7e 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -32,12 +32,17 @@ struct inet_peer { struct inet_peer __rcu *avl_left, *avl_right; struct inetpeer_addr daddr; __u32 avl_height; - __u32 dtime; /* the time of last use of not - * referenced entries */ - atomic_t refcnt; + + u32 metrics[RTAX_MAX]; + u32 rate_tokens; /* rate limiting for ICMP */ + unsigned long rate_last; + unsigned long pmtu_expires; + u32 pmtu_orig; + u32 pmtu_learned; + struct inetpeer_addr_base redirect_learned; /* * Once inet_peer is queued for deletion (refcnt == -1), following fields - * are not available: rid, ip_id_count, tcp_ts, tcp_ts_stamp, metrics + * are not available: rid, ip_id_count, tcp_ts, tcp_ts_stamp * We can share memory with rcu_head to help keep inet_peer small. */ union { @@ -46,17 +51,14 @@ struct inet_peer { atomic_t ip_id_count; /* IP ID for the next packet */ __u32 tcp_ts; __u32 tcp_ts_stamp; - u32 metrics[RTAX_MAX]; - u32 rate_tokens; /* rate limiting for ICMP */ - unsigned long rate_last; - unsigned long pmtu_expires; - u32 pmtu_orig; - u32 pmtu_learned; - struct inetpeer_addr_base redirect_learned; }; struct rcu_head rcu; struct inet_peer *gc_next; }; + + /* following fields might be frequently dirtied */ + __u32 dtime; /* the time of last use of not referenced entries */ + atomic_t refcnt; }; void inet_initpeers(void) __init; -- cgit v1.2.3 From 76369139ceb955deefc509e6e12ce9d6ce50ccab Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 19 May 2011 19:55:04 +0200 Subject: perf: Split up buffer handling from core code And create the internal perf events header. v2: Keep an internal inlined perf_output_copy() Signed-off-by: Frederic Weisbecker Acked-by: Peter Zijlstra Cc: Borislav Petkov Cc: Stephane Eranian Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1305827704-5607-1-git-send-email-fweisbec@gmail.com [ v3: use clearer 'ring_buffer' and 'rb' naming ] Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 33 +-- kernel/events/Makefile | 2 +- kernel/events/core.c | 568 ++++++-------------------------------------- kernel/events/internal.h | 97 ++++++++ kernel/events/ring_buffer.c | 399 +++++++++++++++++++++++++++++++ 5 files changed, 572 insertions(+), 527 deletions(-) create mode 100644 kernel/events/internal.h create mode 100644 kernel/events/ring_buffer.c (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 3412684ce5d5..779f6ed54d52 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -680,33 +680,6 @@ enum perf_event_active_state { }; struct file; - -#define PERF_BUFFER_WRITABLE 0x01 - -struct perf_buffer { - atomic_t refcount; - struct rcu_head rcu_head; -#ifdef CONFIG_PERF_USE_VMALLOC - struct work_struct work; - int page_order; /* allocation order */ -#endif - int nr_pages; /* nr of data pages */ - int writable; /* are we writable */ - - atomic_t poll; /* POLL_ for wakeups */ - - local_t head; /* write position */ - local_t nest; /* nested writers */ - local_t events; /* event limit */ - local_t wakeup; /* wakeup stamp */ - local_t lost; /* nr records lost */ - - long watermark; /* wakeup watermark */ - - struct perf_event_mmap_page *user_page; - void *data_pages[0]; -}; - struct perf_sample_data; typedef void (*perf_overflow_handler_t)(struct perf_event *, int, @@ -745,6 +718,8 @@ struct perf_cgroup { }; #endif +struct ring_buffer; + /** * struct perf_event - performance event kernel representation: */ @@ -834,7 +809,7 @@ struct perf_event { atomic_t mmap_count; int mmap_locked; struct user_struct *mmap_user; - struct perf_buffer *buffer; + struct ring_buffer *rb; /* poll related */ wait_queue_head_t waitq; @@ -945,7 +920,7 @@ struct perf_cpu_context { struct perf_output_handle { struct perf_event *event; - struct perf_buffer *buffer; + struct ring_buffer *rb; unsigned long wakeup; unsigned long size; void *addr; diff --git a/kernel/events/Makefile b/kernel/events/Makefile index 1ce23d3d8394..89e5e8aa4c36 100644 --- a/kernel/events/Makefile +++ b/kernel/events/Makefile @@ -2,5 +2,5 @@ ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_core.o = -pg endif -obj-y := core.o +obj-y := core.o ring_buffer.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o diff --git a/kernel/events/core.c b/kernel/events/core.c index 5e8c7b1389bc..5e70f62752a2 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -36,6 +36,8 @@ #include #include +#include "internal.h" + #include struct remote_function_call { @@ -2886,7 +2888,7 @@ static void free_event_rcu(struct rcu_head *head) kfree(event); } -static void perf_buffer_put(struct perf_buffer *buffer); +static void ring_buffer_put(struct ring_buffer *rb); static void free_event(struct perf_event *event) { @@ -2909,9 +2911,9 @@ static void free_event(struct perf_event *event) } } - if (event->buffer) { - perf_buffer_put(event->buffer); - event->buffer = NULL; + if (event->rb) { + ring_buffer_put(event->rb); + event->rb = NULL; } if (is_cgroup_event(event)) @@ -3139,13 +3141,13 @@ perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) static unsigned int perf_poll(struct file *file, poll_table *wait) { struct perf_event *event = file->private_data; - struct perf_buffer *buffer; + struct ring_buffer *rb; unsigned int events = POLL_HUP; rcu_read_lock(); - buffer = rcu_dereference(event->buffer); - if (buffer) - events = atomic_xchg(&buffer->poll, 0); + rb = rcu_dereference(event->rb); + if (rb) + events = atomic_xchg(&rb->poll, 0); rcu_read_unlock(); poll_wait(file, &event->waitq, wait); @@ -3356,14 +3358,14 @@ static int perf_event_index(struct perf_event *event) void perf_event_update_userpage(struct perf_event *event) { struct perf_event_mmap_page *userpg; - struct perf_buffer *buffer; + struct ring_buffer *rb; rcu_read_lock(); - buffer = rcu_dereference(event->buffer); - if (!buffer) + rb = rcu_dereference(event->rb); + if (!rb) goto unlock; - userpg = buffer->user_page; + userpg = rb->user_page; /* * Disable preemption so as to not let the corresponding user-space @@ -3390,220 +3392,10 @@ unlock: rcu_read_unlock(); } -static unsigned long perf_data_size(struct perf_buffer *buffer); - -static void -perf_buffer_init(struct perf_buffer *buffer, long watermark, int flags) -{ - long max_size = perf_data_size(buffer); - - if (watermark) - buffer->watermark = min(max_size, watermark); - - if (!buffer->watermark) - buffer->watermark = max_size / 2; - - if (flags & PERF_BUFFER_WRITABLE) - buffer->writable = 1; - - atomic_set(&buffer->refcount, 1); -} - -#ifndef CONFIG_PERF_USE_VMALLOC - -/* - * Back perf_mmap() with regular GFP_KERNEL-0 pages. - */ - -static struct page * -perf_mmap_to_page(struct perf_buffer *buffer, unsigned long pgoff) -{ - if (pgoff > buffer->nr_pages) - return NULL; - - if (pgoff == 0) - return virt_to_page(buffer->user_page); - - return virt_to_page(buffer->data_pages[pgoff - 1]); -} - -static void *perf_mmap_alloc_page(int cpu) -{ - struct page *page; - int node; - - node = (cpu == -1) ? cpu : cpu_to_node(cpu); - page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0); - if (!page) - return NULL; - - return page_address(page); -} - -static struct perf_buffer * -perf_buffer_alloc(int nr_pages, long watermark, int cpu, int flags) -{ - struct perf_buffer *buffer; - unsigned long size; - int i; - - size = sizeof(struct perf_buffer); - size += nr_pages * sizeof(void *); - - buffer = kzalloc(size, GFP_KERNEL); - if (!buffer) - goto fail; - - buffer->user_page = perf_mmap_alloc_page(cpu); - if (!buffer->user_page) - goto fail_user_page; - - for (i = 0; i < nr_pages; i++) { - buffer->data_pages[i] = perf_mmap_alloc_page(cpu); - if (!buffer->data_pages[i]) - goto fail_data_pages; - } - - buffer->nr_pages = nr_pages; - - perf_buffer_init(buffer, watermark, flags); - - return buffer; - -fail_data_pages: - for (i--; i >= 0; i--) - free_page((unsigned long)buffer->data_pages[i]); - - free_page((unsigned long)buffer->user_page); - -fail_user_page: - kfree(buffer); - -fail: - return NULL; -} - -static void perf_mmap_free_page(unsigned long addr) -{ - struct page *page = virt_to_page((void *)addr); - - page->mapping = NULL; - __free_page(page); -} - -static void perf_buffer_free(struct perf_buffer *buffer) -{ - int i; - - perf_mmap_free_page((unsigned long)buffer->user_page); - for (i = 0; i < buffer->nr_pages; i++) - perf_mmap_free_page((unsigned long)buffer->data_pages[i]); - kfree(buffer); -} - -static inline int page_order(struct perf_buffer *buffer) -{ - return 0; -} - -#else - -/* - * Back perf_mmap() with vmalloc memory. - * - * Required for architectures that have d-cache aliasing issues. - */ - -static inline int page_order(struct perf_buffer *buffer) -{ - return buffer->page_order; -} - -static struct page * -perf_mmap_to_page(struct perf_buffer *buffer, unsigned long pgoff) -{ - if (pgoff > (1UL << page_order(buffer))) - return NULL; - - return vmalloc_to_page((void *)buffer->user_page + pgoff * PAGE_SIZE); -} - -static void perf_mmap_unmark_page(void *addr) -{ - struct page *page = vmalloc_to_page(addr); - - page->mapping = NULL; -} - -static void perf_buffer_free_work(struct work_struct *work) -{ - struct perf_buffer *buffer; - void *base; - int i, nr; - - buffer = container_of(work, struct perf_buffer, work); - nr = 1 << page_order(buffer); - - base = buffer->user_page; - for (i = 0; i < nr + 1; i++) - perf_mmap_unmark_page(base + (i * PAGE_SIZE)); - - vfree(base); - kfree(buffer); -} - -static void perf_buffer_free(struct perf_buffer *buffer) -{ - schedule_work(&buffer->work); -} - -static struct perf_buffer * -perf_buffer_alloc(int nr_pages, long watermark, int cpu, int flags) -{ - struct perf_buffer *buffer; - unsigned long size; - void *all_buf; - - size = sizeof(struct perf_buffer); - size += sizeof(void *); - - buffer = kzalloc(size, GFP_KERNEL); - if (!buffer) - goto fail; - - INIT_WORK(&buffer->work, perf_buffer_free_work); - - all_buf = vmalloc_user((nr_pages + 1) * PAGE_SIZE); - if (!all_buf) - goto fail_all_buf; - - buffer->user_page = all_buf; - buffer->data_pages[0] = all_buf + PAGE_SIZE; - buffer->page_order = ilog2(nr_pages); - buffer->nr_pages = 1; - - perf_buffer_init(buffer, watermark, flags); - - return buffer; - -fail_all_buf: - kfree(buffer); - -fail: - return NULL; -} - -#endif - -static unsigned long perf_data_size(struct perf_buffer *buffer) -{ - return buffer->nr_pages << (PAGE_SHIFT + page_order(buffer)); -} - static int perf_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct perf_event *event = vma->vm_file->private_data; - struct perf_buffer *buffer; + struct ring_buffer *rb; int ret = VM_FAULT_SIGBUS; if (vmf->flags & FAULT_FLAG_MKWRITE) { @@ -3613,14 +3405,14 @@ static int perf_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) } rcu_read_lock(); - buffer = rcu_dereference(event->buffer); - if (!buffer) + rb = rcu_dereference(event->rb); + if (!rb) goto unlock; if (vmf->pgoff && (vmf->flags & FAULT_FLAG_WRITE)) goto unlock; - vmf->page = perf_mmap_to_page(buffer, vmf->pgoff); + vmf->page = perf_mmap_to_page(rb, vmf->pgoff); if (!vmf->page) goto unlock; @@ -3635,35 +3427,35 @@ unlock: return ret; } -static void perf_buffer_free_rcu(struct rcu_head *rcu_head) +static void rb_free_rcu(struct rcu_head *rcu_head) { - struct perf_buffer *buffer; + struct ring_buffer *rb; - buffer = container_of(rcu_head, struct perf_buffer, rcu_head); - perf_buffer_free(buffer); + rb = container_of(rcu_head, struct ring_buffer, rcu_head); + rb_free(rb); } -static struct perf_buffer *perf_buffer_get(struct perf_event *event) +static struct ring_buffer *ring_buffer_get(struct perf_event *event) { - struct perf_buffer *buffer; + struct ring_buffer *rb; rcu_read_lock(); - buffer = rcu_dereference(event->buffer); - if (buffer) { - if (!atomic_inc_not_zero(&buffer->refcount)) - buffer = NULL; + rb = rcu_dereference(event->rb); + if (rb) { + if (!atomic_inc_not_zero(&rb->refcount)) + rb = NULL; } rcu_read_unlock(); - return buffer; + return rb; } -static void perf_buffer_put(struct perf_buffer *buffer) +static void ring_buffer_put(struct ring_buffer *rb) { - if (!atomic_dec_and_test(&buffer->refcount)) + if (!atomic_dec_and_test(&rb->refcount)) return; - call_rcu(&buffer->rcu_head, perf_buffer_free_rcu); + call_rcu(&rb->rcu_head, rb_free_rcu); } static void perf_mmap_open(struct vm_area_struct *vma) @@ -3678,16 +3470,16 @@ static void perf_mmap_close(struct vm_area_struct *vma) struct perf_event *event = vma->vm_file->private_data; if (atomic_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex)) { - unsigned long size = perf_data_size(event->buffer); + unsigned long size = perf_data_size(event->rb); struct user_struct *user = event->mmap_user; - struct perf_buffer *buffer = event->buffer; + struct ring_buffer *rb = event->rb; atomic_long_sub((size >> PAGE_SHIFT) + 1, &user->locked_vm); vma->vm_mm->locked_vm -= event->mmap_locked; - rcu_assign_pointer(event->buffer, NULL); + rcu_assign_pointer(event->rb, NULL); mutex_unlock(&event->mmap_mutex); - perf_buffer_put(buffer); + ring_buffer_put(rb); free_uid(user); } } @@ -3705,7 +3497,7 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) unsigned long user_locked, user_lock_limit; struct user_struct *user = current_user(); unsigned long locked, lock_limit; - struct perf_buffer *buffer; + struct ring_buffer *rb; unsigned long vma_size; unsigned long nr_pages; long user_extra, extra; @@ -3714,7 +3506,7 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) /* * Don't allow mmap() of inherited per-task counters. This would * create a performance issue due to all children writing to the - * same buffer. + * same rb. */ if (event->cpu == -1 && event->attr.inherit) return -EINVAL; @@ -3726,7 +3518,7 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) nr_pages = (vma_size / PAGE_SIZE) - 1; /* - * If we have buffer pages ensure they're a power-of-two number, so we + * If we have rb pages ensure they're a power-of-two number, so we * can do bitmasks instead of modulo. */ if (nr_pages != 0 && !is_power_of_2(nr_pages)) @@ -3740,9 +3532,9 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) WARN_ON_ONCE(event->ctx->parent_ctx); mutex_lock(&event->mmap_mutex); - if (event->buffer) { - if (event->buffer->nr_pages == nr_pages) - atomic_inc(&event->buffer->refcount); + if (event->rb) { + if (event->rb->nr_pages == nr_pages) + atomic_inc(&event->rb->refcount); else ret = -EINVAL; goto unlock; @@ -3772,18 +3564,18 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) goto unlock; } - WARN_ON(event->buffer); + WARN_ON(event->rb); if (vma->vm_flags & VM_WRITE) - flags |= PERF_BUFFER_WRITABLE; + flags |= RING_BUFFER_WRITABLE; - buffer = perf_buffer_alloc(nr_pages, event->attr.wakeup_watermark, + rb = rb_alloc(nr_pages, event->attr.wakeup_watermark, event->cpu, flags); - if (!buffer) { + if (!rb) { ret = -ENOMEM; goto unlock; } - rcu_assign_pointer(event->buffer, buffer); + rcu_assign_pointer(event->rb, rb); atomic_long_add(user_extra, &user->locked_vm); event->mmap_locked = extra; @@ -3882,117 +3674,6 @@ int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) } EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks); -/* - * Output - */ -static bool perf_output_space(struct perf_buffer *buffer, unsigned long tail, - unsigned long offset, unsigned long head) -{ - unsigned long mask; - - if (!buffer->writable) - return true; - - mask = perf_data_size(buffer) - 1; - - offset = (offset - tail) & mask; - head = (head - tail) & mask; - - if ((int)(head - offset) < 0) - return false; - - return true; -} - -static void perf_output_wakeup(struct perf_output_handle *handle) -{ - atomic_set(&handle->buffer->poll, POLL_IN); - - if (handle->nmi) { - handle->event->pending_wakeup = 1; - irq_work_queue(&handle->event->pending); - } else - perf_event_wakeup(handle->event); -} - -/* - * We need to ensure a later event_id doesn't publish a head when a former - * event isn't done writing. However since we need to deal with NMIs we - * cannot fully serialize things. - * - * We only publish the head (and generate a wakeup) when the outer-most - * event completes. - */ -static void perf_output_get_handle(struct perf_output_handle *handle) -{ - struct perf_buffer *buffer = handle->buffer; - - preempt_disable(); - local_inc(&buffer->nest); - handle->wakeup = local_read(&buffer->wakeup); -} - -static void perf_output_put_handle(struct perf_output_handle *handle) -{ - struct perf_buffer *buffer = handle->buffer; - unsigned long head; - -again: - head = local_read(&buffer->head); - - /* - * IRQ/NMI can happen here, which means we can miss a head update. - */ - - if (!local_dec_and_test(&buffer->nest)) - goto out; - - /* - * Publish the known good head. Rely on the full barrier implied - * by atomic_dec_and_test() order the buffer->head read and this - * write. - */ - buffer->user_page->data_head = head; - - /* - * Now check if we missed an update, rely on the (compiler) - * barrier in atomic_dec_and_test() to re-read buffer->head. - */ - if (unlikely(head != local_read(&buffer->head))) { - local_inc(&buffer->nest); - goto again; - } - - if (handle->wakeup != local_read(&buffer->wakeup)) - perf_output_wakeup(handle); - -out: - preempt_enable(); -} - -__always_inline void perf_output_copy(struct perf_output_handle *handle, - const void *buf, unsigned int len) -{ - do { - unsigned long size = min_t(unsigned long, handle->size, len); - - memcpy(handle->addr, buf, size); - - len -= size; - handle->addr += size; - buf += size; - handle->size -= size; - if (!handle->size) { - struct perf_buffer *buffer = handle->buffer; - - handle->page++; - handle->page &= buffer->nr_pages - 1; - handle->addr = buffer->data_pages[handle->page]; - handle->size = PAGE_SIZE << page_order(buffer); - } - } while (len); -} - static void __perf_event_header__init_id(struct perf_event_header *header, struct perf_sample_data *data, struct perf_event *event) @@ -4023,9 +3704,9 @@ static void __perf_event_header__init_id(struct perf_event_header *header, } } -static void perf_event_header__init_id(struct perf_event_header *header, - struct perf_sample_data *data, - struct perf_event *event) +void perf_event_header__init_id(struct perf_event_header *header, + struct perf_sample_data *data, + struct perf_event *event) { if (event->attr.sample_id_all) __perf_event_header__init_id(header, data, event); @@ -4052,121 +3733,14 @@ static void __perf_event__output_id_sample(struct perf_output_handle *handle, perf_output_put(handle, data->cpu_entry); } -static void perf_event__output_id_sample(struct perf_event *event, - struct perf_output_handle *handle, - struct perf_sample_data *sample) +void perf_event__output_id_sample(struct perf_event *event, + struct perf_output_handle *handle, + struct perf_sample_data *sample) { if (event->attr.sample_id_all) __perf_event__output_id_sample(handle, sample); } -int perf_output_begin(struct perf_output_handle *handle, - struct perf_event *event, unsigned int size, - int nmi, int sample) -{ - struct perf_buffer *buffer; - unsigned long tail, offset, head; - int have_lost; - struct perf_sample_data sample_data; - struct { - struct perf_event_header header; - u64 id; - u64 lost; - } lost_event; - - rcu_read_lock(); - /* - * For inherited events we send all the output towards the parent. - */ - if (event->parent) - event = event->parent; - - buffer = rcu_dereference(event->buffer); - if (!buffer) - goto out; - - handle->buffer = buffer; - handle->event = event; - handle->nmi = nmi; - handle->sample = sample; - - if (!buffer->nr_pages) - goto out; - - have_lost = local_read(&buffer->lost); - if (have_lost) { - lost_event.header.size = sizeof(lost_event); - perf_event_header__init_id(&lost_event.header, &sample_data, - event); - size += lost_event.header.size; - } - - perf_output_get_handle(handle); - - do { - /* - * Userspace could choose to issue a mb() before updating the - * tail pointer. So that all reads will be completed before the - * write is issued. - */ - tail = ACCESS_ONCE(buffer->user_page->data_tail); - smp_rmb(); - offset = head = local_read(&buffer->head); - head += size; - if (unlikely(!perf_output_space(buffer, tail, offset, head))) - goto fail; - } while (local_cmpxchg(&buffer->head, offset, head) != offset); - - if (head - local_read(&buffer->wakeup) > buffer->watermark) - local_add(buffer->watermark, &buffer->wakeup); - - handle->page = offset >> (PAGE_SHIFT + page_order(buffer)); - handle->page &= buffer->nr_pages - 1; - handle->size = offset & ((PAGE_SIZE << page_order(buffer)) - 1); - handle->addr = buffer->data_pages[handle->page]; - handle->addr += handle->size; - handle->size = (PAGE_SIZE << page_order(buffer)) - handle->size; - - if (have_lost) { - lost_event.header.type = PERF_RECORD_LOST; - lost_event.header.misc = 0; - lost_event.id = event->id; - lost_event.lost = local_xchg(&buffer->lost, 0); - - perf_output_put(handle, lost_event); - perf_event__output_id_sample(event, handle, &sample_data); - } - - return 0; - -fail: - local_inc(&buffer->lost); - perf_output_put_handle(handle); -out: - rcu_read_unlock(); - - return -ENOSPC; -} - -void perf_output_end(struct perf_output_handle *handle) -{ - struct perf_event *event = handle->event; - struct perf_buffer *buffer = handle->buffer; - - int wakeup_events = event->attr.wakeup_events; - - if (handle->sample && wakeup_events) { - int events = local_inc_return(&buffer->events); - if (events >= wakeup_events) { - local_sub(wakeup_events, &buffer->events); - local_inc(&buffer->wakeup); - } - } - - perf_output_put_handle(handle); - rcu_read_unlock(); -} - static void perf_output_read_one(struct perf_output_handle *handle, struct perf_event *event, u64 enabled, u64 running) @@ -4187,7 +3761,7 @@ static void perf_output_read_one(struct perf_output_handle *handle, if (read_format & PERF_FORMAT_ID) values[n++] = primary_event_id(event); - perf_output_copy(handle, values, n * sizeof(u64)); + __output_copy(handle, values, n * sizeof(u64)); } /* @@ -4217,7 +3791,7 @@ static void perf_output_read_group(struct perf_output_handle *handle, if (read_format & PERF_FORMAT_ID) values[n++] = primary_event_id(leader); - perf_output_copy(handle, values, n * sizeof(u64)); + __output_copy(handle, values, n * sizeof(u64)); list_for_each_entry(sub, &leader->sibling_list, group_entry) { n = 0; @@ -4229,7 +3803,7 @@ static void perf_output_read_group(struct perf_output_handle *handle, if (read_format & PERF_FORMAT_ID) values[n++] = primary_event_id(sub); - perf_output_copy(handle, values, n * sizeof(u64)); + __output_copy(handle, values, n * sizeof(u64)); } } @@ -4309,7 +3883,7 @@ void perf_output_sample(struct perf_output_handle *handle, size *= sizeof(u64); - perf_output_copy(handle, data->callchain, size); + __output_copy(handle, data->callchain, size); } else { u64 nr = 0; perf_output_put(handle, nr); @@ -4319,8 +3893,8 @@ void perf_output_sample(struct perf_output_handle *handle, if (sample_type & PERF_SAMPLE_RAW) { if (data->raw) { perf_output_put(handle, data->raw->size); - perf_output_copy(handle, data->raw->data, - data->raw->size); + __output_copy(handle, data->raw->data, + data->raw->size); } else { struct { u32 size; @@ -4617,7 +4191,7 @@ static void perf_event_comm_output(struct perf_event *event, comm_event->event_id.tid = perf_event_tid(event, comm_event->task); perf_output_put(&handle, comm_event->event_id); - perf_output_copy(&handle, comm_event->comm, + __output_copy(&handle, comm_event->comm, comm_event->comm_size); perf_event__output_id_sample(event, &handle, &sample); @@ -4763,7 +4337,7 @@ static void perf_event_mmap_output(struct perf_event *event, mmap_event->event_id.tid = perf_event_tid(event, current); perf_output_put(&handle, mmap_event->event_id); - perf_output_copy(&handle, mmap_event->file_name, + __output_copy(&handle, mmap_event->file_name, mmap_event->file_size); perf_event__output_id_sample(event, &handle, &sample); @@ -4819,7 +4393,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) if (file) { /* - * d_path works from the end of the buffer backwards, so we + * d_path works from the end of the rb backwards, so we * need to add enough zero bytes after the string to handle * the 64bit alignment we do later. */ @@ -6346,7 +5920,7 @@ err_size: static int perf_event_set_output(struct perf_event *event, struct perf_event *output_event) { - struct perf_buffer *buffer = NULL, *old_buffer = NULL; + struct ring_buffer *rb = NULL, *old_rb = NULL; int ret = -EINVAL; if (!output_event) @@ -6363,7 +5937,7 @@ perf_event_set_output(struct perf_event *event, struct perf_event *output_event) goto out; /* - * If its not a per-cpu buffer, it must be the same task. + * If its not a per-cpu rb, it must be the same task. */ if (output_event->cpu == -1 && output_event->ctx != event->ctx) goto out; @@ -6375,20 +5949,20 @@ set: goto unlock; if (output_event) { - /* get the buffer we want to redirect to */ - buffer = perf_buffer_get(output_event); - if (!buffer) + /* get the rb we want to redirect to */ + rb = ring_buffer_get(output_event); + if (!rb) goto unlock; } - old_buffer = event->buffer; - rcu_assign_pointer(event->buffer, buffer); + old_rb = event->rb; + rcu_assign_pointer(event->rb, rb); ret = 0; unlock: mutex_unlock(&event->mmap_mutex); - if (old_buffer) - perf_buffer_put(old_buffer); + if (old_rb) + ring_buffer_put(old_rb); out: return ret; } diff --git a/kernel/events/internal.h b/kernel/events/internal.h new file mode 100644 index 000000000000..114f27f3a624 --- /dev/null +++ b/kernel/events/internal.h @@ -0,0 +1,97 @@ +#ifndef _KERNEL_EVENTS_INTERNAL_H +#define _KERNEL_EVENTS_INTERNAL_H + +#define RING_BUFFER_WRITABLE 0x01 + +struct ring_buffer { + atomic_t refcount; + struct rcu_head rcu_head; +#ifdef CONFIG_PERF_USE_VMALLOC + struct work_struct work; + int page_order; /* allocation order */ +#endif + int nr_pages; /* nr of data pages */ + int writable; /* are we writable */ + + atomic_t poll; /* POLL_ for wakeups */ + + local_t head; /* write position */ + local_t nest; /* nested writers */ + local_t events; /* event limit */ + local_t wakeup; /* wakeup stamp */ + local_t lost; /* nr records lost */ + + long watermark; /* wakeup watermark */ + + struct perf_event_mmap_page *user_page; + void *data_pages[0]; +}; + + +extern void rb_free(struct ring_buffer *rb); +extern struct ring_buffer * +rb_alloc(int nr_pages, long watermark, int cpu, int flags); +extern void perf_event_wakeup(struct perf_event *event); + +extern void +perf_event_header__init_id(struct perf_event_header *header, + struct perf_sample_data *data, + struct perf_event *event); +extern void +perf_event__output_id_sample(struct perf_event *event, + struct perf_output_handle *handle, + struct perf_sample_data *sample); + +extern struct page * +perf_mmap_to_page(struct ring_buffer *rb, unsigned long pgoff); + +#ifdef CONFIG_PERF_USE_VMALLOC +/* + * Back perf_mmap() with vmalloc memory. + * + * Required for architectures that have d-cache aliasing issues. + */ + +static inline int page_order(struct ring_buffer *rb) +{ + return rb->page_order; +} + +#else + +static inline int page_order(struct ring_buffer *rb) +{ + return 0; +} +#endif + +static unsigned long perf_data_size(struct ring_buffer *rb) +{ + return rb->nr_pages << (PAGE_SHIFT + page_order(rb)); +} + +static inline void +__output_copy(struct perf_output_handle *handle, + const void *buf, unsigned int len) +{ + do { + unsigned long size = min_t(unsigned long, handle->size, len); + + memcpy(handle->addr, buf, size); + + len -= size; + handle->addr += size; + buf += size; + handle->size -= size; + if (!handle->size) { + struct ring_buffer *rb = handle->rb; + + handle->page++; + handle->page &= rb->nr_pages - 1; + handle->addr = rb->data_pages[handle->page]; + handle->size = PAGE_SIZE << page_order(rb); + } + } while (len); +} + +#endif /* _KERNEL_EVENTS_INTERNAL_H */ diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c new file mode 100644 index 000000000000..fde52595d8f7 --- /dev/null +++ b/kernel/events/ring_buffer.c @@ -0,0 +1,399 @@ +/* + * Performance events ring-buffer code: + * + * Copyright (C) 2008 Thomas Gleixner + * Copyright (C) 2008-2011 Red Hat, Inc., Ingo Molnar + * Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra + * Copyright © 2009 Paul Mackerras, IBM Corp. + * + * For licensing details see kernel-base/COPYING + */ + +#include +#include +#include + +#include "internal.h" + +static bool perf_output_space(struct ring_buffer *rb, unsigned long tail, + unsigned long offset, unsigned long head) +{ + unsigned long mask; + + if (!rb->writable) + return true; + + mask = perf_data_size(rb) - 1; + + offset = (offset - tail) & mask; + head = (head - tail) & mask; + + if ((int)(head - offset) < 0) + return false; + + return true; +} + +static void perf_output_wakeup(struct perf_output_handle *handle) +{ + atomic_set(&handle->rb->poll, POLL_IN); + + if (handle->nmi) { + handle->event->pending_wakeup = 1; + irq_work_queue(&handle->event->pending); + } else + perf_event_wakeup(handle->event); +} + +/* + * We need to ensure a later event_id doesn't publish a head when a former + * event isn't done writing. However since we need to deal with NMIs we + * cannot fully serialize things. + * + * We only publish the head (and generate a wakeup) when the outer-most + * event completes. + */ +static void perf_output_get_handle(struct perf_output_handle *handle) +{ + struct ring_buffer *rb = handle->rb; + + preempt_disable(); + local_inc(&rb->nest); + handle->wakeup = local_read(&rb->wakeup); +} + +static void perf_output_put_handle(struct perf_output_handle *handle) +{ + struct ring_buffer *rb = handle->rb; + unsigned long head; + +again: + head = local_read(&rb->head); + + /* + * IRQ/NMI can happen here, which means we can miss a head update. + */ + + if (!local_dec_and_test(&rb->nest)) + goto out; + + /* + * Publish the known good head. Rely on the full barrier implied + * by atomic_dec_and_test() order the rb->head read and this + * write. + */ + rb->user_page->data_head = head; + + /* + * Now check if we missed an update, rely on the (compiler) + * barrier in atomic_dec_and_test() to re-read rb->head. + */ + if (unlikely(head != local_read(&rb->head))) { + local_inc(&rb->nest); + goto again; + } + + if (handle->wakeup != local_read(&rb->wakeup)) + perf_output_wakeup(handle); + +out: + preempt_enable(); +} + +int perf_output_begin(struct perf_output_handle *handle, + struct perf_event *event, unsigned int size, + int nmi, int sample) +{ + struct ring_buffer *rb; + unsigned long tail, offset, head; + int have_lost; + struct perf_sample_data sample_data; + struct { + struct perf_event_header header; + u64 id; + u64 lost; + } lost_event; + + rcu_read_lock(); + /* + * For inherited events we send all the output towards the parent. + */ + if (event->parent) + event = event->parent; + + rb = rcu_dereference(event->rb); + if (!rb) + goto out; + + handle->rb = rb; + handle->event = event; + handle->nmi = nmi; + handle->sample = sample; + + if (!rb->nr_pages) + goto out; + + have_lost = local_read(&rb->lost); + if (have_lost) { + lost_event.header.size = sizeof(lost_event); + perf_event_header__init_id(&lost_event.header, &sample_data, + event); + size += lost_event.header.size; + } + + perf_output_get_handle(handle); + + do { + /* + * Userspace could choose to issue a mb() before updating the + * tail pointer. So that all reads will be completed before the + * write is issued. + */ + tail = ACCESS_ONCE(rb->user_page->data_tail); + smp_rmb(); + offset = head = local_read(&rb->head); + head += size; + if (unlikely(!perf_output_space(rb, tail, offset, head))) + goto fail; + } while (local_cmpxchg(&rb->head, offset, head) != offset); + + if (head - local_read(&rb->wakeup) > rb->watermark) + local_add(rb->watermark, &rb->wakeup); + + handle->page = offset >> (PAGE_SHIFT + page_order(rb)); + handle->page &= rb->nr_pages - 1; + handle->size = offset & ((PAGE_SIZE << page_order(rb)) - 1); + handle->addr = rb->data_pages[handle->page]; + handle->addr += handle->size; + handle->size = (PAGE_SIZE << page_order(rb)) - handle->size; + + if (have_lost) { + lost_event.header.type = PERF_RECORD_LOST; + lost_event.header.misc = 0; + lost_event.id = event->id; + lost_event.lost = local_xchg(&rb->lost, 0); + + perf_output_put(handle, lost_event); + perf_event__output_id_sample(event, handle, &sample_data); + } + + return 0; + +fail: + local_inc(&rb->lost); + perf_output_put_handle(handle); +out: + rcu_read_unlock(); + + return -ENOSPC; +} + +void perf_output_copy(struct perf_output_handle *handle, + const void *buf, unsigned int len) +{ + __output_copy(handle, buf, len); +} + +void perf_output_end(struct perf_output_handle *handle) +{ + struct perf_event *event = handle->event; + struct ring_buffer *rb = handle->rb; + + int wakeup_events = event->attr.wakeup_events; + + if (handle->sample && wakeup_events) { + int events = local_inc_return(&rb->events); + if (events >= wakeup_events) { + local_sub(wakeup_events, &rb->events); + local_inc(&rb->wakeup); + } + } + + perf_output_put_handle(handle); + rcu_read_unlock(); +} + +static void +ring_buffer_init(struct ring_buffer *rb, long watermark, int flags) +{ + long max_size = perf_data_size(rb); + + if (watermark) + rb->watermark = min(max_size, watermark); + + if (!rb->watermark) + rb->watermark = max_size / 2; + + if (flags & RING_BUFFER_WRITABLE) + rb->writable = 1; + + atomic_set(&rb->refcount, 1); +} + +#ifndef CONFIG_PERF_USE_VMALLOC + +/* + * Back perf_mmap() with regular GFP_KERNEL-0 pages. + */ + +struct page * +perf_mmap_to_page(struct ring_buffer *rb, unsigned long pgoff) +{ + if (pgoff > rb->nr_pages) + return NULL; + + if (pgoff == 0) + return virt_to_page(rb->user_page); + + return virt_to_page(rb->data_pages[pgoff - 1]); +} + +static void *perf_mmap_alloc_page(int cpu) +{ + struct page *page; + int node; + + node = (cpu == -1) ? cpu : cpu_to_node(cpu); + page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0); + if (!page) + return NULL; + + return page_address(page); +} + +struct ring_buffer *rb_alloc(int nr_pages, long watermark, int cpu, int flags) +{ + struct ring_buffer *rb; + unsigned long size; + int i; + + size = sizeof(struct ring_buffer); + size += nr_pages * sizeof(void *); + + rb = kzalloc(size, GFP_KERNEL); + if (!rb) + goto fail; + + rb->user_page = perf_mmap_alloc_page(cpu); + if (!rb->user_page) + goto fail_user_page; + + for (i = 0; i < nr_pages; i++) { + rb->data_pages[i] = perf_mmap_alloc_page(cpu); + if (!rb->data_pages[i]) + goto fail_data_pages; + } + + rb->nr_pages = nr_pages; + + ring_buffer_init(rb, watermark, flags); + + return rb; + +fail_data_pages: + for (i--; i >= 0; i--) + free_page((unsigned long)rb->data_pages[i]); + + free_page((unsigned long)rb->user_page); + +fail_user_page: + kfree(rb); + +fail: + return NULL; +} + +static void perf_mmap_free_page(unsigned long addr) +{ + struct page *page = virt_to_page((void *)addr); + + page->mapping = NULL; + __free_page(page); +} + +void rb_free(struct ring_buffer *rb) +{ + int i; + + perf_mmap_free_page((unsigned long)rb->user_page); + for (i = 0; i < rb->nr_pages; i++) + perf_mmap_free_page((unsigned long)rb->data_pages[i]); + kfree(rb); +} + +#else + +struct page * +perf_mmap_to_page(struct ring_buffer *rb, unsigned long pgoff) +{ + if (pgoff > (1UL << page_order(rb))) + return NULL; + + return vmalloc_to_page((void *)rb->user_page + pgoff * PAGE_SIZE); +} + +static void perf_mmap_unmark_page(void *addr) +{ + struct page *page = vmalloc_to_page(addr); + + page->mapping = NULL; +} + +static void rb_free_work(struct work_struct *work) +{ + struct ring_buffer *rb; + void *base; + int i, nr; + + rb = container_of(work, struct ring_buffer, work); + nr = 1 << page_order(rb); + + base = rb->user_page; + for (i = 0; i < nr + 1; i++) + perf_mmap_unmark_page(base + (i * PAGE_SIZE)); + + vfree(base); + kfree(rb); +} + +void rb_free(struct ring_buffer *rb) +{ + schedule_work(&rb->work); +} + +struct ring_buffer *rb_alloc(int nr_pages, long watermark, int cpu, int flags) +{ + struct ring_buffer *rb; + unsigned long size; + void *all_buf; + + size = sizeof(struct ring_buffer); + size += sizeof(void *); + + rb = kzalloc(size, GFP_KERNEL); + if (!rb) + goto fail; + + INIT_WORK(&rb->work, rb_free_work); + + all_buf = vmalloc_user((nr_pages + 1) * PAGE_SIZE); + if (!all_buf) + goto fail_all_buf; + + rb->user_page = all_buf; + rb->data_pages[0] = all_buf + PAGE_SIZE; + rb->page_order = ilog2(nr_pages); + rb->nr_pages = 1; + + ring_buffer_init(rb, watermark, flags); + + return rb; + +fail_all_buf: + kfree(rb); + +fail: + return NULL; +} + +#endif -- cgit v1.2.3 From 334955ef964bee9d3b1e20966847eee28cfd05f6 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 1 Jun 2011 19:04:57 +0100 Subject: i8253: Create linux/i8253.h and use it in all 8253 related files Signed-off-by: Ralf Baechle Cc: linux-mips@linux-mips.org Link: http://lkml.kernel.org/r/20110601180610.054254048@duck.linux-mips.net Signed-off-by: Thomas Gleixner arch/arm/mach-footbridge/isa-timer.c | 2 +- arch/mips/cobalt/time.c | 2 +- arch/mips/jazz/irq.c | 2 +- arch/mips/kernel/i8253.c | 2 +- arch/mips/mti-malta/malta-time.c | 2 +- arch/mips/sgi-ip22/ip22-time.c | 2 +- arch/mips/sni/time.c | 2 +- arch/x86/kernel/apic/apic.c | 2 +- arch/x86/kernel/apm_32.c | 2 +- arch/x86/kernel/hpet.c | 2 +- arch/x86/kernel/i8253.c | 2 +- arch/x86/kernel/time.c | 2 +- drivers/block/hd.c | 2 +- drivers/clocksource/i8253.c | 2 +- drivers/input/gameport/gameport.c | 2 +- drivers/input/joystick/analog.c | 2 +- drivers/input/misc/pcspkr.c | 2 +- include/linux/i8253.h | 11 +++++++++++ sound/drivers/pcsp/pcsp.h | 2 +- 19 files changed, 29 insertions(+), 18 deletions(-) --- arch/arm/mach-footbridge/isa-timer.c | 2 +- arch/mips/cobalt/time.c | 2 +- arch/mips/jazz/irq.c | 2 +- arch/mips/kernel/i8253.c | 2 +- arch/mips/mti-malta/malta-time.c | 2 +- arch/mips/sgi-ip22/ip22-time.c | 2 +- arch/mips/sni/time.c | 2 +- arch/x86/kernel/apic/apic.c | 2 +- arch/x86/kernel/apm_32.c | 2 +- arch/x86/kernel/hpet.c | 2 +- arch/x86/kernel/i8253.c | 2 +- arch/x86/kernel/time.c | 2 +- drivers/block/hd.c | 2 +- drivers/clocksource/i8253.c | 2 +- drivers/input/gameport/gameport.c | 2 +- drivers/input/joystick/analog.c | 2 +- drivers/input/misc/pcspkr.c | 2 +- include/linux/i8253.h | 11 +++++++++++ sound/drivers/pcsp/pcsp.h | 2 +- 19 files changed, 29 insertions(+), 18 deletions(-) create mode 100644 include/linux/i8253.h (limited to 'include') diff --git a/arch/arm/mach-footbridge/isa-timer.c b/arch/arm/mach-footbridge/isa-timer.c index 7020f1a3feca..3c7367be5459 100644 --- a/arch/arm/mach-footbridge/isa-timer.c +++ b/arch/arm/mach-footbridge/isa-timer.c @@ -6,6 +6,7 @@ */ #include #include +#include #include #include #include @@ -14,7 +15,6 @@ #include #include -#include #include #include "common.h" diff --git a/arch/mips/cobalt/time.c b/arch/mips/cobalt/time.c index 0162f9edc693..3bff3b820baf 100644 --- a/arch/mips/cobalt/time.c +++ b/arch/mips/cobalt/time.c @@ -17,10 +17,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include -#include #include #define GT641XX_BASE_CLOCK 50000000 /* 50MHz */ diff --git a/arch/mips/jazz/irq.c b/arch/mips/jazz/irq.c index 260df4750949..ca9bd2069142 100644 --- a/arch/mips/jazz/irq.c +++ b/arch/mips/jazz/irq.c @@ -7,6 +7,7 @@ * Copyright (C) 1994 - 2001, 2003, 07 Ralf Baechle */ #include +#include #include #include #include @@ -15,7 +16,6 @@ #include #include -#include #include #include #include diff --git a/arch/mips/kernel/i8253.c b/arch/mips/kernel/i8253.c index 391221b6a6aa..82b5a9f037e7 100644 --- a/arch/mips/kernel/i8253.c +++ b/arch/mips/kernel/i8253.c @@ -3,6 +3,7 @@ * */ #include +#include #include #include #include @@ -12,7 +13,6 @@ #include #include -#include #include #include diff --git a/arch/mips/mti-malta/malta-time.c b/arch/mips/mti-malta/malta-time.c index 1620b83cd13e..f8ee945ee411 100644 --- a/arch/mips/mti-malta/malta-time.c +++ b/arch/mips/mti-malta/malta-time.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -31,7 +32,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/mips/sgi-ip22/ip22-time.c b/arch/mips/sgi-ip22/ip22-time.c index 1a94c9894188..607192449335 100644 --- a/arch/mips/sgi-ip22/ip22-time.c +++ b/arch/mips/sgi-ip22/ip22-time.c @@ -10,6 +10,7 @@ * Copyright (C) 2003, 06 Ralf Baechle (ralf@linux-mips.org) */ #include +#include #include #include #include @@ -20,7 +21,6 @@ #include #include -#include #include #include #include diff --git a/arch/mips/sni/time.c b/arch/mips/sni/time.c index 0904d4d30cb3..ec0be14996a4 100644 --- a/arch/mips/sni/time.c +++ b/arch/mips/sni/time.c @@ -1,11 +1,11 @@ #include +#include #include #include #include #include #include -#include #include #include #include diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index b961af86bfea..f3c37841bb33 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -39,7 +40,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index 965a7666c283..a30740e6890e 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -229,11 +229,11 @@ #include #include #include +#include #include #include #include -#include #include #include #include diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 6781765b3a0d..85b8a8a76c21 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -12,7 +13,6 @@ #include #include -#include #include #define HPET_MASK CLOCKSOURCE_MASK(32) diff --git a/arch/x86/kernel/i8253.c b/arch/x86/kernel/i8253.c index fb66dc9e36cb..323555050f89 100644 --- a/arch/x86/kernel/i8253.c +++ b/arch/x86/kernel/i8253.c @@ -9,10 +9,10 @@ #include #include #include +#include #include #include -#include #include #include diff --git a/arch/x86/kernel/time.c b/arch/x86/kernel/time.c index 00cbb272627f..5a64d057be57 100644 --- a/arch/x86/kernel/time.c +++ b/arch/x86/kernel/time.c @@ -11,13 +11,13 @@ #include #include +#include #include #include #include #include #include -#include #include #include #include diff --git a/drivers/block/hd.c b/drivers/block/hd.c index 007c630904c1..b52c9ca146fc 100644 --- a/drivers/block/hd.c +++ b/drivers/block/hd.c @@ -155,7 +155,7 @@ else \ #if (HD_DELAY > 0) -#include +#include unsigned long last_req; diff --git a/drivers/clocksource/i8253.c b/drivers/clocksource/i8253.c index 225c1761b372..33735ebd2dcc 100644 --- a/drivers/clocksource/i8253.c +++ b/drivers/clocksource/i8253.c @@ -7,7 +7,7 @@ #include #include -#include +#include /* * Since the PIT overflows every tick, its not very useful diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 5b8f59d6c3e8..c351aa421f8f 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -47,7 +47,7 @@ static void gameport_disconnect_port(struct gameport *gameport); #if defined(__i386__) -#include +#include #define DELTA(x,y) ((y)-(x)+((y)<(x)?1193182/HZ:0)) #define GET_TIME(x) do { x = get_time_pit(); } while (0) diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c index 4afe0a3b4884..9882971827e6 100644 --- a/drivers/input/joystick/analog.c +++ b/drivers/input/joystick/analog.c @@ -136,7 +136,7 @@ struct analog_port { #ifdef __i386__ -#include +#include #define GET_TIME(x) do { if (cpu_has_tsc) rdtscl(x); else x = get_time_pit(); } while (0) #define DELTA(x,y) (cpu_has_tsc ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? CLOCK_TICK_RATE / HZ : 0))) diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c index f080dd31499b..2aa9d9020179 100644 --- a/drivers/input/misc/pcspkr.c +++ b/drivers/input/misc/pcspkr.c @@ -27,7 +27,7 @@ MODULE_ALIAS("platform:pcspkr"); #if defined(CONFIG_MIPS) || defined(CONFIG_X86) /* Use the global PIT lock ! */ -#include +#include #else #include static DEFINE_RAW_SPINLOCK(i8253_lock); diff --git a/include/linux/i8253.h b/include/linux/i8253.h new file mode 100644 index 000000000000..d2cba88f75d6 --- /dev/null +++ b/include/linux/i8253.h @@ -0,0 +1,11 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#ifndef __LINUX_I8253_H +#define __LINUX_I8253_H + +#include + +#endif /* __LINUX_I8253_H */ diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h index 4ff6c8cc5077..6757c304ee46 100644 --- a/sound/drivers/pcsp/pcsp.h +++ b/sound/drivers/pcsp/pcsp.h @@ -13,7 +13,7 @@ #include #if defined(CONFIG_MIPS) || defined(CONFIG_X86) /* Use the global PIT lock ! */ -#include +#include #else #include static DEFINE_RAW_SPINLOCK(i8253_lock); -- cgit v1.2.3 From cb2455aa274b780802c593fecf115240a655d809 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 1 Jun 2011 19:04:58 +0100 Subject: i8253: Unify all kernel declarations of i8253_lock Signed-off-by: Ralf Baechle Cc: Russell King Cc: linux-arm-kernel@lists.infradead.org Cc: linux-mips@linux-mips.org Link: http://lkml.kernel.org/r/20110601180610.134151920@duck.linux-mips.net Signed-off-by: Thomas Gleixner --- arch/arm/include/asm/i8253.h | 9 --------- arch/mips/include/asm/i8253.h | 12 ------------ arch/x86/include/asm/i8253.h | 10 ---------- include/linux/i8253.h | 14 ++++++++++++++ 4 files changed, 14 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/arch/arm/include/asm/i8253.h b/arch/arm/include/asm/i8253.h index 70656b69d5ce..5b41b92ecf6a 100644 --- a/arch/arm/include/asm/i8253.h +++ b/arch/arm/include/asm/i8253.h @@ -1,15 +1,6 @@ #ifndef __ASMARM_I8253_H #define __ASMARM_I8253_H -/* i8253A PIT registers */ -#define PIT_MODE 0x43 -#define PIT_CH0 0x40 - #define PIT_LATCH ((PIT_TICK_RATE + HZ / 2) / HZ) -extern raw_spinlock_t i8253_lock; - -#define outb_pit outb_p -#define inb_pit inb_p - #endif diff --git a/arch/mips/include/asm/i8253.h b/arch/mips/include/asm/i8253.h index 9ad011366f73..32ec1f00c6b3 100644 --- a/arch/mips/include/asm/i8253.h +++ b/arch/mips/include/asm/i8253.h @@ -5,20 +5,8 @@ #ifndef __ASM_I8253_H #define __ASM_I8253_H -#include - -/* i8253A PIT registers */ -#define PIT_MODE 0x43 -#define PIT_CH0 0x40 -#define PIT_CH2 0x42 - #define PIT_LATCH LATCH -extern raw_spinlock_t i8253_lock; - extern void setup_pit_timer(void); -#define inb_pit inb_p -#define outb_pit outb_p - #endif /* __ASM_I8253_H */ diff --git a/arch/x86/include/asm/i8253.h b/arch/x86/include/asm/i8253.h index 65aaa91d5850..20480cef7547 100644 --- a/arch/x86/include/asm/i8253.h +++ b/arch/x86/include/asm/i8253.h @@ -1,20 +1,10 @@ #ifndef _ASM_X86_I8253_H #define _ASM_X86_I8253_H -/* i8253A PIT registers */ -#define PIT_MODE 0x43 -#define PIT_CH0 0x40 -#define PIT_CH2 0x42 - #define PIT_LATCH LATCH -extern raw_spinlock_t i8253_lock; - extern struct clock_event_device *global_clock_event; extern void setup_pit_timer(void); -#define inb_pit inb_p -#define outb_pit outb_p - #endif /* _ASM_X86_I8253_H */ diff --git a/include/linux/i8253.h b/include/linux/i8253.h index d2cba88f75d6..13048d335368 100644 --- a/include/linux/i8253.h +++ b/include/linux/i8253.h @@ -2,10 +2,24 @@ * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. + * + * Machine specific IO port address definition for generic. + * Written by Osamu Tomita */ #ifndef __LINUX_I8253_H #define __LINUX_I8253_H +#include #include +/* i8253A PIT registers */ +#define PIT_MODE 0x43 +#define PIT_CH0 0x40 +#define PIT_CH2 0x42 + +#define inb_pit inb_p +#define outb_pit outb_p + +extern raw_spinlock_t i8253_lock; + #endif /* __LINUX_I8253_H */ -- cgit v1.2.3 From 49cf3f29a1ef2e59f1be823e376c1fbac586eb8d Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 1 Jun 2011 19:05:07 +0100 Subject: i8253: Consolidate definitions of PIT_LATCH x86 defines PIT_LATCH as LATCH which in is defined as ((CLOCK_TICK_RATE + HZ/2) / HZ) and again defines CLOCK_TICK_RATE as PIT_TICK_RATE. MIPS defines PIT_LATCH as LATCH which in is defined as ((CLOCK_TICK_RATE + HZ/2) / HZ) and again defines CLOCK_TICK_RATE as 1193182. ARM defines PITCH_LATCH as ((PIT_TICK_RATE + HZ / 2) / HZ) - and that's the sanest thing and equivalent to above definitions so use that as the new definition in . Signed-off-by: Ralf Baechle Cc: Russell King Cc: linux-arm-kernel@lists.infradead.org Cc: linux-mips@linux-mips.org Link: http://lkml.kernel.org/r/20110601180610.832810002@duck.linux-mips.net Signed-off-by: Thomas Gleixner --- arch/arm/include/asm/i8253.h | 2 -- arch/mips/include/asm/i8253.h | 2 -- arch/x86/include/asm/i8253.h | 2 -- include/linux/i8253.h | 4 ++++ 4 files changed, 4 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/arm/include/asm/i8253.h b/arch/arm/include/asm/i8253.h index 5b41b92ecf6a..fee59eb4581d 100644 --- a/arch/arm/include/asm/i8253.h +++ b/arch/arm/include/asm/i8253.h @@ -1,6 +1,4 @@ #ifndef __ASMARM_I8253_H #define __ASMARM_I8253_H -#define PIT_LATCH ((PIT_TICK_RATE + HZ / 2) / HZ) - #endif diff --git a/arch/mips/include/asm/i8253.h b/arch/mips/include/asm/i8253.h index 32ec1f00c6b3..26f4eb197c3a 100644 --- a/arch/mips/include/asm/i8253.h +++ b/arch/mips/include/asm/i8253.h @@ -5,8 +5,6 @@ #ifndef __ASM_I8253_H #define __ASM_I8253_H -#define PIT_LATCH LATCH - extern void setup_pit_timer(void); #endif /* __ASM_I8253_H */ diff --git a/arch/x86/include/asm/i8253.h b/arch/x86/include/asm/i8253.h index 3d5f5eea5da4..0049dc9fbfee 100644 --- a/arch/x86/include/asm/i8253.h +++ b/arch/x86/include/asm/i8253.h @@ -1,8 +1,6 @@ #ifndef _ASM_X86_I8253_H #define _ASM_X86_I8253_H -#define PIT_LATCH LATCH - extern void setup_pit_timer(void); #endif /* _ASM_X86_I8253_H */ diff --git a/include/linux/i8253.h b/include/linux/i8253.h index 13048d335368..1ec6323a1507 100644 --- a/include/linux/i8253.h +++ b/include/linux/i8253.h @@ -9,7 +9,9 @@ #ifndef __LINUX_I8253_H #define __LINUX_I8253_H +#include #include +#include #include /* i8253A PIT registers */ @@ -17,6 +19,8 @@ #define PIT_CH0 0x40 #define PIT_CH2 0x42 +#define PIT_LATCH ((PIT_TICK_RATE + HZ/2) / HZ) + #define inb_pit inb_p #define outb_pit outb_p -- cgit v1.2.3 From 850492760cfd38d14d64a5fc6d636091464f755e Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 1 Jun 2011 19:05:08 +0100 Subject: i8253: Move remaining content and delete asm/i8253.h Move setup_pit_timer() declaration to the common header file and remove the arch specific ones. [ tglx: Move it to linux/i8253.h instead of asm/mips and asm/x86 ] Signed-off-by: Ralf Baechle Cc: linux-arm-kernel@lists.infradead.org Cc: Russell King Cc: linux-mips@linux-mips.org Cc: Sergei Shtylyov --- arch/arm/include/asm/i8253.h | 4 ---- arch/mips/include/asm/i8253.h | 10 ---------- arch/x86/include/asm/i8253.h | 6 ------ include/linux/i8253.h | 3 ++- 4 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 arch/arm/include/asm/i8253.h delete mode 100644 arch/mips/include/asm/i8253.h delete mode 100644 arch/x86/include/asm/i8253.h (limited to 'include') diff --git a/arch/arm/include/asm/i8253.h b/arch/arm/include/asm/i8253.h deleted file mode 100644 index fee59eb4581d..000000000000 --- a/arch/arm/include/asm/i8253.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __ASMARM_I8253_H -#define __ASMARM_I8253_H - -#endif diff --git a/arch/mips/include/asm/i8253.h b/arch/mips/include/asm/i8253.h deleted file mode 100644 index 26f4eb197c3a..000000000000 --- a/arch/mips/include/asm/i8253.h +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Machine specific IO port address definition for generic. - * Written by Osamu Tomita - */ -#ifndef __ASM_I8253_H -#define __ASM_I8253_H - -extern void setup_pit_timer(void); - -#endif /* __ASM_I8253_H */ diff --git a/arch/x86/include/asm/i8253.h b/arch/x86/include/asm/i8253.h deleted file mode 100644 index 0049dc9fbfee..000000000000 --- a/arch/x86/include/asm/i8253.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_X86_I8253_H -#define _ASM_X86_I8253_H - -extern void setup_pit_timer(void); - -#endif /* _ASM_X86_I8253_H */ diff --git a/include/linux/i8253.h b/include/linux/i8253.h index 1ec6323a1507..76039c86ab58 100644 --- a/include/linux/i8253.h +++ b/include/linux/i8253.h @@ -12,7 +12,6 @@ #include #include #include -#include /* i8253A PIT registers */ #define PIT_MODE 0x43 @@ -26,4 +25,6 @@ extern raw_spinlock_t i8253_lock; +extern void setup_pit_timer(void); + #endif /* __LINUX_I8253_H */ -- cgit v1.2.3 From 3801b86aa482d26a8ae460f67fca29e016491a86 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 9 Jun 2011 16:22:22 +0100 Subject: regulator: Refactor supply implementation to work as regular consumers Currently the regulator supply implementation is somewhat complex and fragile as it doesn't look like standard consumers but is instead a parallel implementation. This causes issues with locking and reference counting. Move the implementation over to using standard consumers to address this. Rather than only notifying the supply on the first enable/disable we do so every time the regulator is enabled or disabled, simplifying locking as we don't need to hold a lock on the consumer we are about to enable. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/core.c | 101 ++++++++++++++------------------------- include/linux/regulator/driver.h | 4 +- 2 files changed, 37 insertions(+), 68 deletions(-) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index e3b67ee48b23..f0cc3983ffee 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -82,8 +82,7 @@ struct regulator { }; static int _regulator_is_enabled(struct regulator_dev *rdev); -static int _regulator_disable(struct regulator_dev *rdev, - struct regulator_dev **supply_rdev_ptr); +static int _regulator_disable(struct regulator_dev *rdev); static int _regulator_get_voltage(struct regulator_dev *rdev); static int _regulator_get_current_limit(struct regulator_dev *rdev); static unsigned int _regulator_get_mode(struct regulator_dev *rdev); @@ -91,6 +90,9 @@ static void _notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data); static int _regulator_do_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV); +static struct regulator *create_regulator(struct regulator_dev *rdev, + struct device *dev, + const char *supply_name); static const char *rdev_get_name(struct regulator_dev *rdev) { @@ -930,21 +932,20 @@ out: * core if it's child is enabled. */ static int set_supply(struct regulator_dev *rdev, - struct regulator_dev *supply_rdev) + struct regulator_dev *supply_rdev) { int err; - err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj, - "supply"); - if (err) { - rdev_err(rdev, "could not add device link %s err %d\n", - supply_rdev->dev.kobj.name, err); - goto out; + rdev_info(rdev, "supplied by %s\n", rdev_get_name(supply_rdev)); + + rdev->supply = create_regulator(supply_rdev, &rdev->dev, "SUPPLY"); + if (IS_ERR(rdev->supply)) { + err = PTR_ERR(rdev->supply); + rdev->supply = NULL; + return err; } - rdev->supply = supply_rdev; - list_add(&rdev->slist, &supply_rdev->supply_list); -out: - return err; + + return 0; } /** @@ -1303,19 +1304,6 @@ static int _regulator_enable(struct regulator_dev *rdev) { int ret, delay; - if (rdev->use_count == 0) { - /* do we need to enable the supply regulator first */ - if (rdev->supply) { - mutex_lock(&rdev->supply->mutex); - ret = _regulator_enable(rdev->supply); - mutex_unlock(&rdev->supply->mutex); - if (ret < 0) { - rdev_err(rdev, "failed to enable: %d\n", ret); - return ret; - } - } - } - /* check voltage and requested load before enabling */ if (rdev->constraints && (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) @@ -1390,19 +1378,27 @@ int regulator_enable(struct regulator *regulator) struct regulator_dev *rdev = regulator->rdev; int ret = 0; + if (rdev->supply) { + ret = regulator_enable(rdev->supply); + if (ret != 0) + return ret; + } + mutex_lock(&rdev->mutex); ret = _regulator_enable(rdev); mutex_unlock(&rdev->mutex); + + if (ret != 0) + regulator_disable(rdev->supply); + return ret; } EXPORT_SYMBOL_GPL(regulator_enable); /* locks held by regulator_disable() */ -static int _regulator_disable(struct regulator_dev *rdev, - struct regulator_dev **supply_rdev_ptr) +static int _regulator_disable(struct regulator_dev *rdev) { int ret = 0; - *supply_rdev_ptr = NULL; if (WARN(rdev->use_count <= 0, "unbalanced disables for %s\n", rdev_get_name(rdev))) @@ -1429,9 +1425,6 @@ static int _regulator_disable(struct regulator_dev *rdev, NULL); } - /* decrease our supplies ref count and disable if required */ - *supply_rdev_ptr = rdev->supply; - rdev->use_count = 0; } else if (rdev->use_count > 1) { @@ -1442,6 +1435,7 @@ static int _regulator_disable(struct regulator_dev *rdev, rdev->use_count--; } + return ret; } @@ -1460,29 +1454,21 @@ static int _regulator_disable(struct regulator_dev *rdev, int regulator_disable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; - struct regulator_dev *supply_rdev = NULL; int ret = 0; mutex_lock(&rdev->mutex); - ret = _regulator_disable(rdev, &supply_rdev); + ret = _regulator_disable(rdev); mutex_unlock(&rdev->mutex); - /* decrease our supplies ref count and disable if required */ - while (supply_rdev != NULL) { - rdev = supply_rdev; - - mutex_lock(&rdev->mutex); - _regulator_disable(rdev, &supply_rdev); - mutex_unlock(&rdev->mutex); - } + if (ret == 0 && rdev->supply) + regulator_disable(rdev->supply); return ret; } EXPORT_SYMBOL_GPL(regulator_disable); /* locks held by regulator_force_disable() */ -static int _regulator_force_disable(struct regulator_dev *rdev, - struct regulator_dev **supply_rdev_ptr) +static int _regulator_force_disable(struct regulator_dev *rdev) { int ret = 0; @@ -1499,10 +1485,6 @@ static int _regulator_force_disable(struct regulator_dev *rdev, REGULATOR_EVENT_DISABLE, NULL); } - /* decrease our supplies ref count and disable if required */ - *supply_rdev_ptr = rdev->supply; - - rdev->use_count = 0; return ret; } @@ -1518,16 +1500,16 @@ static int _regulator_force_disable(struct regulator_dev *rdev, int regulator_force_disable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; - struct regulator_dev *supply_rdev = NULL; int ret; mutex_lock(&rdev->mutex); regulator->uA_load = 0; - ret = _regulator_force_disable(rdev, &supply_rdev); + ret = _regulator_force_disable(regulator->rdev); mutex_unlock(&rdev->mutex); - if (supply_rdev) - regulator_disable(get_device_regulator(rdev_get_dev(supply_rdev))); + if (rdev->supply) + while (rdev->open_count--) + regulator_disable(rdev->supply); return ret; } @@ -2138,7 +2120,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) /* get input voltage */ input_uV = 0; if (rdev->supply) - input_uV = _regulator_get_voltage(rdev->supply); + input_uV = regulator_get_voltage(rdev->supply); if (input_uV <= 0) input_uV = rdev->constraints->input_uV; if (input_uV <= 0) { @@ -2208,17 +2190,8 @@ EXPORT_SYMBOL_GPL(regulator_unregister_notifier); static void _notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data) { - struct regulator_dev *_rdev; - /* call rdev chain first */ blocking_notifier_call_chain(&rdev->notifier, event, NULL); - - /* now notify regulator we supply */ - list_for_each_entry(_rdev, &rdev->supply_list, slist) { - mutex_lock(&_rdev->mutex); - _notifier_call_chain(_rdev, event, data); - mutex_unlock(&_rdev->mutex); - } } /** @@ -2610,9 +2583,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, rdev->owner = regulator_desc->owner; rdev->desc = regulator_desc; INIT_LIST_HEAD(&rdev->consumer_list); - INIT_LIST_HEAD(&rdev->supply_list); INIT_LIST_HEAD(&rdev->list); - INIT_LIST_HEAD(&rdev->slist); BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier); /* preform any regulator specific init */ @@ -2724,7 +2695,7 @@ void regulator_unregister(struct regulator_dev *rdev) unset_regulator_supplies(rdev); list_del(&rdev->list); if (rdev->supply) - sysfs_remove_link(&rdev->dev.kobj, "supply"); + regulator_put(rdev->supply); device_unregister(&rdev->dev); kfree(rdev->constraints); mutex_unlock(®ulator_list_mutex); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 6c433b89c80d..1a80bc77517d 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -188,18 +188,16 @@ struct regulator_dev { /* lists we belong to */ struct list_head list; /* list of all regulators */ - struct list_head slist; /* list of supplied regulators */ /* lists we own */ struct list_head consumer_list; /* consumers we supply */ - struct list_head supply_list; /* regulators we supply */ struct blocking_notifier_head notifier; struct mutex mutex; /* consumer lock */ struct module *owner; struct device dev; struct regulation_constraints *constraints; - struct regulator_dev *supply; /* for tree */ + struct regulator *supply; /* for tree */ void *reg_data; /* regulator_dev data */ -- cgit v1.2.3 From 5a9d0a3ffbc40ea1f5a0636501e7599cbb327bcc Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Tue, 7 Jun 2011 11:18:06 +0200 Subject: Bluetooth: Clean up some code style issues Fix lines longer than 80 chars in length. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/bluetooth.h | 7 ++++--- include/net/bluetooth/hci_core.h | 38 +++++++++++++++++++++++++------------- include/net/bluetooth/rfcomm.h | 9 ++++++--- 3 files changed, 35 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index af930a3a66be..7bccaf921cab 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -137,7 +137,8 @@ int bt_sock_register(int proto, const struct net_proto_family *ops); int bt_sock_unregister(int proto); void bt_sock_link(struct bt_sock_list *l, struct sock *s); void bt_sock_unlink(struct bt_sock_list *l, struct sock *s); -int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); +int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags); int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); uint bt_sock_poll(struct file * file, struct socket *sock, poll_table *wait); @@ -172,8 +173,8 @@ static inline struct sk_buff *bt_skb_alloc(unsigned int len, gfp_t how) return skb; } -static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk, unsigned long len, - int nb, int *err) +static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk, + unsigned long len, int nb, int *err) { struct sk_buff *skb; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 818eadbc3f7f..836d3e8c4bf1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -224,7 +224,7 @@ struct hci_conn { spinlock_t lock; bdaddr_t dst; - __u8 dst_type; + __u8 dst_type; __u16 handle; __u16 state; __u8 mode; @@ -317,7 +317,8 @@ static inline long inquiry_entry_age(struct inquiry_entry *e) return jiffies - e->timestamp; } -struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); +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); /* ----- HCI Connections ----- */ @@ -431,7 +432,8 @@ int hci_conn_del(struct hci_conn *conn); void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev); -struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type); +struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, + __u8 sec_level, __u8 auth_type); int hci_conn_check_link_mode(struct hci_conn *conn); int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level); int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type); @@ -460,10 +462,12 @@ static inline void hci_conn_put(struct hci_conn *conn) timeo = msecs_to_jiffies(conn->disc_timeout); if (!conn->out) timeo *= 2; - } else + } else { timeo = msecs_to_jiffies(10); - } else + } + } else { timeo = msecs_to_jiffies(10); + } mod_timer(&conn->disc_timer, jiffies + timeo); } } @@ -578,16 +582,20 @@ struct hci_proto { void *priv; - int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type); + int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, + __u8 type); int (*connect_cfm) (struct hci_conn *conn, __u8 status); int (*disconn_ind) (struct hci_conn *conn); int (*disconn_cfm) (struct hci_conn *conn, __u8 reason); - int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, __u16 flags); + int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, + __u16 flags); int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb); - int (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); + int (*security_cfm) (struct hci_conn *conn, __u8 status, + __u8 encrypt); }; -static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type) +static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, + __u8 type) { register struct hci_proto *hp; int mask = 0; @@ -673,7 +681,8 @@ static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) conn->security_cfm_cb(conn, status); } -static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt) +static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, + __u8 encrypt) { register struct hci_proto *hp; @@ -698,7 +707,8 @@ struct hci_cb { char *name; - void (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); + void (*security_cfm) (struct hci_conn *conn, __u8 status, + __u8 encrypt); void (*key_change_cfm) (struct hci_conn *conn, __u8 status); void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role); }; @@ -724,7 +734,8 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) read_unlock_bh(&hci_cb_list_lock); } -static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt) +static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, + __u8 encrypt) { struct list_head *p; @@ -755,7 +766,8 @@ static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status) read_unlock_bh(&hci_cb_list_lock); } -static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, __u8 role) +static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, + __u8 role) { struct list_head *p; diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 6eac4a760c3b..d5eee2093b1e 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -234,7 +234,8 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, /* ---- RFCOMM DLCs (channels) ---- */ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio); void rfcomm_dlc_free(struct rfcomm_dlc *d); -int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel); +int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, + u8 channel); int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason); int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb); int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig); @@ -271,7 +272,8 @@ static inline void rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) } /* ---- RFCOMM sessions ---- */ -void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst); +void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, + bdaddr_t *dst); static inline void rfcomm_session_hold(struct rfcomm_session *s) { @@ -312,7 +314,8 @@ struct rfcomm_pinfo { int rfcomm_init_sockets(void); void rfcomm_cleanup_sockets(void); -int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d); +int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, + struct rfcomm_dlc **d); /* ---- RFCOMM TTY ---- */ #define RFCOMM_MAX_DEV 256 -- cgit v1.2.3 From b4c8cc88c18213688268d1d53a51d97ce2f19a64 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Thu, 9 Jun 2011 15:05:48 -0700 Subject: ethtool.h: fix typos Signed-off-by: Yegor Yefremov Signed-off-by: David S. Miller --- include/linux/ethtool.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index c6a850ab2ec5..439b173c5882 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -268,7 +268,7 @@ struct ethtool_pauseparam { __u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */ /* If the link is being auto-negotiated (via ethtool_cmd.autoneg - * being true) the user may set 'autonet' here non-zero to have the + * being true) the user may set 'autoneg' here non-zero to have the * pause parameters be auto-negotiated too. In such a case, the * {rx,tx}_pause values below determine what capabilities are * advertised. @@ -811,7 +811,7 @@ bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported); * @get_tx_csum: Deprecated as redundant. Report whether transmit checksums * are turned on or off. * @set_tx_csum: Deprecated in favour of generic netdev features. Turn - * transmit checksums on or off. Returns a egative error code or zero. + * transmit checksums on or off. Returns a negative error code or zero. * @get_sg: Deprecated as redundant. Report whether scatter-gather is * enabled. * @set_sg: Deprecated in favour of generic netdev features. Turn @@ -1087,7 +1087,7 @@ struct ethtool_ops { /* The following are all involved in forcing a particular link * mode for the device for setting things. When getting the * devices settings, these indicate the current mode and whether - * it was foced up into this mode or autonegotiated. + * it was forced up into this mode or autonegotiated. */ /* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb, 10GbE. */ -- cgit v1.2.3 From c7ac8679bec9397afe8918f788cbcef88c38da54 Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Fri, 10 Jun 2011 01:27:09 +0000 Subject: rtnetlink: Compute and store minimum ifinfo dump size The message size allocated for rtnl ifinfo dumps was limited to a single page. This is not enough for additional interface info available with devices that support SR-IOV and caused a bug in which VF info would not be displayed if more than approximately 40 VFs were created per interface. Implement a new function pointer for the rtnl_register service that will calculate the amount of data required for the ifinfo dump and allocate enough data to satisfy the request. Signed-off-by: Greg Rose Signed-off-by: Jeff Kirsher --- drivers/infiniband/core/netlink.c | 2 +- include/linux/netlink.h | 6 ++-- include/net/rtnetlink.h | 7 +++-- net/bridge/br_netlink.c | 15 ++++++--- net/core/fib_rules.c | 6 ++-- net/core/neighbour.c | 11 ++++--- net/core/rtnetlink.c | 60 +++++++++++++++++++++++++++++------- net/dcb/dcbnl.c | 4 +-- net/decnet/dn_dev.c | 6 ++-- net/decnet/dn_fib.c | 4 +-- net/decnet/dn_route.c | 5 +-- net/ipv4/devinet.c | 6 ++-- net/ipv4/fib_frontend.c | 6 ++-- net/ipv4/inet_diag.c | 2 +- net/ipv4/ipmr.c | 3 +- net/ipv4/route.c | 2 +- net/ipv6/addrconf.c | 16 ++++++---- net/ipv6/addrlabel.c | 9 ++++-- net/ipv6/ip6_fib.c | 3 +- net/ipv6/ip6mr.c | 3 +- net/ipv6/route.c | 6 ++-- net/netfilter/ipset/ip_set_core.c | 2 +- net/netfilter/nf_conntrack_netlink.c | 4 +-- net/netlink/af_netlink.c | 17 ++++++---- net/netlink/genetlink.c | 2 +- net/phonet/pn_netlink.c | 13 ++++---- net/sched/act_api.c | 7 +++-- net/sched/cls_api.c | 6 ++-- net/sched/sch_api.c | 12 ++++---- net/xfrm/xfrm_user.c | 3 +- 30 files changed, 158 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c index 4a5abaf0a25c..9227f4acd79c 100644 --- a/drivers/infiniband/core/netlink.c +++ b/drivers/infiniband/core/netlink.c @@ -148,7 +148,7 @@ static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) return -EINVAL; return netlink_dump_start(nls, skb, nlh, client->cb_table[op].dump, - NULL); + NULL, 0); } } diff --git a/include/linux/netlink.h b/include/linux/netlink.h index a9dd89552f9c..fdd0188a167e 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -221,7 +221,8 @@ struct netlink_callback { int (*dump)(struct sk_buff * skb, struct netlink_callback *cb); int (*done)(struct netlink_callback *cb); - int family; + u16 family; + u16 min_dump_alloc; long args[6]; }; @@ -259,7 +260,8 @@ __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags) 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*)); + int (*done)(struct netlink_callback*), + u16 min_dump_alloc); #define NL_NONROOT_RECV 0x1 diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 4093ca78cf60..678f1ffaf843 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -6,11 +6,14 @@ typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *, void *); typedef int (*rtnl_dumpit_func)(struct sk_buff *, struct netlink_callback *); +typedef u16 (*rtnl_calcit_func)(struct sk_buff *); extern int __rtnl_register(int protocol, int msgtype, - rtnl_doit_func, rtnl_dumpit_func); + rtnl_doit_func, rtnl_dumpit_func, + rtnl_calcit_func); extern void rtnl_register(int protocol, int msgtype, - rtnl_doit_func, rtnl_dumpit_func); + rtnl_doit_func, rtnl_dumpit_func, + rtnl_calcit_func); extern int rtnl_unregister(int protocol, int msgtype); extern void rtnl_unregister_all(int protocol); diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index ffb0dc4cc0e8..6814083a92f4 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -218,19 +218,24 @@ int __init br_netlink_init(void) if (err < 0) goto err1; - err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo); + err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, + br_dump_ifinfo, NULL); if (err) goto err2; - err = __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL); + err = __rtnl_register(PF_BRIDGE, RTM_SETLINK, + br_rtm_setlink, NULL, NULL); if (err) goto err3; - err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, br_fdb_add, NULL); + err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, + br_fdb_add, NULL, NULL); if (err) goto err3; - err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL); + err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, + br_fdb_delete, NULL, NULL); if (err) goto err3; - err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump); + err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, + NULL, br_fdb_dump, NULL); if (err) goto err3; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 008dc70b064b..e7ab0c0285b5 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -740,9 +740,9 @@ static struct pernet_operations fib_rules_net_ops = { static int __init fib_rules_init(void) { int err; - rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL); - rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL); - rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule); + rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule, NULL); err = register_pernet_subsys(&fib_rules_net_ops); if (err < 0) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 799f06e03a22..ceb505b1507c 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2909,12 +2909,13 @@ EXPORT_SYMBOL(neigh_sysctl_unregister); static int __init neigh_init(void) { - rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL); - rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL); - rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info); + rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info, NULL); - rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info); - rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL); + rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info, + NULL); + rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, NULL); return 0; } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index abd936d8a716..a798fc6f2aa1 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -56,9 +56,11 @@ struct rtnl_link { rtnl_doit_func doit; rtnl_dumpit_func dumpit; + rtnl_calcit_func calcit; }; static DEFINE_MUTEX(rtnl_mutex); +static u16 min_ifinfo_dump_size; void rtnl_lock(void) { @@ -144,12 +146,28 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) return tab ? tab[msgindex].dumpit : NULL; } +static rtnl_calcit_func rtnl_get_calcit(int protocol, int msgindex) +{ + struct rtnl_link *tab; + + if (protocol <= RTNL_FAMILY_MAX) + tab = rtnl_msg_handlers[protocol]; + else + tab = NULL; + + if (tab == NULL || tab[msgindex].calcit == NULL) + tab = rtnl_msg_handlers[PF_UNSPEC]; + + return tab ? tab[msgindex].calcit : NULL; +} + /** * __rtnl_register - Register a rtnetlink message type * @protocol: Protocol family or PF_UNSPEC * @msgtype: rtnetlink message type * @doit: Function pointer called for each request message * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message + * @calcit: Function pointer to calc size of dump message * * Registers the specified function pointers (at least one of them has * to be non-NULL) to be called whenever a request message for the @@ -162,7 +180,8 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) * Returns 0 on success or a negative error code. */ int __rtnl_register(int protocol, int msgtype, - rtnl_doit_func doit, rtnl_dumpit_func dumpit) + rtnl_doit_func doit, rtnl_dumpit_func dumpit, + rtnl_calcit_func calcit) { struct rtnl_link *tab; int msgindex; @@ -185,6 +204,9 @@ int __rtnl_register(int protocol, int msgtype, if (dumpit) tab[msgindex].dumpit = dumpit; + if (calcit) + tab[msgindex].calcit = calcit; + return 0; } EXPORT_SYMBOL_GPL(__rtnl_register); @@ -199,9 +221,10 @@ EXPORT_SYMBOL_GPL(__rtnl_register); * of memory implies no sense in continuing. */ void rtnl_register(int protocol, int msgtype, - rtnl_doit_func doit, rtnl_dumpit_func dumpit) + rtnl_doit_func doit, rtnl_dumpit_func dumpit, + rtnl_calcit_func calcit) { - if (__rtnl_register(protocol, msgtype, doit, dumpit) < 0) + if (__rtnl_register(protocol, msgtype, doit, dumpit, calcit) < 0) panic("Unable to register rtnetlink message handler, " "protocol = %d, message type = %d\n", protocol, msgtype); @@ -1818,6 +1841,11 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) return err; } +static u16 rtnl_calcit(struct sk_buff *skb) +{ + return min_ifinfo_dump_size; +} + static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) { int idx; @@ -1847,11 +1875,14 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change) struct net *net = dev_net(dev); struct sk_buff *skb; int err = -ENOBUFS; + size_t if_info_size; - skb = nlmsg_new(if_nlmsg_size(dev), GFP_KERNEL); + skb = nlmsg_new((if_info_size = if_nlmsg_size(dev)), GFP_KERNEL); if (skb == NULL) goto errout; + min_ifinfo_dump_size = max_t(u16, if_info_size, min_ifinfo_dump_size); + err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_size() */ @@ -1902,14 +1933,20 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { struct sock *rtnl; rtnl_dumpit_func dumpit; + rtnl_calcit_func calcit; + u16 min_dump_alloc = 0; dumpit = rtnl_get_dumpit(family, type); if (dumpit == NULL) return -EOPNOTSUPP; + calcit = rtnl_get_calcit(family, type); + if (calcit) + min_dump_alloc = calcit(skb); __rtnl_unlock(); rtnl = net->rtnl; - err = netlink_dump_start(rtnl, skb, nlh, dumpit, NULL); + err = netlink_dump_start(rtnl, skb, nlh, dumpit, + NULL, min_dump_alloc); rtnl_lock(); return err; } @@ -2019,12 +2056,13 @@ void __init rtnetlink_init(void) netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV); register_netdevice_notifier(&rtnetlink_dev_notifier); - rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo); - rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL); - rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL); - rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL); + rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, + rtnl_dump_ifinfo, rtnl_calcit); + rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all); - rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all); + rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, NULL); + rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, NULL); } diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 3609eacaf4ce..ed1bb8c65a9e 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1819,8 +1819,8 @@ static int __init dcbnl_init(void) { INIT_LIST_HEAD(&dcb_app_list); - rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL); - rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL); + rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL, NULL); return 0; } diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index cf26ac74a188..3780fd6e7bfe 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -1414,9 +1414,9 @@ void __init dn_dev_init(void) dn_dev_devices_on(); - rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL); - rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL); - rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr); + rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL, NULL); + rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL, NULL); + rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr, NULL); proc_net_fops_create(&init_net, "decnet_dev", S_IRUGO, &dn_dev_seq_fops); diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index 1c74ed36ce8f..104324d6d535 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -763,8 +763,8 @@ void __init dn_fib_init(void) register_dnaddr_notifier(&dn_fib_dnaddr_notifier); - rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL); - rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL); + rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL, NULL); + rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL, NULL); } diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 74544bc6fdec..2949ca474ede 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1841,10 +1841,11 @@ void __init dn_route_init(void) proc_net_fops_create(&init_net, "decnet_cache", S_IRUGO, &dn_rt_cache_seq_fops); #ifdef CONFIG_DECNET_ROUTER - rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, dn_fib_dump); + rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, + dn_fib_dump, NULL); #else rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, - dn_cache_dump); + dn_cache_dump, NULL); #endif } diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 0d4a184af16f..37b3c188d8b3 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1833,8 +1833,8 @@ void __init devinet_init(void) rtnl_af_register(&inet_af_ops); - rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL); - rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL); - rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr); + rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL); + rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL); + rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL); } diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 22524716fe70..92fc5f69f5da 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1124,9 +1124,9 @@ static struct pernet_operations fib_net_ops = { void __init ip_fib_init(void) { - rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL); - rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL); - rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib); + rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL); + rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL); + rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL); register_pernet_subsys(&fib_net_ops); register_netdevice_notifier(&fib_netdev_notifier); diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 6ffe94ca5bc9..5ff47656fced 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -871,7 +871,7 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) } return netlink_dump_start(idiagnl, skb, nlh, - inet_diag_dump, NULL); + inet_diag_dump, NULL, 0); } return inet_diag_get_exact(skb, nlh); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 30a7763c400e..aae2bd8cd924 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -2544,7 +2544,8 @@ int __init ip_mr_init(void) goto add_proto_fail; } #endif - rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, NULL, ipmr_rtm_dumproute); + rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, + NULL, ipmr_rtm_dumproute, NULL); return 0; #ifdef CONFIG_IP_PIMSM_V2 diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 52b0b956508b..aa29c6291353 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -3295,7 +3295,7 @@ int __init ip_rt_init(void) xfrm_init(); xfrm4_init(ip_rt_max_size); #endif - rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL); + rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL, NULL); #ifdef CONFIG_SYSCTL register_pernet_subsys(&sysctl_route_ops); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 3e369425df58..05838c7fcf64 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -4727,16 +4727,20 @@ int __init addrconf_init(void) if (err < 0) goto errout_af; - err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo); + err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo, + NULL); if (err < 0) goto errout; /* Only the first call to __rtnl_register can fail */ - __rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL); - __rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL); - __rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr, inet6_dump_ifaddr); - __rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL, inet6_dump_ifmcaddr); - __rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, inet6_dump_ifacaddr); + __rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL, NULL); + __rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL, NULL); + __rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr, + inet6_dump_ifaddr, NULL); + __rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL, + inet6_dump_ifmcaddr, NULL); + __rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, + inet6_dump_ifacaddr, NULL); ipv6_addr_label_rtnl_register(); diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index c8993e5a337c..2d8ddba9ee58 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -592,8 +592,11 @@ out: void __init ipv6_addr_label_rtnl_register(void) { - __rtnl_register(PF_INET6, RTM_NEWADDRLABEL, ip6addrlbl_newdel, NULL); - __rtnl_register(PF_INET6, RTM_DELADDRLABEL, ip6addrlbl_newdel, NULL); - __rtnl_register(PF_INET6, RTM_GETADDRLABEL, ip6addrlbl_get, ip6addrlbl_dump); + __rtnl_register(PF_INET6, RTM_NEWADDRLABEL, ip6addrlbl_newdel, + NULL, NULL); + __rtnl_register(PF_INET6, RTM_DELADDRLABEL, ip6addrlbl_newdel, + NULL, NULL); + __rtnl_register(PF_INET6, RTM_GETADDRLABEL, ip6addrlbl_get, + ip6addrlbl_dump, NULL); } diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 4076a0b14b20..3030bdfd3ca4 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1586,7 +1586,8 @@ int __init fib6_init(void) if (ret) goto out_kmem_cache_create; - ret = __rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib); + ret = __rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib, + NULL); if (ret) goto out_unregister_subsys; out: diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 82a809901f8e..705c82886281 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1354,7 +1354,8 @@ int __init ip6_mr_init(void) goto add_proto_fail; } #endif - rtnl_register(RTNL_FAMILY_IP6MR, RTM_GETROUTE, NULL, ip6mr_rtm_dumproute); + rtnl_register(RTNL_FAMILY_IP6MR, RTM_GETROUTE, NULL, + ip6mr_rtm_dumproute, NULL); return 0; #ifdef CONFIG_IPV6_PIMSM_V2 add_proto_fail: diff --git a/net/ipv6/route.c b/net/ipv6/route.c index de2b1decd786..216ff31a0cc9 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2925,9 +2925,9 @@ int __init ip6_route_init(void) goto xfrm6_init; ret = -ENOBUFS; - if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL) || - __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL) || - __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL)) + if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || + __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || + __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) goto fib6_rules_init; ret = register_netdevice_notifier(&ip6_route_dev_notifier); diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 8041befc6555..333b0bedf298 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1120,7 +1120,7 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb, return netlink_dump_start(ctnl, skb, nlh, ip_set_dump_start, - ip_set_dump_done); + ip_set_dump_done, 0); } /* Add, del and test */ diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 482e90c61850..7dec88a1755b 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -970,7 +970,7 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, if (nlh->nlmsg_flags & NLM_F_DUMP) return netlink_dump_start(ctnl, skb, nlh, ctnetlink_dump_table, - ctnetlink_done); + ctnetlink_done, 0); err = ctnetlink_parse_zone(cda[CTA_ZONE], &zone); if (err < 0) @@ -1840,7 +1840,7 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, if (nlh->nlmsg_flags & NLM_F_DUMP) { return netlink_dump_start(ctnl, skb, nlh, ctnetlink_exp_dump_table, - ctnetlink_exp_done); + ctnetlink_exp_done, 0); } err = ctnetlink_parse_zone(cda[CTA_EXPECT_ZONE], &zone); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 6ef64adf7362..0b92f75491b1 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1659,13 +1659,10 @@ static int netlink_dump(struct sock *sk) { struct netlink_sock *nlk = nlk_sk(sk); struct netlink_callback *cb; - struct sk_buff *skb; + struct sk_buff *skb = NULL; struct nlmsghdr *nlh; int len, err = -ENOBUFS; - - skb = sock_rmalloc(sk, NLMSG_GOODSIZE, 0, GFP_KERNEL); - if (!skb) - goto errout; + int alloc_size; mutex_lock(nlk->cb_mutex); @@ -1675,6 +1672,12 @@ static int netlink_dump(struct sock *sk) goto errout_skb; } + alloc_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE); + + skb = sock_rmalloc(sk, alloc_size, 0, GFP_KERNEL); + if (!skb) + goto errout; + len = cb->dump(skb, cb); if (len > 0) { @@ -1721,7 +1724,8 @@ 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 *)) + int (*done)(struct netlink_callback *), + u16 min_dump_alloc) { struct netlink_callback *cb; struct sock *sk; @@ -1735,6 +1739,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, cb->dump = dump; cb->done = done; cb->nlh = nlh; + cb->min_dump_alloc = min_dump_alloc; atomic_inc(&skb->users); cb->skb = skb; diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 1781d99145e2..482fa571b4ee 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -525,7 +525,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) genl_unlock(); err = netlink_dump_start(net->genl_sock, skb, nlh, - ops->dumpit, ops->done); + ops->dumpit, ops->done, 0); genl_lock(); return err; } diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c index 438accb7a5a8..d61f6761777d 100644 --- a/net/phonet/pn_netlink.c +++ b/net/phonet/pn_netlink.c @@ -289,15 +289,16 @@ out: int __init phonet_netlink_register(void) { - int err = __rtnl_register(PF_PHONET, RTM_NEWADDR, addr_doit, NULL); + int err = __rtnl_register(PF_PHONET, RTM_NEWADDR, addr_doit, + NULL, NULL); if (err) return err; /* Further __rtnl_register() cannot fail */ - __rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL); - __rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit); - __rtnl_register(PF_PHONET, RTM_NEWROUTE, route_doit, NULL); - __rtnl_register(PF_PHONET, RTM_DELROUTE, route_doit, NULL); - __rtnl_register(PF_PHONET, RTM_GETROUTE, NULL, route_dumpit); + __rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL, NULL); + __rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit, NULL); + __rtnl_register(PF_PHONET, RTM_NEWROUTE, route_doit, NULL, NULL); + __rtnl_register(PF_PHONET, RTM_DELROUTE, route_doit, NULL, NULL); + __rtnl_register(PF_PHONET, RTM_GETROUTE, NULL, route_dumpit, NULL); return 0; } diff --git a/net/sched/act_api.c b/net/sched/act_api.c index a606025814a1..2f64262ab5d2 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -1115,9 +1115,10 @@ nlmsg_failure: static int __init tc_action_init(void) { - rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL); - rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL); - rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action); + rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action, + NULL); return 0; } diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index bb2c523f8158..9563887f219f 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -610,10 +610,10 @@ EXPORT_SYMBOL(tcf_exts_dump_stats); static int __init tc_filter_init(void) { - rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL); - rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL, NULL); rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter, - tc_dump_tfilter); + tc_dump_tfilter, NULL); return 0; } diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 6b8627661c98..8182aefafb02 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1792,12 +1792,12 @@ static int __init pktsched_init(void) register_qdisc(&pfifo_head_drop_qdisc_ops); register_qdisc(&mq_qdisc_ops); - rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL); - rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL); - rtnl_register(PF_UNSPEC, RTM_GETQDISC, tc_get_qdisc, tc_dump_qdisc); - rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL); - rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL); - rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass); + rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETQDISC, tc_get_qdisc, tc_dump_qdisc, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass, NULL); return 0; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index c658cb3bc7c3..0256b8a0a7cf 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2299,7 +2299,8 @@ 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); + return netlink_dump_start(net->xfrm.nlsk, skb, nlh, + link->dump, link->done, 0); } err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX, -- cgit v1.2.3 From 28f65c11f2ffb3957259dece647a24f8ad2e241b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 9 Jun 2011 09:13:32 -0700 Subject: treewide: Convert uses of struct resource to resource_size(ptr) Several fixes as well where the +1 was missing. Done via coccinelle scripts like: @@ struct resource *ptr; @@ - ptr->end - ptr->start + 1 + resource_size(ptr) and some grep and typing. Mostly uncompiled, no cross-compilers. Signed-off-by: Joe Perches Signed-off-by: Jiri Kosina --- arch/arm/common/scoop.c | 2 +- arch/arm/mach-at91/at91sam9261_devices.c | 2 +- arch/arm/mach-mv78xx0/pcie.c | 8 ++++---- arch/arm/mach-u300/core.c | 2 +- arch/arm/plat-mxc/pwm.c | 8 ++++---- arch/arm/plat-s5p/sysmmu.c | 6 +++--- arch/arm/plat-samsung/pm-check.c | 2 +- arch/avr32/kernel/setup.c | 10 +++++----- arch/avr32/mach-at32ap/extint.c | 2 +- arch/avr32/mach-at32ap/hsmc.c | 2 +- arch/avr32/mach-at32ap/intc.c | 2 +- arch/avr32/mach-at32ap/pio.c | 2 +- arch/microblaze/pci/pci-common.c | 2 +- arch/mips/pci/pci-rc32434.c | 2 +- arch/mips/pci/pci-vr41xx.c | 2 +- arch/mips/powertv/asic/asic_devices.c | 10 ++++------ arch/powerpc/include/asm/macio.h | 2 +- arch/powerpc/kernel/machine_kexec.c | 4 ++-- arch/powerpc/kernel/pci-common.c | 2 +- arch/powerpc/platforms/52xx/mpc52xx_pci.c | 8 ++++---- arch/powerpc/platforms/83xx/km83xx.c | 2 +- arch/powerpc/platforms/83xx/mpc832x_mds.c | 2 +- arch/powerpc/platforms/83xx/mpc834x_mds.c | 2 +- arch/powerpc/platforms/83xx/mpc836x_mds.c | 2 +- arch/powerpc/platforms/83xx/usb.c | 2 +- arch/powerpc/platforms/85xx/sbc8560.c | 2 +- arch/powerpc/platforms/85xx/xes_mpc85xx.c | 2 +- arch/powerpc/platforms/cell/celleb_scc_epci.c | 8 ++++---- arch/powerpc/platforms/cell/celleb_scc_pciex.c | 2 +- arch/powerpc/platforms/cell/spu_manage.c | 2 +- arch/powerpc/platforms/chrp/pci.c | 2 +- arch/powerpc/platforms/pasemi/dma_lib.c | 2 +- arch/powerpc/platforms/powermac/nvram.c | 4 ++-- arch/powerpc/platforms/powermac/pci.c | 6 ++---- arch/powerpc/platforms/powermac/time.c | 2 +- arch/powerpc/sysdev/axonram.c | 2 +- arch/powerpc/sysdev/cpm1.c | 2 +- arch/powerpc/sysdev/cpm_common.c | 2 +- arch/powerpc/sysdev/dart_iommu.c | 2 +- arch/powerpc/sysdev/fsl_msi.c | 2 +- arch/powerpc/sysdev/fsl_pci.c | 12 ++++++------ arch/powerpc/sysdev/fsl_rio.c | 2 +- arch/powerpc/sysdev/ipic.c | 2 +- arch/powerpc/sysdev/mmio_nvram.c | 2 +- arch/powerpc/sysdev/mpc8xx_pic.c | 2 +- arch/powerpc/sysdev/mv64x60_udbg.c | 4 ++-- arch/powerpc/sysdev/ppc4xx_pci.c | 16 ++++++++-------- arch/powerpc/sysdev/qe_lib/qe_ic.c | 2 +- arch/powerpc/sysdev/qe_lib/qe_io.c | 2 +- arch/powerpc/sysdev/xics/icp-native.c | 2 +- arch/sh/kernel/io_trapped.c | 8 ++++---- arch/sh/kernel/machine_kexec.c | 2 +- arch/sparc/kernel/ioport.c | 12 ++++++------ arch/sparc/kernel/pci.c | 6 ++---- arch/tile/kernel/setup.c | 3 +-- arch/x86/kernel/pci-calgary_64.c | 2 +- arch/x86/kernel/probe_roms.c | 2 +- drivers/char/bsr.c | 2 +- drivers/char/xilinx_hwicap/xilinx_hwicap.c | 2 +- drivers/dma/mv_xor.c | 5 ++--- drivers/edac/cell_edac.c | 2 +- drivers/edac/mpc85xx_edac.c | 12 ++++++------ drivers/gpio/gpio-u300.c | 8 ++++---- drivers/ide/palm_bk3710.c | 2 +- drivers/ide/tx4939ide.c | 4 ++-- drivers/input/serio/sa1111ps2.c | 6 ++---- drivers/media/video/davinci/vpif.c | 2 +- drivers/media/video/omap24xxcam.c | 5 ++--- drivers/message/i2o/iop.c | 8 ++++---- drivers/mfd/tc6387xb.c | 2 +- drivers/misc/atmel-ssc.c | 2 +- drivers/misc/atmel_pwm.c | 2 +- drivers/mmc/host/dw_mmc.c | 2 +- drivers/mtd/maps/bfin-async-flash.c | 2 +- drivers/mtd/maps/ixp2000.c | 11 ++++++----- drivers/mtd/maps/pxa2xx-flash.c | 2 +- drivers/mtd/nand/atmel_nand.c | 4 ++-- drivers/mtd/nand/bcm_umi_nand.c | 2 +- drivers/mtd/nand/mpc5121_nfc.c | 2 +- drivers/net/bcm63xx_enet.c | 8 ++++---- drivers/net/can/softing/softing_main.c | 2 +- drivers/net/davinci_emac.c | 6 +++--- drivers/net/ethoc.c | 2 +- drivers/net/fec_mpc52xx.c | 7 ++++--- drivers/net/fs_enet/mii-bitbang.c | 4 ++-- drivers/net/fs_enet/mii-fec.c | 2 +- drivers/net/gianfar_ptp.c | 2 +- drivers/net/ibm_newemac/core.c | 2 +- drivers/net/macb.c | 2 +- drivers/net/mv643xx_eth.c | 2 +- drivers/net/pxa168_eth.c | 2 +- drivers/net/sb1250-mac.c | 2 +- drivers/parport/parport_ax88796.c | 2 +- drivers/pci/hotplug/shpchp_sysfs.c | 21 +++++++++------------ drivers/pcmcia/at91_cf.c | 7 +++---- drivers/pcmcia/electra_cf.c | 4 ++-- drivers/pcmcia/rsrc_iodyn.c | 6 +++--- drivers/pcmcia/rsrc_nonstatic.c | 6 +++--- drivers/pnp/pnpacpi/rsparser.c | 10 +++++----- drivers/pnp/pnpbios/rsparser.c | 12 ++++++------ drivers/rtc/rtc-at32ap700x.c | 2 +- drivers/rtc/rtc-cmos.c | 6 +++--- drivers/rtc/rtc-ds1286.c | 2 +- drivers/rtc/rtc-ds1511.c | 2 +- drivers/rtc/rtc-ds1742.c | 2 +- drivers/rtc/rtc-m48t35.c | 2 +- drivers/rtc/rtc-m48t59.c | 2 +- drivers/rtc/rtc-mrst.c | 5 ++--- drivers/rtc/rtc-puv3.c | 5 ++--- drivers/rtc/rtc-s3c.c | 5 ++--- drivers/staging/generic_serial/ser_a2232.c | 3 ++- drivers/staging/gma500/psb_gtt.c | 8 ++++---- drivers/tty/serial/bfin_5xx.c | 5 ++--- drivers/tty/serial/imx.c | 5 ++--- drivers/tty/serial/m32r_sio.c | 2 +- drivers/tty/serial/omap-serial.c | 6 +++--- drivers/tty/serial/pxa.c | 2 +- drivers/tty/serial/sunsu.c | 2 +- drivers/tty/serial/vt8500_serial.c | 3 +-- drivers/uio/uio_pdrv.c | 2 +- drivers/uio/uio_pdrv_genirq.c | 2 +- drivers/usb/gadget/atmel_usba_udc.c | 2 +- drivers/usb/gadget/fsl_udc_core.c | 6 +++--- drivers/usb/host/ehci-ath79.c | 2 +- drivers/usb/host/ehci-cns3xxx.c | 2 +- drivers/usb/host/ehci-fsl.c | 2 +- drivers/usb/host/ehci-grlib.c | 2 +- drivers/usb/host/ehci-ixp4xx.c | 2 +- drivers/usb/host/ehci-octeon.c | 2 +- drivers/usb/host/ehci-pmcmsp.c | 10 +++++----- drivers/usb/host/ehci-ppc-of.c | 2 +- drivers/usb/host/ehci-w90x900.c | 2 +- drivers/usb/host/ehci-xilinx-of.c | 2 +- drivers/usb/host/fhci-hcd.c | 2 +- drivers/usb/host/ohci-ath79.c | 4 ++-- drivers/usb/host/ohci-cns3xxx.c | 2 +- drivers/usb/host/ohci-da8xx.c | 2 +- drivers/usb/host/ohci-octeon.c | 2 +- drivers/usb/host/ohci-ppc-of.c | 2 +- drivers/usb/host/ohci-ppc-soc.c | 2 +- drivers/usb/host/ohci-sa1111.c | 2 +- drivers/usb/host/ohci-sm501.c | 11 +++++------ drivers/usb/host/ohci-tmio.c | 6 +++--- drivers/usb/host/oxu210hp-hcd.c | 2 +- drivers/usb/host/uhci-grlib.c | 2 +- drivers/usb/host/whci/init.c | 2 +- drivers/uwb/whc-rc.c | 2 +- drivers/video/atmel_lcdfb.c | 4 ++-- drivers/video/aty/atyfb_base.c | 7 ++++--- drivers/video/au1100fb.c | 2 +- drivers/video/cobalt_lcdfb.c | 2 +- drivers/video/controlfb.c | 4 ++-- drivers/video/mb862xx/mb862xxfbdrv.c | 4 ++-- drivers/video/msm/mdp.c | 3 +-- drivers/video/msm/msm_fb.c | 7 +++---- drivers/video/nuc900fb.c | 2 +- drivers/video/platinumfb.c | 5 ++--- drivers/video/pxa168fb.c | 2 +- include/linux/dio.h | 2 +- include/linux/pnp.h | 2 +- include/linux/zorro.h | 2 +- kernel/kexec.c | 2 +- sound/aoa/soundbus/i2sbus/core.c | 9 ++++----- sound/atmel/abdac.c | 2 +- sound/atmel/ac97c.c | 2 +- sound/ppc/pmac.c | 9 +++------ sound/soc/fsl/fsl_ssi.c | 2 +- sound/soc/fsl/mpc5200_dma.c | 2 +- 168 files changed, 308 insertions(+), 333 deletions(-) (limited to 'include') diff --git a/arch/arm/common/scoop.c b/arch/arm/common/scoop.c index c11af1e4bad3..a07b0e763a80 100644 --- a/arch/arm/common/scoop.c +++ b/arch/arm/common/scoop.c @@ -193,7 +193,7 @@ static int __devinit scoop_probe(struct platform_device *pdev) spin_lock_init(&devptr->scoop_lock); inf = pdev->dev.platform_data; - devptr->base = ioremap(mem->start, mem->end - mem->start + 1); + devptr->base = ioremap(mem->start, resource_size(mem)); if (!devptr->base) { ret = -ENOMEM; diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c index 3eb4538fceeb..14dd666850b0 100644 --- a/arch/arm/mach-at91/at91sam9261_devices.c +++ b/arch/arm/mach-at91/at91sam9261_devices.c @@ -525,7 +525,7 @@ void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data) if (ARRAY_SIZE(lcdc_resources) > 2) { void __iomem *fb; struct resource *fb_res = &lcdc_resources[2]; - size_t fb_len = fb_res->end - fb_res->start + 1; + size_t fb_len = resource_size(fb_res); fb = ioremap(fb_res->start, fb_len); if (fb) { diff --git a/arch/arm/mach-mv78xx0/pcie.c b/arch/arm/mach-mv78xx0/pcie.c index a560439dcc3c..f27c7d2fa9f7 100644 --- a/arch/arm/mach-mv78xx0/pcie.c +++ b/arch/arm/mach-mv78xx0/pcie.c @@ -129,12 +129,12 @@ static void __init mv78xx0_pcie_preinit(void) struct pcie_port *pp = pcie_port + i; mv78xx0_setup_pcie_io_win(win++, pp->res[0].start, - pp->res[0].end - pp->res[0].start + 1, - pp->maj, pp->min); + resource_size(&pp->res[0]), + pp->maj, pp->min); mv78xx0_setup_pcie_mem_win(win++, pp->res[1].start, - pp->res[1].end - pp->res[1].start + 1, - pp->maj, pp->min); + resource_size(&pp->res[1]), + pp->maj, pp->min); } } diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 513d6abec1f5..399c89f14dfb 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -1791,7 +1791,7 @@ static void __init u300_assign_physmem(void) 0 == res->start) { res->start = curr_start; res->end += curr_start; - curr_start += (res->end - res->start + 1); + curr_start += resource_size(res); printk(KERN_INFO "core.c: Mapping RAM " \ "%#x-%#x to device %s:%s\n", diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c index 7a61ef8f471a..761c3c940a68 100644 --- a/arch/arm/plat-mxc/pwm.c +++ b/arch/arm/plat-mxc/pwm.c @@ -214,14 +214,14 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev) goto err_free_clk; } - r = request_mem_region(r->start, r->end - r->start + 1, pdev->name); + r = request_mem_region(r->start, resource_size(r), pdev->name); if (r == NULL) { dev_err(&pdev->dev, "failed to request memory resource\n"); ret = -EBUSY; goto err_free_clk; } - pwm->mmio_base = ioremap(r->start, r->end - r->start + 1); + pwm->mmio_base = ioremap(r->start, resource_size(r)); if (pwm->mmio_base == NULL) { dev_err(&pdev->dev, "failed to ioremap() registers\n"); ret = -ENODEV; @@ -236,7 +236,7 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev) return 0; err_free_mem: - release_mem_region(r->start, r->end - r->start + 1); + release_mem_region(r->start, resource_size(r)); err_free_clk: clk_put(pwm->clk); err_free: @@ -260,7 +260,7 @@ static int __devexit mxc_pwm_remove(struct platform_device *pdev) iounmap(pwm->mmio_base); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, r->end - r->start + 1); + release_mem_region(r->start, resource_size(r)); clk_put(pwm->clk); diff --git a/arch/arm/plat-s5p/sysmmu.c b/arch/arm/plat-s5p/sysmmu.c index 54f5eddc921d..e1cbc728c775 100644 --- a/arch/arm/plat-s5p/sysmmu.c +++ b/arch/arm/plat-s5p/sysmmu.c @@ -232,8 +232,8 @@ static int s5p_sysmmu_probe(struct platform_device *pdev) goto err_res; } - mem = request_mem_region(res->start, - ((res->end) - (res->start)) + 1, pdev->name); + mem = request_mem_region(res->start, resource_size(res), + pdev->name); if (!mem) { dev_err(dev, "Failed to request the memory region of %s.\n", sysmmu_ips_name[i]); @@ -241,7 +241,7 @@ static int s5p_sysmmu_probe(struct platform_device *pdev) goto err_res; } - sysmmusfrs[i] = ioremap(res->start, res->end - res->start + 1); + sysmmusfrs[i] = ioremap(res->start, resource_size(res)); if (!sysmmusfrs[i]) { dev_err(dev, "Failed to ioremap() for %s.\n", sysmmu_ips_name[i]); diff --git a/arch/arm/plat-samsung/pm-check.c b/arch/arm/plat-samsung/pm-check.c index 6b733fafe7cd..3cbd62666b1e 100644 --- a/arch/arm/plat-samsung/pm-check.c +++ b/arch/arm/plat-samsung/pm-check.c @@ -72,7 +72,7 @@ static void s3c_pm_run_sysram(run_fn_t fn, u32 *arg) static u32 *s3c_pm_countram(struct resource *res, u32 *val) { - u32 size = (u32)(res->end - res->start)+1; + u32 size = (u32)resource_size(res); size += CHECK_CHUNKSIZE-1; size /= CHECK_CHUNKSIZE; diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c index bb0974cce4ac..b4247f478065 100644 --- a/arch/avr32/kernel/setup.c +++ b/arch/avr32/kernel/setup.c @@ -444,7 +444,7 @@ static unsigned long __init find_bootmap_pfn(const struct resource *mem) { unsigned long bootmap_pages, bootmap_len; - unsigned long node_pages = PFN_UP(mem->end - mem->start + 1); + unsigned long node_pages = PFN_UP(resource_size(mem)); unsigned long bootmap_start; bootmap_pages = bootmem_bootmap_pages(node_pages); @@ -541,10 +541,10 @@ static void __init setup_bootmem(void) */ if (res->start >= PFN_PHYS(first_pfn) && res->end < PFN_PHYS(max_pfn)) - reserve_bootmem_node( - NODE_DATA(node), res->start, - res->end - res->start + 1, - BOOTMEM_DEFAULT); + reserve_bootmem_node(NODE_DATA(node), + res->start, + resource_size(res), + BOOTMEM_DEFAULT); } node_set_online(node); diff --git a/arch/avr32/mach-at32ap/extint.c b/arch/avr32/mach-at32ap/extint.c index fbc2aeaebddb..cfb298d66305 100644 --- a/arch/avr32/mach-at32ap/extint.c +++ b/arch/avr32/mach-at32ap/extint.c @@ -204,7 +204,7 @@ static int __init eic_probe(struct platform_device *pdev) } eic->first_irq = EIM_IRQ_BASE + 32 * pdev->id; - eic->regs = ioremap(regs->start, regs->end - regs->start + 1); + eic->regs = ioremap(regs->start, resource_size(regs)); if (!eic->regs) { dev_dbg(&pdev->dev, "failed to map regs\n"); goto err_ioremap; diff --git a/arch/avr32/mach-at32ap/hsmc.c b/arch/avr32/mach-at32ap/hsmc.c index f7672d3e86b8..f66245e6e63e 100644 --- a/arch/avr32/mach-at32ap/hsmc.c +++ b/arch/avr32/mach-at32ap/hsmc.c @@ -245,7 +245,7 @@ static int hsmc_probe(struct platform_device *pdev) hsmc->pclk = pclk; hsmc->mck = mck; - hsmc->regs = ioremap(regs->start, regs->end - regs->start + 1); + hsmc->regs = ioremap(regs->start, resource_size(regs)); if (!hsmc->regs) goto out_disable_clocks; diff --git a/arch/avr32/mach-at32ap/intc.c b/arch/avr32/mach-at32ap/intc.c index 3e3646186c9f..6c700431e34a 100644 --- a/arch/avr32/mach-at32ap/intc.c +++ b/arch/avr32/mach-at32ap/intc.c @@ -107,7 +107,7 @@ void __init init_IRQ(void) clk_enable(pclk); - intc0.regs = ioremap(regs->start, regs->end - regs->start + 1); + intc0.regs = ioremap(regs->start, resource_size(regs)); if (!intc0.regs) { printk(KERN_EMERG "intc: failed to map registers (0x%08lx)\n", (unsigned long)regs->start); diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c index 2e0aa853a4bc..9b39dea6682f 100644 --- a/arch/avr32/mach-at32ap/pio.c +++ b/arch/avr32/mach-at32ap/pio.c @@ -461,7 +461,7 @@ void __init at32_init_pio(struct platform_device *pdev) clk_enable(pio->clk); pio->pdev = pdev; - pio->regs = ioremap(regs->start, regs->end - regs->start + 1); + pio->regs = ioremap(regs->start, resource_size(regs)); /* start with irqs disabled and acked */ pio_writel(pio, IDR, ~0UL); diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index 53599067d2f9..dd3fae06ece4 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -89,7 +89,7 @@ void pcibios_free_controller(struct pci_controller *phb) static resource_size_t pcibios_io_size(const struct pci_controller *hose) { - return hose->io_resource.end - hose->io_resource.start + 1; + return resource_size(&hose->io_resource); } int pcibios_vaddr_is_ioport(void __iomem *address) diff --git a/arch/mips/pci/pci-rc32434.c b/arch/mips/pci/pci-rc32434.c index f31218e17d3c..764362ce5e40 100644 --- a/arch/mips/pci/pci-rc32434.c +++ b/arch/mips/pci/pci-rc32434.c @@ -215,7 +215,7 @@ static int __init rc32434_pci_init(void) rc32434_pcibridge_init(); io_map_base = ioremap(rc32434_res_pci_io1.start, - rc32434_res_pci_io1.end - rc32434_res_pci_io1.start + 1); + resource_size(&rcrc32434_res_pci_io1)); if (!io_map_base) return -ENOMEM; diff --git a/arch/mips/pci/pci-vr41xx.c b/arch/mips/pci/pci-vr41xx.c index 56525711f8b7..444b8d8004ad 100644 --- a/arch/mips/pci/pci-vr41xx.c +++ b/arch/mips/pci/pci-vr41xx.c @@ -305,7 +305,7 @@ static int __init vr41xx_pciu_init(void) struct resource *res = vr41xx_pci_controller.io_resource; master = setup->master_io; io_map_base = ioremap(master->bus_base_address, - res->end - res->start + 1); + resource_size(res)); if (!io_map_base) return -EBUSY; diff --git a/arch/mips/powertv/asic/asic_devices.c b/arch/mips/powertv/asic/asic_devices.c index e56fa61b3991..bce1872249ba 100644 --- a/arch/mips/powertv/asic/asic_devices.c +++ b/arch/mips/powertv/asic/asic_devices.c @@ -394,23 +394,21 @@ void __init platform_alloc_bootmem(void) /* Loop through looking for resources that want a particular address */ for (i = 0; gp_resources[i].flags != 0; i++) { - int size = gp_resources[i].end - gp_resources[i].start + 1; + int size = resource_size(&gp_resources[i]); if ((gp_resources[i].start != 0) && ((gp_resources[i].flags & IORESOURCE_MEM) != 0)) { reserve_bootmem(dma_to_phys(gp_resources[i].start), size, 0); - total += gp_resources[i].end - - gp_resources[i].start + 1; + total += resource_size(&gp_resources[i]); pr_info("reserve resource %s at %08x (%u bytes)\n", gp_resources[i].name, gp_resources[i].start, - gp_resources[i].end - - gp_resources[i].start + 1); + resource_size(&gp_resources[i])); } } /* Loop through assigning addresses for those that are left */ for (i = 0; gp_resources[i].flags != 0; i++) { - int size = gp_resources[i].end - gp_resources[i].start + 1; + int size = resource_size(&gp_resources[i]); if ((gp_resources[i].start == 0) && ((gp_resources[i].flags & IORESOURCE_MEM) != 0)) { void *mem = alloc_bootmem_pages(size); diff --git a/arch/powerpc/include/asm/macio.h b/arch/powerpc/include/asm/macio.h index 7ab82c825a03..27af7f8bbb8d 100644 --- a/arch/powerpc/include/asm/macio.h +++ b/arch/powerpc/include/asm/macio.h @@ -76,7 +76,7 @@ static inline unsigned long macio_resource_len(struct macio_dev *dev, int resour struct resource *res = &dev->resource[resource_no]; if (res->start == 0 || res->end == 0 || res->end < res->start) return 0; - return res->end - res->start + 1; + return resource_size(res); } extern int macio_enable_devres(struct macio_dev *dev); diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c index 7ee50f0547cb..6658a1589955 100644 --- a/arch/powerpc/kernel/machine_kexec.c +++ b/arch/powerpc/kernel/machine_kexec.c @@ -126,7 +126,7 @@ void __init reserve_crashkernel(void) /* We might have got these values via the command line or the * device tree, either way sanitise them now. */ - crash_size = crashk_res.end - crashk_res.start + 1; + crash_size = resource_size(&crashk_res); #ifndef CONFIG_RELOCATABLE if (crashk_res.start != KDUMP_KERNELBASE) @@ -222,7 +222,7 @@ static void __init export_crashk_values(struct device_node *node) if (crashk_res.start != 0) { prom_add_property(node, &crashk_base_prop); - crashk_size = crashk_res.end - crashk_res.start + 1; + crashk_size = resource_size(&crashk_res); prom_add_property(node, &crashk_size_prop); } } diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 893af2a9cd03..3764e37205fb 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -107,7 +107,7 @@ static resource_size_t pcibios_io_size(const struct pci_controller *hose) #ifdef CONFIG_PPC64 return hose->pci_io_size; #else - return hose->io_resource.end - hose->io_resource.start + 1; + return resource_size(&hose->io_resource); #endif } diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pci.c b/arch/powerpc/platforms/52xx/mpc52xx_pci.c index da110bd88346..5f5e69309080 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_pci.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_pci.c @@ -264,7 +264,7 @@ mpc52xx_pci_setup(struct pci_controller *hose, (unsigned long long)res->flags); out_be32(&pci_regs->iw0btar, MPC52xx_PCI_IWBTAR_TRANSLATION(res->start, res->start, - res->end - res->start + 1)); + resource_size(res))); iwcr0 = MPC52xx_PCI_IWCR_ENABLE | MPC52xx_PCI_IWCR_MEM; if (res->flags & IORESOURCE_PREFETCH) iwcr0 |= MPC52xx_PCI_IWCR_READ_MULTI; @@ -278,7 +278,7 @@ mpc52xx_pci_setup(struct pci_controller *hose, res->start, res->end, res->flags); out_be32(&pci_regs->iw1btar, MPC52xx_PCI_IWBTAR_TRANSLATION(res->start, res->start, - res->end - res->start + 1)); + resource_size(res))); iwcr1 = MPC52xx_PCI_IWCR_ENABLE | MPC52xx_PCI_IWCR_MEM; if (res->flags & IORESOURCE_PREFETCH) iwcr1 |= MPC52xx_PCI_IWCR_READ_MULTI; @@ -300,7 +300,7 @@ mpc52xx_pci_setup(struct pci_controller *hose, out_be32(&pci_regs->iw2btar, MPC52xx_PCI_IWBTAR_TRANSLATION(hose->io_base_phys, res->start, - res->end - res->start + 1)); + resource_size(res))); iwcr2 = MPC52xx_PCI_IWCR_ENABLE | MPC52xx_PCI_IWCR_IO; /* Set all the IWCR fields at once; they're in the same reg */ @@ -402,7 +402,7 @@ mpc52xx_add_bridge(struct device_node *node) hose->ops = &mpc52xx_pci_ops; - pci_regs = ioremap(rsrc.start, rsrc.end - rsrc.start + 1); + pci_regs = ioremap(rsrc.start, resource_size(&rsrc)); if (!pci_regs) return -ENOMEM; diff --git a/arch/powerpc/platforms/83xx/km83xx.c b/arch/powerpc/platforms/83xx/km83xx.c index a2b9b9ef1240..f8fa2fc3129f 100644 --- a/arch/powerpc/platforms/83xx/km83xx.c +++ b/arch/powerpc/platforms/83xx/km83xx.c @@ -101,7 +101,7 @@ static void __init mpc83xx_km_setup_arch(void) __func__); return; } - base = ioremap(res.start, res.end - res.start + 1); + base = ioremap(res.start, resource_size(&res)); /* * IMMR + 0x14A8[4:5] = 11 (clk delay for UCC 2) diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c index ec0b401bc9cf..93e60f1f21a9 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c @@ -68,7 +68,7 @@ static void __init mpc832x_sys_setup_arch(void) struct resource res; of_address_to_resource(np, 0, &res); - bcsr_regs = ioremap(res.start, res.end - res.start +1); + bcsr_regs = ioremap(res.start, resource_size(&res)); of_node_put(np); } diff --git a/arch/powerpc/platforms/83xx/mpc834x_mds.c b/arch/powerpc/platforms/83xx/mpc834x_mds.c index d0a634b056ca..c1b1dc50b32a 100644 --- a/arch/powerpc/platforms/83xx/mpc834x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc834x_mds.c @@ -53,7 +53,7 @@ static int mpc834xemds_usb_cfg(void) struct resource res; of_address_to_resource(np, 0, &res); - bcsr_regs = ioremap(res.start, res.end - res.start + 1); + bcsr_regs = ioremap(res.start, resource_size(&res)); of_node_put(np); } if (!bcsr_regs) diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c index 09e9d6fb7411..81c052b1353e 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c @@ -76,7 +76,7 @@ static void __init mpc836x_mds_setup_arch(void) struct resource res; of_address_to_resource(np, 0, &res); - bcsr_regs = ioremap(res.start, res.end - res.start +1); + bcsr_regs = ioremap(res.start, resource_size(&res)); of_node_put(np); } diff --git a/arch/powerpc/platforms/83xx/usb.c b/arch/powerpc/platforms/83xx/usb.c index 2c64164722d0..1ad748bb39b4 100644 --- a/arch/powerpc/platforms/83xx/usb.c +++ b/arch/powerpc/platforms/83xx/usb.c @@ -171,7 +171,7 @@ int mpc831x_usb_cfg(void) of_node_put(np); return ret; } - usb_regs = ioremap(res.start, res.end - res.start + 1); + usb_regs = ioremap(res.start, resource_size(&res)); /* Using on-chip PHY */ if (prop && (!strcmp(prop, "utmi_wide") || diff --git a/arch/powerpc/platforms/85xx/sbc8560.c b/arch/powerpc/platforms/85xx/sbc8560.c index d2dfd465fbf6..09ced7221750 100644 --- a/arch/powerpc/platforms/85xx/sbc8560.c +++ b/arch/powerpc/platforms/85xx/sbc8560.c @@ -285,7 +285,7 @@ static int __init sbc8560_bdrstcr_init(void) printk(KERN_INFO "sbc8560: Found BRSTCR at i/o 0x%x\n", res.start); - brstcr = ioremap(res.start, res.end - res.start); + brstcr = ioremap(res.start, resource_size(&res)); if(!brstcr) printk(KERN_WARNING "sbc8560: ioremap of brstcr failed.\n"); diff --git a/arch/powerpc/platforms/85xx/xes_mpc85xx.c b/arch/powerpc/platforms/85xx/xes_mpc85xx.c index 0125604d096e..a9dc5e795123 100644 --- a/arch/powerpc/platforms/85xx/xes_mpc85xx.c +++ b/arch/powerpc/platforms/85xx/xes_mpc85xx.c @@ -123,7 +123,7 @@ static void xes_mpc85xx_fixups(void) continue; } - l2_base = ioremap(r[0].start, r[0].end - r[0].start + 1); + l2_base = ioremap(r[0].start, resource_size(&r[0])); xes_mpc85xx_configure_l2(l2_base); } diff --git a/arch/powerpc/platforms/cell/celleb_scc_epci.c b/arch/powerpc/platforms/cell/celleb_scc_epci.c index 05b0db3ef638..844c0facb4f7 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_epci.c +++ b/arch/powerpc/platforms/cell/celleb_scc_epci.c @@ -393,19 +393,19 @@ static int __init celleb_setup_epci(struct device_node *node, if (of_address_to_resource(node, 0, &r)) goto error; - hose->cfg_addr = ioremap(r.start, (r.end - r.start + 1)); + hose->cfg_addr = ioremap(r.start, resource_size(&r)); if (!hose->cfg_addr) goto error; pr_debug("EPCI: cfg_addr map 0x%016llx->0x%016lx + 0x%016llx\n", - r.start, (unsigned long)hose->cfg_addr, (r.end - r.start + 1)); + r.start, (unsigned long)hose->cfg_addr, resource_size(&r)); if (of_address_to_resource(node, 2, &r)) goto error; - hose->cfg_data = ioremap(r.start, (r.end - r.start + 1)); + hose->cfg_data = ioremap(r.start, resource_size(&r)); if (!hose->cfg_data) goto error; pr_debug("EPCI: cfg_data map 0x%016llx->0x%016lx + 0x%016llx\n", - r.start, (unsigned long)hose->cfg_data, (r.end - r.start + 1)); + r.start, (unsigned long)hose->cfg_data, resource_size(&r)); hose->ops = &celleb_epci_ops; celleb_epci_init(hose); diff --git a/arch/powerpc/platforms/cell/celleb_scc_pciex.c b/arch/powerpc/platforms/cell/celleb_scc_pciex.c index a881bbee8de0..ae790ac4a589 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_pciex.c +++ b/arch/powerpc/platforms/cell/celleb_scc_pciex.c @@ -494,7 +494,7 @@ static __init int celleb_setup_pciex(struct device_node *node, pr_err("PCIEXC:Failed to get config resource.\n"); return 1; } - phb->cfg_addr = ioremap(r.start, r.end - r.start + 1); + phb->cfg_addr = ioremap(r.start, resource_size(&r)); if (!phb->cfg_addr) { pr_err("PCIEXC:Failed to remap SMMIO region.\n"); return 1; diff --git a/arch/powerpc/platforms/cell/spu_manage.c b/arch/powerpc/platforms/cell/spu_manage.c index f465d474ad9b..4e5c91489c02 100644 --- a/arch/powerpc/platforms/cell/spu_manage.c +++ b/arch/powerpc/platforms/cell/spu_manage.c @@ -222,7 +222,7 @@ static int spu_map_resource(struct spu *spu, int nr, return ret; if (phys) *phys = resource.start; - len = resource.end - resource.start + 1; + len = resource_size(&resource); *virt = ioremap(resource.start, len); if (!*virt) return -EINVAL; diff --git a/arch/powerpc/platforms/chrp/pci.c b/arch/powerpc/platforms/chrp/pci.c index 8f67a394b2d0..3f65443f1714 100644 --- a/arch/powerpc/platforms/chrp/pci.c +++ b/arch/powerpc/platforms/chrp/pci.c @@ -142,7 +142,7 @@ hydra_init(void) return 0; } of_node_put(np); - Hydra = ioremap(r.start, r.end-r.start); + Hydra = ioremap(r.start, resource_size(&r)); printk("Hydra Mac I/O at %llx\n", (unsigned long long)r.start); printk("Hydra Feature_Control was %x", in_le32(&Hydra->Feature_Control)); diff --git a/arch/powerpc/platforms/pasemi/dma_lib.c b/arch/powerpc/platforms/pasemi/dma_lib.c index 321a9b3a2d00..756123bf06ac 100644 --- a/arch/powerpc/platforms/pasemi/dma_lib.c +++ b/arch/powerpc/platforms/pasemi/dma_lib.c @@ -576,7 +576,7 @@ int pasemi_dma_init(void) res.start = 0xfd800000; res.end = res.start + 0x1000; } - dma_status = __ioremap(res.start, res.end-res.start, 0); + dma_status = __ioremap(res.start, resource_size(&res), 0); pci_dev_put(iob_pdev); for (i = 0; i < MAX_TXCH; i++) diff --git a/arch/powerpc/platforms/powermac/nvram.c b/arch/powerpc/platforms/powermac/nvram.c index b1cdcf94aa8e..695443bfdb08 100644 --- a/arch/powerpc/platforms/powermac/nvram.c +++ b/arch/powerpc/platforms/powermac/nvram.c @@ -580,10 +580,10 @@ int __init pmac_nvram_init(void) /* Try to obtain an address */ if (of_address_to_resource(dp, 0, &r1) == 0) { nvram_naddrs = 1; - s1 = (r1.end - r1.start) + 1; + s1 = resource_size(&r1); if (of_address_to_resource(dp, 1, &r2) == 0) { nvram_naddrs = 2; - s2 = (r2.end - r2.start) + 1; + s2 = resource_size(&r2); } } diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index f33e08d573ce..4d4eba324837 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -838,8 +838,7 @@ static void __init setup_u3_ht(struct pci_controller* hose) * into cfg_addr */ hose->cfg_data = ioremap(cfg_res.start, 0x02000000); - hose->cfg_addr = ioremap(self_res.start, - self_res.end - self_res.start + 1); + hose->cfg_addr = ioremap(self_res.start, resource_size(&self_res)); /* * /ht node doesn't expose a "ranges" property, we read the register @@ -1323,8 +1322,7 @@ static void fixup_u4_pcie(struct pci_dev* dev) */ if (r->start >= 0xf0000000 && r->start < 0xf3000000) continue; - if (!region || (r->end - r->start) > - (region->end - region->start)) + if (!region || resource_size(r) > resource_size(region)) region = r; } /* Nothing found, bail */ diff --git a/arch/powerpc/platforms/powermac/time.c b/arch/powerpc/platforms/powermac/time.c index 48211ca134c3..11c9fce43b5b 100644 --- a/arch/powerpc/platforms/powermac/time.c +++ b/arch/powerpc/platforms/powermac/time.c @@ -274,7 +274,7 @@ int __init via_calibrate_decr(void) return 0; } of_node_put(vias); - via = ioremap(rsrc.start, rsrc.end - rsrc.start + 1); + via = ioremap(rsrc.start, resource_size(&rsrc)); if (via == NULL) { printk(KERN_ERR "Failed to map VIA for timer calibration !\n"); return 0; diff --git a/arch/powerpc/sysdev/axonram.c b/arch/powerpc/sysdev/axonram.c index bd0d54060b94..265f0f09395a 100644 --- a/arch/powerpc/sysdev/axonram.c +++ b/arch/powerpc/sysdev/axonram.c @@ -203,7 +203,7 @@ static int axon_ram_probe(struct platform_device *device) goto failed; } - bank->size = resource.end - resource.start + 1; + bank->size = resource_size(&resource); if (bank->size == 0) { dev_err(&device->dev, "No DDR2 memory found for %s%d\n", diff --git a/arch/powerpc/sysdev/cpm1.c b/arch/powerpc/sysdev/cpm1.c index 350787c83e22..5d7d59a43c4c 100644 --- a/arch/powerpc/sysdev/cpm1.c +++ b/arch/powerpc/sysdev/cpm1.c @@ -148,7 +148,7 @@ unsigned int cpm_pic_init(void) if (ret) goto end; - cpic_reg = ioremap(res.start, res.end - res.start + 1); + cpic_reg = ioremap(res.start, resource_size(&res)); if (cpic_reg == NULL) goto end; diff --git a/arch/powerpc/sysdev/cpm_common.c b/arch/powerpc/sysdev/cpm_common.c index 2b69aa0315b3..d55d0ad0deab 100644 --- a/arch/powerpc/sysdev/cpm_common.c +++ b/arch/powerpc/sysdev/cpm_common.c @@ -115,7 +115,7 @@ int cpm_muram_init(void) max = r.end; rh_attach_region(&cpm_muram_info, r.start - muram_pbase, - r.end - r.start + 1); + resource_size(&r)); } muram_vbase = ioremap(muram_pbase, max - muram_pbase + 1); diff --git a/arch/powerpc/sysdev/dart_iommu.c b/arch/powerpc/sysdev/dart_iommu.c index 8e9e06a7ca59..4f2680f431b5 100644 --- a/arch/powerpc/sysdev/dart_iommu.c +++ b/arch/powerpc/sysdev/dart_iommu.c @@ -239,7 +239,7 @@ static int __init dart_init(struct device_node *dart_node) DARTMAP_RPNMASK); /* Map in DART registers */ - dart = ioremap(r.start, r.end - r.start + 1); + dart = ioremap(r.start, resource_size(&r)); if (dart == NULL) panic("DART: Cannot map registers!"); diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index 92e78333c47c..419a77239bd7 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -349,7 +349,7 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev) goto error_out; } - msi->msi_regs = ioremap(res.start, res.end - res.start + 1); + msi->msi_regs = ioremap(res.start, resource_size(&res)); if (!msi->msi_regs) { dev_err(&dev->dev, "ioremap problem failed\n"); goto error_out; diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index 68ca9290df94..ba5cb3fa7074 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -64,7 +64,7 @@ static int __init setup_one_atmu(struct ccsr_pci __iomem *pci, { resource_size_t pci_addr = res->start - offset; resource_size_t phys_addr = res->start; - resource_size_t size = res->end - res->start + 1; + resource_size_t size = resource_size(res); u32 flags = 0x80044000; /* enable & mem R/W */ unsigned int i; @@ -108,7 +108,7 @@ static void __init setup_pci_atmu(struct pci_controller *hose, char *name = hose->dn->full_name; pr_debug("PCI memory map start 0x%016llx, size 0x%016llx\n", - (u64)rsrc->start, (u64)rsrc->end - (u64)rsrc->start + 1); + (u64)rsrc->start, (u64)resource_size(rsrc)); if (of_device_is_compatible(hose->dn, "fsl,qoriq-pcie-v2.2")) { win_idx = 2; @@ -116,7 +116,7 @@ static void __init setup_pci_atmu(struct pci_controller *hose, end_idx = 3; } - pci = ioremap(rsrc->start, rsrc->end - rsrc->start + 1); + pci = ioremap(rsrc->start, resource_size(rsrc)); if (!pci) { dev_err(hose->parent, "Unable to map ATMU registers\n"); return; @@ -153,9 +153,9 @@ static void __init setup_pci_atmu(struct pci_controller *hose, } else { pr_debug("PCI IO resource start 0x%016llx, size 0x%016llx, " "phy base 0x%016llx.\n", - (u64)hose->io_resource.start, - (u64)hose->io_resource.end - (u64)hose->io_resource.start + 1, - (u64)hose->io_base_phys); + (u64)hose->io_resource.start, + (u64)resource_size(&hose->io_resource), + (u64)hose->io_base_phys); out_be32(&pci->pow[j].potar, (hose->io_resource.start >> 12)); out_be32(&pci->pow[j].potear, 0); out_be32(&pci->pow[j].powbar, (hose->io_base_phys >> 12)); diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c index 5b206a2fe17c..95853386a664 100644 --- a/arch/powerpc/sysdev/fsl_rio.c +++ b/arch/powerpc/sysdev/fsl_rio.c @@ -1523,7 +1523,7 @@ int fsl_rio_setup(struct platform_device *dev) port->priv = priv; port->phys_efptr = 0x100; - priv->regs_win = ioremap(regs.start, regs.end - regs.start + 1); + priv->regs_win = ioremap(regs.start, resource_size(®s)); rio_regs_win = priv->regs_win; /* Probe the master port phy type */ diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c index 7367d17364cb..95da897f05a7 100644 --- a/arch/powerpc/sysdev/ipic.c +++ b/arch/powerpc/sysdev/ipic.c @@ -736,7 +736,7 @@ struct ipic * __init ipic_init(struct device_node *node, unsigned int flags) return NULL; } - ipic->regs = ioremap(res.start, res.end - res.start + 1); + ipic->regs = ioremap(res.start, resource_size(&res)); ipic->irqhost->host_data = ipic; diff --git a/arch/powerpc/sysdev/mmio_nvram.c b/arch/powerpc/sysdev/mmio_nvram.c index ddc877a3a23a..69f5814ae6d4 100644 --- a/arch/powerpc/sysdev/mmio_nvram.c +++ b/arch/powerpc/sysdev/mmio_nvram.c @@ -129,7 +129,7 @@ int __init mmio_nvram_init(void) goto out; } nvram_addr = r.start; - mmio_nvram_len = r.end - r.start + 1; + mmio_nvram_len = resource_size(&r); if ( (!mmio_nvram_len) || (!nvram_addr) ) { printk(KERN_WARNING "nvram: address or length is 0\n"); ret = -EIO; diff --git a/arch/powerpc/sysdev/mpc8xx_pic.c b/arch/powerpc/sysdev/mpc8xx_pic.c index 20924f2246f0..22e48e2d71f1 100644 --- a/arch/powerpc/sysdev/mpc8xx_pic.c +++ b/arch/powerpc/sysdev/mpc8xx_pic.c @@ -166,7 +166,7 @@ int mpc8xx_pic_init(void) if (ret) goto out; - siu_reg = ioremap(res.start, res.end - res.start + 1); + siu_reg = ioremap(res.start, resource_size(&res)); if (siu_reg == NULL) { ret = -EINVAL; goto out; diff --git a/arch/powerpc/sysdev/mv64x60_udbg.c b/arch/powerpc/sysdev/mv64x60_udbg.c index 2792dc8b038c..50a81387e9b1 100644 --- a/arch/powerpc/sysdev/mv64x60_udbg.c +++ b/arch/powerpc/sysdev/mv64x60_udbg.c @@ -125,11 +125,11 @@ static void mv64x60_udbg_init(void) of_node_put(np); - mpsc_base = ioremap(r[0].start, r[0].end - r[0].start + 1); + mpsc_base = ioremap(r[0].start, resource_size(&r[0])); if (!mpsc_base) return; - mpsc_intr_cause = ioremap(r[1].start, r[1].end - r[1].start + 1); + mpsc_intr_cause = ioremap(r[1].start, resource_size(&r[1])); if (!mpsc_intr_cause) { iounmap(mpsc_base); return; diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c index 156aa7d36258..deda60a7f996 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.c +++ b/arch/powerpc/sysdev/ppc4xx_pci.c @@ -265,7 +265,7 @@ static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose, if (ppc4xx_setup_one_pci_PMM(hose, reg, res->start, res->start - hose->pci_mem_offset, - res->end + 1 - res->start, + resource_size(res), res->flags, j) == 0) { j++; @@ -290,7 +290,7 @@ static void __init ppc4xx_configure_pci_PTMs(struct pci_controller *hose, void __iomem *reg, const struct resource *res) { - resource_size_t size = res->end - res->start + 1; + resource_size_t size = resource_size(res); u32 sa; /* Calculate window size */ @@ -349,7 +349,7 @@ static void __init ppc4xx_probe_pci_bridge(struct device_node *np) bus_range = of_get_property(np, "bus-range", NULL); /* Map registers */ - reg = ioremap(rsrc_reg.start, rsrc_reg.end + 1 - rsrc_reg.start); + reg = ioremap(rsrc_reg.start, resource_size(&rsrc_reg)); if (reg == NULL) { printk(KERN_ERR "%s: Can't map registers !", np->full_name); goto fail; @@ -465,7 +465,7 @@ static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose, if (ppc4xx_setup_one_pcix_POM(hose, reg, res->start, res->start - hose->pci_mem_offset, - res->end + 1 - res->start, + resource_size(res), res->flags, j) == 0) { j++; @@ -492,7 +492,7 @@ static void __init ppc4xx_configure_pcix_PIMs(struct pci_controller *hose, int big_pim, int enable_msi_hole) { - resource_size_t size = res->end - res->start + 1; + resource_size_t size = resource_size(res); u32 sa; /* RAM is always at 0 */ @@ -555,7 +555,7 @@ static void __init ppc4xx_probe_pcix_bridge(struct device_node *np) bus_range = of_get_property(np, "bus-range", NULL); /* Map registers */ - reg = ioremap(rsrc_reg.start, rsrc_reg.end + 1 - rsrc_reg.start); + reg = ioremap(rsrc_reg.start, resource_size(&rsrc_reg)); if (reg == NULL) { printk(KERN_ERR "%s: Can't map registers !", np->full_name); goto fail; @@ -1604,7 +1604,7 @@ static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port, if (ppc4xx_setup_one_pciex_POM(port, hose, mbase, res->start, res->start - hose->pci_mem_offset, - res->end + 1 - res->start, + resource_size(res), res->flags, j) == 0) { j++; @@ -1639,7 +1639,7 @@ static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port, void __iomem *mbase, struct resource *res) { - resource_size_t size = res->end - res->start + 1; + resource_size_t size = resource_size(res); u64 sa; if (port->endpoint) { diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.c b/arch/powerpc/sysdev/qe_lib/qe_ic.c index b2acda07220d..18e75ca19fe6 100644 --- a/arch/powerpc/sysdev/qe_lib/qe_ic.c +++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c @@ -347,7 +347,7 @@ void __init qe_ic_init(struct device_node *node, unsigned int flags, return; } - qe_ic->regs = ioremap(res.start, res.end - res.start + 1); + qe_ic->regs = ioremap(res.start, resource_size(&res)); qe_ic->irqhost->host_data = qe_ic; qe_ic->hc_irq = qe_ic_irq_chip; diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/arch/powerpc/sysdev/qe_lib/qe_io.c index 77e4934b88c5..fd1a6c3b1721 100644 --- a/arch/powerpc/sysdev/qe_lib/qe_io.c +++ b/arch/powerpc/sysdev/qe_lib/qe_io.c @@ -41,7 +41,7 @@ int par_io_init(struct device_node *np) ret = of_address_to_resource(np, 0, &res); if (ret) return ret; - par_io = ioremap(res.start, res.end - res.start + 1); + par_io = ioremap(res.start, resource_size(&res)); num_ports = of_get_property(np, "num-ports", NULL); if (num_ports) diff --git a/arch/powerpc/sysdev/xics/icp-native.c b/arch/powerpc/sysdev/xics/icp-native.c index 1f15ad436140..039a7820ba7f 100644 --- a/arch/powerpc/sysdev/xics/icp-native.c +++ b/arch/powerpc/sysdev/xics/icp-native.c @@ -247,7 +247,7 @@ static int __init icp_native_init_one_node(struct device_node *np, return -1; } - if (icp_native_map_one_cpu(*indx, r.start, r.end - r.start)) + if (icp_native_map_one_cpu(*indx, r.start, resource_size(&r))) return -1; (*indx)++; diff --git a/arch/sh/kernel/io_trapped.c b/arch/sh/kernel/io_trapped.c index 32c385ef1011..0f62f4672754 100644 --- a/arch/sh/kernel/io_trapped.c +++ b/arch/sh/kernel/io_trapped.c @@ -58,7 +58,7 @@ int register_trapped_io(struct trapped_io *tiop) for (k = 0; k < tiop->num_resources; k++) { res = tiop->resource + k; - len += roundup((res->end - res->start) + 1, PAGE_SIZE); + len += roundup(resource_size(res), PAGE_SIZE); flags |= res->flags; } @@ -85,7 +85,7 @@ int register_trapped_io(struct trapped_io *tiop) (unsigned long)(tiop->virt_base + len), res->flags & IORESOURCE_IO ? "io" : "mmio", (unsigned long)res->start); - len += roundup((res->end - res->start) + 1, PAGE_SIZE); + len += roundup(resource_size(res), PAGE_SIZE); } tiop->magic = IO_TRAPPED_MAGIC; @@ -128,7 +128,7 @@ void __iomem *match_trapped_io_handler(struct list_head *list, return tiop->virt_base + voffs; } - len = (res->end - res->start) + 1; + len = resource_size(res); voffs += roundup(len, PAGE_SIZE); } } @@ -173,7 +173,7 @@ static unsigned long lookup_address(struct trapped_io *tiop, for (k = 0; k < tiop->num_resources; k++) { res = tiop->resource + k; - len = roundup((res->end - res->start) + 1, PAGE_SIZE); + len = roundup(resource_size(res), PAGE_SIZE); if (address < (vaddr + len)) return res->start + (address - vaddr); vaddr += len; diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c index e2a3af31ff99..c5a33f007f88 100644 --- a/arch/sh/kernel/machine_kexec.c +++ b/arch/sh/kernel/machine_kexec.c @@ -170,7 +170,7 @@ void __init reserve_crashkernel(void) if (crashk_res.end == crashk_res.start) goto disable; - crash_size = PAGE_ALIGN(crashk_res.end - crashk_res.start + 1); + crash_size = PAGE_ALIGN(resource_size(&crashk_res)); if (!crashk_res.start) { unsigned long max = memblock_end_of_DRAM() - memory_limit; crashk_res.start = __memblock_alloc_base(crash_size, PAGE_SIZE, max); diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c index 1c9c80a1a86a..6ffccd6e0156 100644 --- a/arch/sparc/kernel/ioport.c +++ b/arch/sparc/kernel/ioport.c @@ -228,7 +228,7 @@ _sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz) } pa &= PAGE_MASK; - sparc_mapiorange(bus, pa, res->start, res->end - res->start + 1); + sparc_mapiorange(bus, pa, res->start, resource_size(res)); return (void __iomem *)(unsigned long)(res->start + offset); } @@ -240,7 +240,7 @@ static void _sparc_free_io(struct resource *res) { unsigned long plen; - plen = res->end - res->start + 1; + plen = resource_size(res); BUG_ON((plen & (PAGE_SIZE-1)) != 0); sparc_unmapiorange(res->start, plen); release_resource(res); @@ -331,9 +331,9 @@ static void sbus_free_coherent(struct device *dev, size_t n, void *p, } n = PAGE_ALIGN(n); - if ((res->end-res->start)+1 != n) { + if (resource_size(res) != n) { printk("sbus_free_consistent: region 0x%lx asked 0x%zx\n", - (long)((res->end-res->start)+1), n); + (long)resource_size(res), n); return; } @@ -504,9 +504,9 @@ static void pci32_free_coherent(struct device *dev, size_t n, void *p, } n = PAGE_ALIGN(n); - if ((res->end-res->start)+1 != n) { + if (resource_size(res) != n) { printk("pci_free_consistent: region 0x%lx asked 0x%lx\n", - (long)((res->end-res->start)+1), (long)n); + (long)resource_size(res), (long)n); return; } diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index 713dc91020a6..2d1453dd93d5 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -820,11 +820,9 @@ static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struc unsigned long space_size, user_offset, user_size; if (mmap_state == pci_mmap_io) { - space_size = (pbm->io_space.end - - pbm->io_space.start) + 1; + space_size = resource_size(&pbm->io_space); } else { - space_size = (pbm->mem_space.end - - pbm->mem_space.start) + 1; + space_size = resource_size(&pbm->mem_space); } /* Make sure the request is in range. */ diff --git a/arch/tile/kernel/setup.c b/arch/tile/kernel/setup.c index 6cdc9ba55fe0..5f85d8b34dbb 100644 --- a/arch/tile/kernel/setup.c +++ b/arch/tile/kernel/setup.c @@ -553,8 +553,7 @@ static void __init setup_bootmem_allocator(void) #ifdef CONFIG_KEXEC if (crashk_res.start != crashk_res.end) - reserve_bootmem(crashk_res.start, - crashk_res.end - crashk_res.start + 1, 0); + reserve_bootmem(crashk_res.start, resource_size(&crashk_res), 0); #endif } diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c index e8c33a302006..726494b58345 100644 --- a/arch/x86/kernel/pci-calgary_64.c +++ b/arch/x86/kernel/pci-calgary_64.c @@ -1553,7 +1553,7 @@ static void __init calgary_fixup_one_tce_space(struct pci_dev *dev) continue; /* cover the whole region */ - npages = (r->end - r->start) >> PAGE_SHIFT; + npages = resource_size(r) >> PAGE_SHIFT; npages++; iommu_range_reserve(tbl, r->start, npages); diff --git a/arch/x86/kernel/probe_roms.c b/arch/x86/kernel/probe_roms.c index ba0a4cce53be..63228035f9d7 100644 --- a/arch/x86/kernel/probe_roms.c +++ b/arch/x86/kernel/probe_roms.c @@ -234,7 +234,7 @@ void __init probe_roms(void) /* check for extension rom (ignore length byte!) */ rom = isa_bus_to_virt(extension_rom_resource.start); if (romsignature(rom)) { - length = extension_rom_resource.end - extension_rom_resource.start + 1; + length = resource_size(&extension_rom_resource); if (romchecksum(rom, length)) { request_resource(&iomem_resource, &extension_rom_resource); upper = extension_rom_resource.start; diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index cf39bc08ce08..0c688232aab3 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c @@ -212,7 +212,7 @@ static int bsr_add_node(struct device_node *bn) cur->bsr_minor = i + total_bsr_devs; cur->bsr_addr = res.start; - cur->bsr_len = res.end - res.start + 1; + cur->bsr_len = resource_size(&res); cur->bsr_bytes = bsr_bytes[i]; cur->bsr_stride = bsr_stride[i]; cur->bsr_dev = MKDEV(bsr_major, i + total_bsr_devs); diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index 39ccdeada791..e90e1c74fd4c 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -621,7 +621,7 @@ static int __devinit hwicap_setup(struct device *dev, int id, drvdata->mem_start = regs_res->start; drvdata->mem_end = regs_res->end; - drvdata->mem_size = regs_res->end - regs_res->start + 1; + drvdata->mem_size = resource_size(regs_res); if (!request_mem_region(drvdata->mem_start, drvdata->mem_size, DRIVER_NAME)) { diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 954e334e01bb..06f9f27dbe7c 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -1304,8 +1304,7 @@ static int mv_xor_shared_probe(struct platform_device *pdev) if (!res) return -ENODEV; - msp->xor_base = devm_ioremap(&pdev->dev, res->start, - res->end - res->start + 1); + msp->xor_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!msp->xor_base) return -EBUSY; @@ -1314,7 +1313,7 @@ static int mv_xor_shared_probe(struct platform_device *pdev) return -ENODEV; msp->xor_high_base = devm_ioremap(&pdev->dev, res->start, - res->end - res->start + 1); + resource_size(res)); if (!msp->xor_high_base) return -EBUSY; diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c index db1df59ae2b6..9a6a274e6925 100644 --- a/drivers/edac/cell_edac.c +++ b/drivers/edac/cell_edac.c @@ -140,7 +140,7 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci) if (of_node_to_nid(np) != priv->node) continue; csrow->first_page = r.start >> PAGE_SHIFT; - csrow->nr_pages = (r.end - r.start + 1) >> PAGE_SHIFT; + csrow->nr_pages = resource_size(&r) >> PAGE_SHIFT; csrow->last_page = csrow->first_page + csrow->nr_pages - 1; csrow->mtype = MEM_XDR; csrow->edac_mode = EDAC_SECDED; diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index 38ab8e2cd7f4..11e1a5dad96f 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -538,15 +538,15 @@ static int __devinit mpc85xx_l2_err_probe(struct platform_device *op) /* we only need the error registers */ r.start += 0xe00; - if (!devm_request_mem_region(&op->dev, r.start, - r.end - r.start + 1, pdata->name)) { + if (!devm_request_mem_region(&op->dev, r.start, resource_size(&r), + pdata->name)) { printk(KERN_ERR "%s: Error while requesting mem region\n", __func__); res = -EBUSY; goto err; } - pdata->l2_vbase = devm_ioremap(&op->dev, r.start, r.end - r.start + 1); + pdata->l2_vbase = devm_ioremap(&op->dev, r.start, resource_size(&r)); if (!pdata->l2_vbase) { printk(KERN_ERR "%s: Unable to setup L2 err regs\n", __func__); res = -ENOMEM; @@ -987,15 +987,15 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op) goto err; } - if (!devm_request_mem_region(&op->dev, r.start, - r.end - r.start + 1, pdata->name)) { + if (!devm_request_mem_region(&op->dev, r.start, resource_size(&r), + pdata->name)) { printk(KERN_ERR "%s: Error while requesting mem region\n", __func__); res = -EBUSY; goto err; } - pdata->mc_vbase = devm_ioremap(&op->dev, r.start, r.end - r.start + 1); + pdata->mc_vbase = devm_ioremap(&op->dev, r.start, resource_size(&r)); if (!pdata->mc_vbase) { printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__); res = -ENOMEM; diff --git a/drivers/gpio/gpio-u300.c b/drivers/gpio/gpio-u300.c index d92790140fe5..1a86fefd0f83 100644 --- a/drivers/gpio/gpio-u300.c +++ b/drivers/gpio/gpio-u300.c @@ -581,8 +581,8 @@ static int __init gpio_probe(struct platform_device *pdev) if (!memres) goto err_no_resource; - if (request_mem_region(memres->start, memres->end - memres->start, "GPIO Controller") - == NULL) { + if (!request_mem_region(memres->start, resource_size(memres), + "GPIO Controller")) { err = -ENODEV; goto err_no_ioregion; } @@ -640,7 +640,7 @@ static int __init gpio_probe(struct platform_device *pdev) free_irq(gpio_ports[i].irq, &gpio_ports[i]); iounmap(virtbase); err_no_ioremap: - release_mem_region(memres->start, memres->end - memres->start); + release_mem_region(memres->start, resource_size(memres)); err_no_ioregion: err_no_resource: clk_disable(clk); @@ -660,7 +660,7 @@ static int __exit gpio_remove(struct platform_device *pdev) for (i = 0 ; i < U300_GPIO_NUM_PORTS; i++) free_irq(gpio_ports[i].irq, &gpio_ports[i]); iounmap(virtbase); - release_mem_region(memres->start, memres->end - memres->start); + release_mem_region(memres->start, resource_size(memres)); clk_disable(clk); clk_put(clk); return 0; diff --git a/drivers/ide/palm_bk3710.c b/drivers/ide/palm_bk3710.c index 9e8f4e1b0cc9..712c7904d03e 100644 --- a/drivers/ide/palm_bk3710.c +++ b/drivers/ide/palm_bk3710.c @@ -342,7 +342,7 @@ static int __init palm_bk3710_probe(struct platform_device *pdev) return -ENODEV; } - mem_size = mem->end - mem->start + 1; + mem_size = resource_size(mem); if (request_mem_region(mem->start, mem_size, "palm_bk3710") == NULL) { printk(KERN_ERR "failed to request memory region\n"); return -EBUSY; diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index bed3e39aac96..71c231954972 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -551,10 +551,10 @@ static int __init tx4939ide_probe(struct platform_device *pdev) return -ENODEV; if (!devm_request_mem_region(&pdev->dev, res->start, - res->end - res->start + 1, "tx4938ide")) + resource_size(res), "tx4938ide")) return -EBUSY; mapbase = (unsigned long)devm_ioremap(&pdev->dev, res->start, - res->end - res->start + 1); + resource_size(res)); if (!mapbase) return -EBUSY; memset(&hw, 0, sizeof(hw)); diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c index d55874e5d1c2..44fc8b4bcd81 100644 --- a/drivers/input/serio/sa1111ps2.c +++ b/drivers/input/serio/sa1111ps2.c @@ -300,8 +300,7 @@ static int __devinit ps2_probe(struct sa1111_dev *dev) out: sa1111_disable_device(ps2if->dev); - release_mem_region(dev->res.start, - dev->res.end - dev->res.start + 1); + release_mem_region(dev->res.start, resource_size(&dev->res)); free: sa1111_set_drvdata(dev, NULL); kfree(ps2if); @@ -317,8 +316,7 @@ static int __devexit ps2_remove(struct sa1111_dev *dev) struct ps2if *ps2if = sa1111_get_drvdata(dev); serio_unregister_port(ps2if->io); - release_mem_region(dev->res.start, - dev->res.end - dev->res.start + 1); + release_mem_region(dev->res.start, resource_size(&dev->res)); sa1111_set_drvdata(dev, NULL); kfree(ps2if); diff --git a/drivers/media/video/davinci/vpif.c b/drivers/media/video/davinci/vpif.c index 9f3bfc1eb240..af9680273ff9 100644 --- a/drivers/media/video/davinci/vpif.c +++ b/drivers/media/video/davinci/vpif.c @@ -422,7 +422,7 @@ static int __init vpif_probe(struct platform_device *pdev) if (!res) return -ENOENT; - res_len = res->end - res->start + 1; + res_len = resource_size(res); res = request_mem_region(res->start, res_len, res->name); if (!res) diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c index f6626e87dbc5..69b60ba5dd7a 100644 --- a/drivers/media/video/omap24xxcam.c +++ b/drivers/media/video/omap24xxcam.c @@ -1768,14 +1768,13 @@ static int __devinit omap24xxcam_probe(struct platform_device *pdev) dev_err(cam->dev, "no mem resource?\n"); goto err; } - if (!request_mem_region(mem->start, (mem->end - mem->start) + 1, - pdev->name)) { + if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { dev_err(cam->dev, "cannot reserve camera register I/O region\n"); goto err; } cam->mmio_base_phys = mem->start; - cam->mmio_size = (mem->end - mem->start) + 1; + cam->mmio_size = resource_size(mem); /* map the region */ cam->mmio_base = (unsigned long) diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c index 090d2a3a6548..a8c08f332da0 100644 --- a/drivers/message/i2o/iop.c +++ b/drivers/message/i2o/iop.c @@ -681,11 +681,11 @@ static int i2o_iop_systab_set(struct i2o_controller *c) if (root && allocate_resource(root, res, sb->desired_mem_size, sb->desired_mem_size, sb->desired_mem_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ NULL, NULL) >= 0) { c->mem_alloc = 1; - sb->current_mem_size = 1 + res->end - res->start; + sb->current_mem_size = resource_size(res); sb->current_mem_base = res->start; osm_info("%s: allocated %llu bytes of PCI memory at " "0x%016llX.\n", c->name, - (unsigned long long)(1 + res->end - res->start), + (unsigned long long)resource_size(res), (unsigned long long)res->start); } } @@ -703,11 +703,11 @@ static int i2o_iop_systab_set(struct i2o_controller *c) if (root && allocate_resource(root, res, sb->desired_io_size, sb->desired_io_size, sb->desired_io_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ NULL, NULL) >= 0) { c->io_alloc = 1; - sb->current_io_size = 1 + res->end - res->start; + sb->current_io_size = resource_size(res); sb->current_mem_base = res->start; osm_info("%s: allocated %llu bytes of PCI I/O at " "0x%016llX.\n", c->name, - (unsigned long long)(1 + res->end - res->start), + (unsigned long long)resource_size(res), (unsigned long long)res->start); } } diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c index ad715bf49cac..71bc835324d8 100644 --- a/drivers/mfd/tc6387xb.c +++ b/drivers/mfd/tc6387xb.c @@ -177,7 +177,7 @@ static int __devinit tc6387xb_probe(struct platform_device *dev) if (ret) goto err_resource; - tc6387xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1); + tc6387xb->scr = ioremap(rscr->start, resource_size(rscr)); if (!tc6387xb->scr) { ret = -ENOMEM; goto err_ioremap; diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 4afffe610f99..769a4e8e10dc 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -95,7 +95,7 @@ static int __init ssc_probe(struct platform_device *pdev) } ssc->pdev = pdev; - ssc->regs = ioremap(regs->start, regs->end - regs->start + 1); + ssc->regs = ioremap(regs->start, resource_size(regs)); if (!ssc->regs) { dev_dbg(&pdev->dev, "ioremap failed\n"); retval = -EINVAL; diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c index 0f3fb4f03bdf..28f5aaa19d4a 100644 --- a/drivers/misc/atmel_pwm.c +++ b/drivers/misc/atmel_pwm.c @@ -329,7 +329,7 @@ static int __init pwm_probe(struct platform_device *pdev) p->pdev = pdev; p->mask = *mp; p->irq = irq; - p->base = ioremap(r->start, r->end - r->start + 1); + p->base = ioremap(r->start, resource_size(r)); if (!p->base) goto fail; p->clk = clk_get(&pdev->dev, "pwm_clk"); diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 66dcddb9c205..2a069f908b27 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1595,7 +1595,7 @@ static int dw_mci_probe(struct platform_device *pdev) INIT_LIST_HEAD(&host->queue); ret = -ENOMEM; - host->regs = ioremap(regs->start, regs->end - regs->start + 1); + host->regs = ioremap(regs->start, resource_size(regs)); if (!host->regs) goto err_freehost; diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index d4297a97e100..67815eed2f00 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c @@ -142,7 +142,7 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev) state->map.write = bfin_flash_write; state->map.copy_to = bfin_flash_copy_to; state->map.bankwidth = pdata->width; - state->map.size = memory->end - memory->start + 1; + state->map.size = resource_size(memory); state->map.virt = (void __iomem *)memory->start; state->map.phys = memory->start; state->map.map_priv_1 = (unsigned long)state; diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index c00b9175ba9e..1594a802631d 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -155,7 +155,7 @@ static int ixp2000_flash_probe(struct platform_device *dev) if (!plat) return -ENODEV; - window_size = dev->resource->end - dev->resource->start + 1; + window_size = resource_size(dev->resource); dev_info(&dev->dev, "Probe of IXP2000 flash(%d banks x %dMiB)\n", ixp_data->nr_banks, ((u32)window_size >> 20)); @@ -194,16 +194,17 @@ static int ixp2000_flash_probe(struct platform_device *dev) info->map.copy_to = ixp2000_flash_copy_to; info->res = request_mem_region(dev->resource->start, - dev->resource->end - dev->resource->start + 1, - dev_name(&dev->dev)); + resource_size(dev->resource), + dev_name(&dev->dev)); if (!info->res) { dev_err(&dev->dev, "Could not reserve memory region\n"); err = -ENOMEM; goto Error; } - info->map.map_priv_1 = (unsigned long) ioremap(dev->resource->start, - dev->resource->end - dev->resource->start + 1); + info->map.map_priv_1 = + (unsigned long)ioremap(dev->resource->start, + resource_size(dev->resource)); if (!info->map.map_priv_1) { dev_err(&dev->dev, "Failed to ioremap flash region\n"); err = -EIO; diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index f59d62f74d44..7ae137d4b998 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -70,7 +70,7 @@ static int __devinit pxa2xx_flash_probe(struct platform_device *pdev) info->map.name = (char *) flash->name; info->map.bankwidth = flash->width; info->map.phys = res->start; - info->map.size = res->end - res->start + 1; + info->map.size = resource_size(res); info->parts = flash->parts; info->nr_parts = flash->nr_parts; diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index b300705d41cb..d4ba1f218e9a 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -513,7 +513,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) host->io_phys = (dma_addr_t)mem->start; - host->io_base = ioremap(mem->start, mem->end - mem->start + 1); + host->io_base = ioremap(mem->start, resource_size(mem)); if (host->io_base == NULL) { printk(KERN_ERR "atmel_nand: ioremap failed\n"); res = -EIO; @@ -547,7 +547,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) if (no_ecc) nand_chip->ecc.mode = NAND_ECC_NONE; if (hard_ecc && regs) { - host->ecc = ioremap(regs->start, regs->end - regs->start + 1); + host->ecc = ioremap(regs->start, resource_size(regs)); if (host->ecc == NULL) { printk(KERN_ERR "atmel_nand: ioremap failed\n"); res = -EIO; diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index 9ec280738a9a..8c569e454dc5 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -380,7 +380,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) return -ENXIO; /* map physical address */ - bcm_umi_io_base = ioremap(r->start, r->end - r->start + 1); + bcm_umi_io_base = ioremap(r->start, resource_size(r)); if (!bcm_umi_io_base) { printk(KERN_ERR "ioremap to access BCM UMI NAND chip failed\n"); diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 2f7c930872f9..eb1fbac63eb6 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -713,7 +713,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op) } regs_paddr = res.start; - regs_size = res.end - res.start + 1; + regs_size = resource_size(&res); if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) { dev_err(dev, "Error requesting memory region!\n"); diff --git a/drivers/net/bcm63xx_enet.c b/drivers/net/bcm63xx_enet.c index f1573d492e90..85045cde3126 100644 --- a/drivers/net/bcm63xx_enet.c +++ b/drivers/net/bcm63xx_enet.c @@ -1646,7 +1646,7 @@ static int __devinit bcm_enet_probe(struct platform_device *pdev) if (ret) goto out; - iomem_size = res_mem->end - res_mem->start + 1; + iomem_size = resource_size(res_mem); if (!request_mem_region(res_mem->start, iomem_size, "bcm63xx_enet")) { ret = -EBUSY; goto out; @@ -1861,7 +1861,7 @@ static int __devexit bcm_enet_remove(struct platform_device *pdev) /* release device resources */ iounmap(priv->base); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); /* disable hw block clocks */ if (priv->phy_clk) { @@ -1897,7 +1897,7 @@ static int __devinit bcm_enet_shared_probe(struct platform_device *pdev) if (!res) return -ENODEV; - iomem_size = res->end - res->start + 1; + iomem_size = resource_size(res); if (!request_mem_region(res->start, iomem_size, "bcm63xx_enet_dma")) return -EBUSY; @@ -1915,7 +1915,7 @@ static int __devexit bcm_enet_shared_remove(struct platform_device *pdev) iounmap(bcm_enet_shared_base); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); return 0; } diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c index 60a49e5a2a53..f76e88e74285 100644 --- a/drivers/net/can/softing/softing_main.c +++ b/drivers/net/can/softing/softing_main.c @@ -799,7 +799,7 @@ static __devinit int softing_pdev_probe(struct platform_device *pdev) if (!pres) goto platform_resource_failed; card->dpram_phys = pres->start; - card->dpram_size = pres->end - pres->start + 1; + card->dpram_size = resource_size(pres); card->dpram = ioremap_nocache(card->dpram_phys, card->dpram_size); if (!card->dpram) { dev_alert(&card->pdev->dev, "dpram ioremap failed\n"); diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index dcc4a170b0f3..c35ba5fba8f6 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -1821,7 +1821,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) } priv->emac_base_phys = res->start + pdata->ctrl_reg_offset; - size = res->end - res->start + 1; + size = resource_size(res); if (!request_mem_region(res->start, size, ndev->name)) { dev_err(&pdev->dev, "failed request_mem_region() for regs\n"); rc = -ENXIO; @@ -1926,7 +1926,7 @@ no_irq_res: cpdma_ctlr_destroy(priv->dma); no_dma: res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); iounmap(priv->remap_addr); probe_quit: @@ -1960,7 +1960,7 @@ static int __devexit davinci_emac_remove(struct platform_device *pdev) cpdma_chan_destroy(priv->rxchan); cpdma_ctlr_destroy(priv->dma); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); unregister_netdev(ndev); iounmap(priv->remap_addr); diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index a83dd312c3ac..15e4a71fbf1e 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -965,7 +965,7 @@ static int __devinit ethoc_probe(struct platform_device *pdev) priv = netdev_priv(netdev); priv->netdev = netdev; priv->dma_alloc = 0; - priv->io_region_size = mmio->end - mmio->start + 1; + priv->io_region_size = resource_size(mmio); priv->iobase = devm_ioremap_nocache(&pdev->dev, netdev->base_addr, resource_size(mmio)); diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c index 9f81b1ac130e..fe57eee8a679 100644 --- a/drivers/net/fec_mpc52xx.c +++ b/drivers/net/fec_mpc52xx.c @@ -867,10 +867,11 @@ static int __devinit mpc52xx_fec_probe(struct platform_device *op) "Error while parsing device node resource\n" ); goto err_netdev; } - if ((mem.end - mem.start + 1) < sizeof(struct mpc52xx_fec)) { + if (resource_size(&mem) < sizeof(struct mpc52xx_fec)) { printk(KERN_ERR DRIVER_NAME - " - invalid resource size (%lx < %x), check mpc52xx_devices.c\n", - (unsigned long)(mem.end - mem.start + 1), sizeof(struct mpc52xx_fec)); + " - invalid resource size (%lx < %x), check mpc52xx_devices.c\n", + (unsigned long)resource_size(&mem), + sizeof(struct mpc52xx_fec)); rv = -EINVAL; goto err_netdev; } diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/fs_enet/mii-bitbang.c index ad2975440719..b09270b5d0a5 100644 --- a/drivers/net/fs_enet/mii-bitbang.c +++ b/drivers/net/fs_enet/mii-bitbang.c @@ -120,7 +120,7 @@ static int __devinit fs_mii_bitbang_init(struct mii_bus *bus, if (ret) return ret; - if (res.end - res.start < 13) + if (resource_size(&res) <= 13) return -ENODEV; /* This should really encode the pin number as well, but all @@ -139,7 +139,7 @@ static int __devinit fs_mii_bitbang_init(struct mii_bus *bus, return -ENODEV; mdc_pin = *data; - bitbang->dir = ioremap(res.start, res.end - res.start + 1); + bitbang->dir = ioremap(res.start, resource_size(&res)); if (!bitbang->dir) return -ENOMEM; diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c index 6a2e150e75bb..e0e9d6c35d83 100644 --- a/drivers/net/fs_enet/mii-fec.c +++ b/drivers/net/fs_enet/mii-fec.c @@ -136,7 +136,7 @@ static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev) snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", res.start); - fec->fecp = ioremap(res.start, res.end - res.start + 1); + fec->fecp = ioremap(res.start, resource_size(&res)); if (!fec->fecp) goto out_fec; diff --git a/drivers/net/gianfar_ptp.c b/drivers/net/gianfar_ptp.c index d8e175382d1d..1c97861596f0 100644 --- a/drivers/net/gianfar_ptp.c +++ b/drivers/net/gianfar_ptp.c @@ -491,7 +491,7 @@ static int gianfar_ptp_probe(struct platform_device *dev) spin_lock_init(&etsects->lock); etsects->regs = ioremap(etsects->rsrc->start, - 1 + etsects->rsrc->end - etsects->rsrc->start); + resource_size(etsects->rsrc)); if (!etsects->regs) { pr_err("ioremap ptp registers failed\n"); goto no_ioremap; diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c index 079450fe5e96..725399ea0690 100644 --- a/drivers/net/ibm_newemac/core.c +++ b/drivers/net/ibm_newemac/core.c @@ -2770,7 +2770,7 @@ static int __devinit emac_probe(struct platform_device *ofdev) } // TODO : request_mem_region dev->emacp = ioremap(dev->rsrc_regs.start, - dev->rsrc_regs.end - dev->rsrc_regs.start + 1); + resource_size(&dev->rsrc_regs)); if (dev->emacp == NULL) { printk(KERN_ERR "%s: Can't map device registers!\n", np->full_name); diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 6c6a02869dfc..27125cdd7e01 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -1169,7 +1169,7 @@ static int __init macb_probe(struct platform_device *pdev) clk_enable(bp->hclk); #endif - bp->regs = ioremap(regs->start, regs->end - regs->start + 1); + bp->regs = ioremap(regs->start, resource_size(regs)); if (!bp->regs) { dev_err(&pdev->dev, "failed to map registers, aborting.\n"); err = -ENOMEM; diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index a5d9b1c310b3..b75648257381 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -2593,7 +2593,7 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) if (msp == NULL) goto out; - msp->base = ioremap(res->start, res->end - res->start + 1); + msp->base = ioremap(res->start, resource_size(res)); if (msp->base == NULL) goto out_free; diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c index 89f7540d90f9..df1292eb9c2e 100644 --- a/drivers/net/pxa168_eth.c +++ b/drivers/net/pxa168_eth.c @@ -1502,7 +1502,7 @@ static int pxa168_eth_probe(struct platform_device *pdev) err = -ENODEV; goto err_netdev; } - pep->base = ioremap(res->start, res->end - res->start + 1); + pep->base = ioremap(res->start, resource_size(res)); if (pep->base == NULL) { err = -ENOMEM; goto err_netdev; diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c index 68d50429ddf3..ea65f7ec360a 100644 --- a/drivers/net/sb1250-mac.c +++ b/drivers/net/sb1250-mac.c @@ -2597,7 +2597,7 @@ static int __devinit sbmac_probe(struct platform_device *pldev) res = platform_get_resource(pldev, IORESOURCE_MEM, 0); BUG_ON(!res); - sbm_base = ioremap_nocache(res->start, res->end - res->start + 1); + sbm_base = ioremap_nocache(res->start, resource_size(res)); if (!sbm_base) { printk(KERN_ERR "%s: unable to map device registers\n", dev_name(&pldev->dev)); diff --git a/drivers/parport/parport_ax88796.c b/drivers/parport/parport_ax88796.c index 2c5ac2bf5c56..844f6137970a 100644 --- a/drivers/parport/parport_ax88796.c +++ b/drivers/parport/parport_ax88796.c @@ -293,7 +293,7 @@ static int parport_ax88796_probe(struct platform_device *pdev) goto exit_mem; } - size = (res->end - res->start) + 1; + size = resource_size(res); spacing = size / 3; dd->io = request_mem_region(res->start, size, pdev->name); diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c index 071b7dc0094b..efa30da1ae8f 100644 --- a/drivers/pci/hotplug/shpchp_sysfs.c +++ b/drivers/pci/hotplug/shpchp_sysfs.c @@ -50,29 +50,26 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha pci_bus_for_each_resource(bus, res, index) { if (res && (res->flags & IORESOURCE_MEM) && !(res->flags & IORESOURCE_PREFETCH)) { - out += sprintf(out, "start = %8.8llx, " - "length = %8.8llx\n", - (unsigned long long)res->start, - (unsigned long long)(res->end - res->start)); + out += sprintf(out, "start = %8.8llx, length = %8.8llx\n", + (unsigned long long)res->start, + (unsigned long long)resource_size(res)); } } out += sprintf(out, "Free resources: prefetchable memory\n"); pci_bus_for_each_resource(bus, res, index) { if (res && (res->flags & IORESOURCE_MEM) && (res->flags & IORESOURCE_PREFETCH)) { - out += sprintf(out, "start = %8.8llx, " - "length = %8.8llx\n", - (unsigned long long)res->start, - (unsigned long long)(res->end - res->start)); + out += sprintf(out, "start = %8.8llx, length = %8.8llx\n", + (unsigned long long)res->start, + (unsigned long long)resource_size(res)); } } out += sprintf(out, "Free resources: IO\n"); pci_bus_for_each_resource(bus, res, index) { if (res && (res->flags & IORESOURCE_IO)) { - out += sprintf(out, "start = %8.8llx, " - "length = %8.8llx\n", - (unsigned long long)res->start, - (unsigned long long)(res->end - res->start)); + out += sprintf(out, "start = %8.8llx, length = %8.8llx\n", + (unsigned long long)res->start, + (unsigned long long)resource_size(res)); } } out += sprintf(out, "Free resources: bus numbers\n"); diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index fb33fa42d249..4902206f53d9 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -283,8 +283,7 @@ static int __init at91_cf_probe(struct platform_device *pdev) } /* reserve chip-select regions */ - if (!request_mem_region(io->start, io->end + 1 - io->start, - driver_name)) { + if (!request_mem_region(io->start, resource_size(io), driver_name)) { status = -ENXIO; goto fail1; } @@ -308,7 +307,7 @@ static int __init at91_cf_probe(struct platform_device *pdev) return 0; fail2: - release_mem_region(io->start, io->end + 1 - io->start); + release_mem_region(io->start, resource_size(io)); fail1: if (cf->socket.io_offset) iounmap((void __iomem *) cf->socket.io_offset); @@ -339,7 +338,7 @@ static int __exit at91_cf_remove(struct platform_device *pdev) struct resource *io = cf->socket.io[0].res; pcmcia_unregister_socket(&cf->socket); - release_mem_region(io->start, io->end + 1 - io->start); + release_mem_region(io->start, resource_size(io)); iounmap((void __iomem *) cf->socket.io_offset); if (board->irq_pin) { free_irq(board->irq_pin, cf); diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c index 6defd4a8168e..06ad3e5e7d3d 100644 --- a/drivers/pcmcia/electra_cf.c +++ b/drivers/pcmcia/electra_cf.c @@ -209,9 +209,9 @@ static int __devinit electra_cf_probe(struct platform_device *ofdev) cf->ofdev = ofdev; cf->mem_phys = mem.start; - cf->mem_size = PAGE_ALIGN(mem.end - mem.start); + cf->mem_size = PAGE_ALIGN(resource_size(&mem)); cf->mem_base = ioremap(cf->mem_phys, cf->mem_size); - cf->io_size = PAGE_ALIGN(io.end - io.start); + cf->io_size = PAGE_ALIGN(resource_size(&io)); area = __get_vm_area(cf->io_size, 0, PHB_IO_BASE, PHB_IO_END); if (area == NULL) diff --git a/drivers/pcmcia/rsrc_iodyn.c b/drivers/pcmcia/rsrc_iodyn.c index 523eb691c30b..f53c237bda2f 100644 --- a/drivers/pcmcia/rsrc_iodyn.c +++ b/drivers/pcmcia/rsrc_iodyn.c @@ -135,7 +135,7 @@ static int iodyn_find_io(struct pcmcia_socket *s, unsigned int attr, try = res->end + 1; if ((*base == 0) || (*base == try)) { if (adjust_resource(s->io[i].res, res->start, - res->end - res->start + num + 1)) + resource_size(res) + num)) continue; *base = try; s->io[i].InUse += num; @@ -147,8 +147,8 @@ static int iodyn_find_io(struct pcmcia_socket *s, unsigned int attr, try = res->start - num; if ((*base == 0) || (*base == try)) { if (adjust_resource(s->io[i].res, - res->start - num, - res->end - res->start + num + 1)) + res->start - num, + resource_size(res) + num)) continue; *base = try; s->io[i].InUse += num; diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index b187555d4388..9da9656242af 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -770,7 +770,7 @@ static int nonstatic_find_io(struct pcmcia_socket *s, unsigned int attr, res->end + num); if (!ret) { ret = adjust_resource(s->io[i].res, res->start, - res->end - res->start + num + 1); + resource_size(res) + num); if (ret) continue; *base = try; @@ -788,8 +788,8 @@ static int nonstatic_find_io(struct pcmcia_socket *s, unsigned int attr, res->end); if (!ret) { ret = adjust_resource(s->io[i].res, - res->start - num, - res->end - res->start + num + 1); + res->start - num, + resource_size(res) + num); if (ret) continue; *base = try; diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 100e4d9372f1..1a6937d9118f 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -1018,7 +1018,7 @@ static void pnpacpi_encode_io(struct pnp_dev *dev, io->minimum = p->start; io->maximum = p->end; io->alignment = 0; /* Correct? */ - io->address_length = p->end - p->start + 1; + io->address_length = resource_size(p); } else { io->minimum = 0; io->address_length = 0; @@ -1036,7 +1036,7 @@ static void pnpacpi_encode_fixed_io(struct pnp_dev *dev, if (pnp_resource_enabled(p)) { fixed_io->address = p->start; - fixed_io->address_length = p->end - p->start + 1; + fixed_io->address_length = resource_size(p); } else { fixed_io->address = 0; fixed_io->address_length = 0; @@ -1059,7 +1059,7 @@ static void pnpacpi_encode_mem24(struct pnp_dev *dev, memory24->minimum = p->start; memory24->maximum = p->end; memory24->alignment = 0; - memory24->address_length = p->end - p->start + 1; + memory24->address_length = resource_size(p); } else { memory24->minimum = 0; memory24->address_length = 0; @@ -1083,7 +1083,7 @@ static void pnpacpi_encode_mem32(struct pnp_dev *dev, memory32->minimum = p->start; memory32->maximum = p->end; memory32->alignment = 0; - memory32->address_length = p->end - p->start + 1; + memory32->address_length = resource_size(p); } else { memory32->minimum = 0; memory32->alignment = 0; @@ -1106,7 +1106,7 @@ static void pnpacpi_encode_fixed_mem32(struct pnp_dev *dev, p->flags & IORESOURCE_MEM_WRITEABLE ? ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; fixed_memory32->address = p->start; - fixed_memory32->address_length = p->end - p->start + 1; + fixed_memory32->address_length = resource_size(p); } else { fixed_memory32->address = 0; fixed_memory32->address_length = 0; diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c index cb1f47bfee96..cca2f9f9f3e3 100644 --- a/drivers/pnp/pnpbios/rsparser.c +++ b/drivers/pnp/pnpbios/rsparser.c @@ -505,7 +505,7 @@ static void pnpbios_encode_mem(struct pnp_dev *dev, unsigned char *p, if (pnp_resource_enabled(res)) { base = res->start; - len = res->end - res->start + 1; + len = resource_size(res); } else { base = 0; len = 0; @@ -529,7 +529,7 @@ static void pnpbios_encode_mem32(struct pnp_dev *dev, unsigned char *p, if (pnp_resource_enabled(res)) { base = res->start; - len = res->end - res->start + 1; + len = resource_size(res); } else { base = 0; len = 0; @@ -559,7 +559,7 @@ static void pnpbios_encode_fixed_mem32(struct pnp_dev *dev, unsigned char *p, if (pnp_resource_enabled(res)) { base = res->start; - len = res->end - res->start + 1; + len = resource_size(res); } else { base = 0; len = 0; @@ -617,7 +617,7 @@ static void pnpbios_encode_port(struct pnp_dev *dev, unsigned char *p, if (pnp_resource_enabled(res)) { base = res->start; - len = res->end - res->start + 1; + len = resource_size(res); } else { base = 0; len = 0; @@ -636,11 +636,11 @@ static void pnpbios_encode_fixed_port(struct pnp_dev *dev, unsigned char *p, struct resource *res) { unsigned long base = res->start; - unsigned long len = res->end - res->start + 1; + unsigned long len = resource_size(res); if (pnp_resource_enabled(res)) { base = res->start; - len = res->end - res->start + 1; + len = resource_size(res); } else { base = 0; len = 0; diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c index e725d51e773d..8dd08305aae1 100644 --- a/drivers/rtc/rtc-at32ap700x.c +++ b/drivers/rtc/rtc-at32ap700x.c @@ -223,7 +223,7 @@ static int __init at32_rtc_probe(struct platform_device *pdev) } rtc->irq = irq; - rtc->regs = ioremap(regs->start, regs->end - regs->start + 1); + rtc->regs = ioremap(regs->start, resource_size(regs)); if (!rtc->regs) { ret = -ENOMEM; dev_dbg(&pdev->dev, "could not map I/O memory\n"); diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 911e75cdc125..05beb6c1ca79 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -606,7 +606,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) * (needing ioremap etc), not i/o space resources like this ... */ ports = request_region(ports->start, - ports->end + 1 - ports->start, + resource_size(ports), driver_name); if (!ports) { dev_dbg(dev, "i/o registers already in use\n"); @@ -750,7 +750,7 @@ cleanup1: cmos_rtc.dev = NULL; rtc_device_unregister(cmos_rtc.rtc); cleanup0: - release_region(ports->start, ports->end + 1 - ports->start); + release_region(ports->start, resource_size(ports)); return retval; } @@ -779,7 +779,7 @@ static void __exit cmos_do_remove(struct device *dev) cmos->rtc = NULL; ports = cmos->iomem; - release_region(ports->start, ports->end + 1 - ports->start); + release_region(ports->start, resource_size(ports)); cmos->iomem = NULL; cmos->dev = NULL; diff --git a/drivers/rtc/rtc-ds1286.c b/drivers/rtc/rtc-ds1286.c index 47e681df31e2..68e6caf25496 100644 --- a/drivers/rtc/rtc-ds1286.c +++ b/drivers/rtc/rtc-ds1286.c @@ -343,7 +343,7 @@ static int __devinit ds1286_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->size = res->end - res->start + 1; + priv->size = resource_size(res); if (!request_mem_region(res->start, priv->size, pdev->name)) { ret = -EBUSY; goto out; diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index fbabc773dded..568ad30617e7 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -490,7 +490,7 @@ ds1511_rtc_probe(struct platform_device *pdev) pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; - pdata->size = res->end - res->start + 1; + pdata->size = resource_size(res); if (!devm_request_mem_region(&pdev->dev, res->start, pdata->size, pdev->name)) return -EBUSY; diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c index 042630c90dd3..d84a448dd754 100644 --- a/drivers/rtc/rtc-ds1742.c +++ b/drivers/rtc/rtc-ds1742.c @@ -173,7 +173,7 @@ static int __devinit ds1742_rtc_probe(struct platform_device *pdev) pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; - pdata->size = res->end - res->start + 1; + pdata->size = resource_size(res); if (!devm_request_mem_region(&pdev->dev, res->start, pdata->size, pdev->name)) return -EBUSY; diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c index 7410875e5838..8e2a24e33ed6 100644 --- a/drivers/rtc/rtc-m48t35.c +++ b/drivers/rtc/rtc-m48t35.c @@ -154,7 +154,7 @@ static int __devinit m48t35_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->size = res->end - res->start + 1; + priv->size = resource_size(res); /* * kludge: remove the #ifndef after ioc3 resource * conflicts are resolved diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index 3978f4caf724..28365388fb6c 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -433,7 +433,7 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) if (!m48t59->ioaddr) { /* ioaddr not mapped externally */ - m48t59->ioaddr = ioremap(res->start, res->end - res->start + 1); + m48t59->ioaddr = ioremap(res->start, resource_size(res)); if (!m48t59->ioaddr) goto out; } diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c index 0cec5650d56a..d33544802a2e 100644 --- a/drivers/rtc/rtc-mrst.c +++ b/drivers/rtc/rtc-mrst.c @@ -332,9 +332,8 @@ vrtc_mrst_do_probe(struct device *dev, struct resource *iomem, int rtc_irq) if (!iomem) return -ENODEV; - iomem = request_mem_region(iomem->start, - iomem->end + 1 - iomem->start, - driver_name); + iomem = request_mem_region(iomem->start, resource_size(iomem), + driver_name); if (!iomem) { dev_dbg(dev, "i/o mem already in use.\n"); return -EBUSY; diff --git a/drivers/rtc/rtc-puv3.c b/drivers/rtc/rtc-puv3.c index 46f14b82f3ab..b3eba3cddd42 100644 --- a/drivers/rtc/rtc-puv3.c +++ b/drivers/rtc/rtc-puv3.c @@ -267,9 +267,8 @@ static int puv3_rtc_probe(struct platform_device *pdev) return -ENOENT; } - puv3_rtc_mem = request_mem_region(res->start, - res->end-res->start+1, - pdev->name); + puv3_rtc_mem = request_mem_region(res->start, resource_size(res), + pdev->name); if (puv3_rtc_mem == NULL) { dev_err(&pdev->dev, "failed to reserve memory region\n"); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 16512ecae31a..2a65e85e0f56 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -455,8 +455,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) return -ENOENT; } - s3c_rtc_mem = request_mem_region(res->start, - res->end-res->start+1, + s3c_rtc_mem = request_mem_region(res->start, resource_size(res), pdev->name); if (s3c_rtc_mem == NULL) { @@ -465,7 +464,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) goto err_nores; } - s3c_rtc_base = ioremap(res->start, res->end - res->start + 1); + s3c_rtc_base = ioremap(res->start, resource_size(res)); if (s3c_rtc_base == NULL) { dev_err(&pdev->dev, "failed ioremap()\n"); ret = -EINVAL; diff --git a/drivers/staging/generic_serial/ser_a2232.c b/drivers/staging/generic_serial/ser_a2232.c index 3f47c2ead8e5..0c08e1c25583 100644 --- a/drivers/staging/generic_serial/ser_a2232.c +++ b/drivers/staging/generic_serial/ser_a2232.c @@ -746,7 +746,8 @@ static int __init a2232board_init(void) zd_a2232[nr_a2232] = z; boardaddr = ZTWO_VADDR( z->resource.start ); - printk("Board is located at address 0x%x, size is 0x%x.\n", boardaddr, (unsigned int) ((z->resource.end+1) - (z->resource.start))); + printk("Board is located at address 0x%x, size is 0x%x\n", + boardaddr, (unsigned int)resource_size(&z->resource)); mem = (volatile struct a2232memory *) boardaddr; diff --git a/drivers/staging/gma500/psb_gtt.c b/drivers/staging/gma500/psb_gtt.c index 74c5a6569d08..280f9d445468 100644 --- a/drivers/staging/gma500/psb_gtt.c +++ b/drivers/staging/gma500/psb_gtt.c @@ -80,7 +80,7 @@ static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r) { struct drm_psb_private *dev_priv = dev->dev_private; u32 *gtt_slot, pte; - int numpages = (r->resource.end + 1 - r->resource.start) >> PAGE_SHIFT; + int numpages = resource_size(&r->resource) >> PAGE_SHIFT; struct page **pages; int i; @@ -121,7 +121,7 @@ static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r) { struct drm_psb_private *dev_priv = dev->dev_private; u32 *gtt_slot, pte; - int numpages = (r->resource.end + 1 - r->resource.start) >> PAGE_SHIFT; + int numpages = resource_size(&r->resource) >> PAGE_SHIFT; int i; WARN_ON(r->stolen); @@ -149,7 +149,7 @@ static int psb_gtt_attach_pages(struct gtt_range *gt) struct address_space *mapping; int i; struct page *p; - int pages = (gt->resource.end + 1 - gt->resource.start) >> PAGE_SHIFT; + int pages = resource_size(>->resource) >> PAGE_SHIFT; WARN_ON(gt->pages); @@ -191,7 +191,7 @@ err: static void psb_gtt_detach_pages(struct gtt_range *gt) { int i; - int pages = (gt->resource.end + 1 - gt->resource.start) >> PAGE_SHIFT; + int pages = resource_size(>->resource) >> PAGE_SHIFT; for (i = 0; i < pages; i++) { /* FIXME: do we need to force dirty */ diff --git a/drivers/tty/serial/bfin_5xx.c b/drivers/tty/serial/bfin_5xx.c index 9b1ff2b6bb37..ff6979181ac5 100644 --- a/drivers/tty/serial/bfin_5xx.c +++ b/drivers/tty/serial/bfin_5xx.c @@ -1304,8 +1304,7 @@ static int bfin_serial_probe(struct platform_device *pdev) goto out_error_free_peripherals; } - uart->port.membase = ioremap(res->start, - res->end - res->start); + uart->port.membase = ioremap(res->start, resource_size(res)); if (!uart->port.membase) { dev_err(&pdev->dev, "Cannot map uart IO\n"); ret = -ENXIO; @@ -1483,7 +1482,7 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev) } bfin_earlyprintk_port.port.membase = ioremap(res->start, - res->end - res->start); + resource_size(res)); if (!bfin_earlyprintk_port.port.membase) { dev_err(&pdev->dev, "Cannot map uart IO\n"); ret = -ENXIO; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index a54473123e0a..22fe801cce31 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -954,7 +954,7 @@ static void imx_release_port(struct uart_port *port) struct resource *mmres; mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mmres->start, mmres->end - mmres->start + 1); + release_mem_region(mmres->start, resource_size(mmres)); } /* @@ -970,8 +970,7 @@ static int imx_request_port(struct uart_port *port) if (!mmres) return -ENODEV; - ret = request_mem_region(mmres->start, mmres->end - mmres->start + 1, - "imx-uart"); + ret = request_mem_region(mmres->start, resource_size(mmres), "imx-uart"); return ret ? 0 : -EBUSY; } diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c index 84db7321cce8..8e07517f8acd 100644 --- a/drivers/tty/serial/m32r_sio.c +++ b/drivers/tty/serial/m32r_sio.c @@ -892,7 +892,7 @@ static int m32r_sio_request_port(struct uart_port *port) * If we have a mapbase, then request that as well. */ if (ret == 0 && up->port.flags & UPF_IOREMAP) { - int size = res->end - res->start + 1; + int size = resource_size(res); up->port.membase = ioremap(up->port.mapbase, size); if (!up->port.membase) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 47cadf474149..c37df8d0fa28 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1241,8 +1241,8 @@ static int serial_omap_probe(struct platform_device *pdev) return -ENODEV; } - if (!request_mem_region(mem->start, (mem->end - mem->start) + 1, - pdev->dev.driver->name)) { + if (!request_mem_region(mem->start, resource_size(mem), + pdev->dev.driver->name)) { dev_err(&pdev->dev, "memory region already claimed\n"); return -EBUSY; } @@ -1308,7 +1308,7 @@ err: dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", pdev->id, __func__, ret); do_release_region: - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); return ret; } diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 4302e6e3768e..531931c1b250 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -803,7 +803,7 @@ static int serial_pxa_probe(struct platform_device *dev) break; } - sport->port.membase = ioremap(mmres->start, mmres->end - mmres->start + 1); + sport->port.membase = ioremap(mmres->start, resource_size(mmres)); if (!sport->port.membase) { ret = -ENOMEM; goto err_clk; diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 92aa54550e84..ad0f8f5f6ea1 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1435,7 +1435,7 @@ static int __devinit su_probe(struct platform_device *op) rp = &op->resource[0]; up->port.mapbase = rp->start; - up->reg_size = (rp->end - rp->start) + 1; + up->reg_size = resource_size(rp); up->port.membase = of_ioremap(rp, 0, up->reg_size, "su"); if (!up->port.membase) { if (type != SU_PORT_PORT) diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 37fc4e3d487c..026cb9ea5cd1 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -573,8 +573,7 @@ static int __init vt8500_serial_probe(struct platform_device *pdev) snprintf(vt8500_port->name, sizeof(vt8500_port->name), "VT8500 UART%d", pdev->id); - vt8500_port->uart.membase = ioremap(mmres->start, - mmres->end - mmres->start + 1); + vt8500_port->uart.membase = ioremap(mmres->start, resource_size(mmres)); if (!vt8500_port->uart.membase) { ret = -ENOMEM; goto err; diff --git a/drivers/uio/uio_pdrv.c b/drivers/uio/uio_pdrv.c index 7d3e469b9904..bdc3db946122 100644 --- a/drivers/uio/uio_pdrv.c +++ b/drivers/uio/uio_pdrv.c @@ -58,7 +58,7 @@ static int uio_pdrv_probe(struct platform_device *pdev) uiomem->memtype = UIO_MEM_PHYS; uiomem->addr = r->start; - uiomem->size = r->end - r->start + 1; + uiomem->size = resource_size(r); ++uiomem; } diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 0f424af7f109..31e799d9efe5 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -137,7 +137,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) uiomem->memtype = UIO_MEM_PHYS; uiomem->addr = r->start; - uiomem->size = r->end - r->start + 1; + uiomem->size = resource_size(r); ++uiomem; } diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index db1a659702ba..f045c8968a6e 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -272,7 +272,7 @@ static void usba_init_debugfs(struct usba_udc *udc) regs_resource = platform_get_resource(udc->pdev, IORESOURCE_MEM, CTRL_IOMEM_ID); - regs->d_inode->i_size = regs_resource->end - regs_resource->start + 1; + regs->d_inode->i_size = resource_size(regs_resource); udc->debugfs_regs = regs; usba_ep_init_debugfs(udc, to_usba_ep(udc->gadget.ep0)); diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 2cd9a60c7f3a..9c8e56fd0ffe 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2445,7 +2445,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) } if (pdata->operating_mode == FSL_USB2_DR_DEVICE) { - if (!request_mem_region(res->start, res->end - res->start + 1, + if (!request_mem_region(res->start, resource_size(res), driver_name)) { ERR("request mem region for %s failed\n", pdev->name); ret = -EBUSY; @@ -2593,7 +2593,7 @@ err_iounmap_noclk: iounmap(dr_regs); err_release_mem_region: if (pdata->operating_mode == FSL_USB2_DR_DEVICE) - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); err_kfree: kfree(udc_controller); udc_controller = NULL; @@ -2628,7 +2628,7 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) free_irq(udc_controller->irq, udc_controller); iounmap(dr_regs); if (pdata->operating_mode == FSL_USB2_DR_DEVICE) - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); device_unregister(&udc_controller->gadget.dev); /* free udc --wait for the release() finished */ diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c index 98cc8a13169c..eab3d7059fbe 100644 --- a/drivers/usb/host/ehci-ath79.c +++ b/drivers/usb/host/ehci-ath79.c @@ -146,7 +146,7 @@ static int ehci_ath79_probe(struct platform_device *pdev) return -ENOMEM; hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_len = resource_size(res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { dev_dbg(&pdev->dev, "controller already in use\n"); diff --git a/drivers/usb/host/ehci-cns3xxx.c b/drivers/usb/host/ehci-cns3xxx.c index d41745c6f0c4..6536abdea6e6 100644 --- a/drivers/usb/host/ehci-cns3xxx.c +++ b/drivers/usb/host/ehci-cns3xxx.c @@ -107,7 +107,7 @@ static int cns3xxx_ehci_probe(struct platform_device *pdev) } hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_len = resource_size(res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, driver->description)) { diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index f380bf97e5af..34a3140d1e5f 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -100,7 +100,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, goto err2; } hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_len = resource_size(res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, driver->description)) { dev_dbg(&pdev->dev, "controller already in use\n"); diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index 93b230dc51a2..fdfd8c5b639b 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -130,7 +130,7 @@ static int __devinit ehci_hcd_grlib_probe(struct platform_device *op) return -ENOMEM; hcd->rsrc_start = res.start; - hcd->rsrc_len = res.end - res.start + 1; + hcd->rsrc_len = resource_size(&res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c index 50e600d26e28..c4460f3d009f 100644 --- a/drivers/usb/host/ehci-ixp4xx.c +++ b/drivers/usb/host/ehci-ixp4xx.c @@ -100,7 +100,7 @@ static int ixp4xx_ehci_probe(struct platform_device *pdev) goto fail_request_resource; } hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_len = resource_size(res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, driver->description)) { diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index ff55757ba7d8..c3ba3ed5f3a6 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -124,7 +124,7 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev) return -ENOMEM; hcd->rsrc_start = res_mem->start; - hcd->rsrc_len = res_mem->end - res_mem->start + 1; + hcd->rsrc_len = resource_size(res_mem); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, OCTEON_EHCI_HCD_NAME)) { diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c index cd69099cda19..e8d54de44acc 100644 --- a/drivers/usb/host/ehci-pmcmsp.c +++ b/drivers/usb/host/ehci-pmcmsp.c @@ -124,7 +124,7 @@ static int usb_hcd_msp_map_regs(struct mspusb_device *dev) res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (res == NULL) return -ENOMEM; - res_len = res->end - res->start + 1; + res_len = resource_size(res); if (!request_mem_region(res->start, res_len, "mab regs")) return -EBUSY; @@ -140,7 +140,7 @@ static int usb_hcd_msp_map_regs(struct mspusb_device *dev) retval = -ENOMEM; goto err2; } - res_len = res->end - res->start + 1; + res_len = resource_size(res); if (!request_mem_region(res->start, res_len, "usbid regs")) { retval = -EBUSY; goto err2; @@ -154,13 +154,13 @@ static int usb_hcd_msp_map_regs(struct mspusb_device *dev) return 0; err3: res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - res_len = res->end - res->start + 1; + res_len = resource_size(res); release_mem_region(res->start, res_len); err2: iounmap(dev->mab_regs); err1: res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - res_len = res->end - res->start + 1; + res_len = resource_size(res); release_mem_region(res->start, res_len); dev_err(&pdev->dev, "Failed to map non-EHCI regs.\n"); return retval; @@ -194,7 +194,7 @@ int usb_hcd_msp_probe(const struct hc_driver *driver, goto err1; } hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_len = resource_size(res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, dev->name)) { retval = -EBUSY; goto err1; diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 8552db6c29c9..41d11fe14252 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -130,7 +130,7 @@ static int __devinit ehci_hcd_ppc_of_probe(struct platform_device *op) return -ENOMEM; hcd->rsrc_start = res.start; - hcd->rsrc_len = res.end - res.start + 1; + hcd->rsrc_len = resource_size(&res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index 52a027aaa370..d661cf7de140 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -41,7 +41,7 @@ static int __devinit usb_w90x900_probe(const struct hc_driver *driver, } hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_len = resource_size(res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { retval = -EBUSY; diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index a64d6d66d760..32793ce3d9e9 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -174,7 +174,7 @@ static int __devinit ehci_hcd_xilinx_of_probe(struct platform_device *op) return -ENOMEM; hcd->rsrc_start = res.start; - hcd->rsrc_len = res.end - res.start + 1; + hcd->rsrc_len = resource_size(&res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 19223c7449e1..572ea53b0226 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -605,7 +605,7 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev) goto err_regs; } - hcd->regs = ioremap(usb_regs.start, usb_regs.end - usb_regs.start + 1); + hcd->regs = ioremap(usb_regs.start, resource_size(&usb_regs)); if (!hcd->regs) { dev_err(dev, "could not ioremap regs\n"); ret = -ENOMEM; diff --git a/drivers/usb/host/ohci-ath79.c b/drivers/usb/host/ohci-ath79.c index ffea3e7cb0a8..c620c50f6770 100644 --- a/drivers/usb/host/ohci-ath79.c +++ b/drivers/usb/host/ohci-ath79.c @@ -93,8 +93,8 @@ static int ohci_ath79_probe(struct platform_device *pdev) ret = -ENODEV; goto err_put_hcd; } - hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { dev_dbg(&pdev->dev, "controller already in use\n"); diff --git a/drivers/usb/host/ohci-cns3xxx.c b/drivers/usb/host/ohci-cns3xxx.c index f05ef87e934c..5a00a1e1c6ca 100644 --- a/drivers/usb/host/ohci-cns3xxx.c +++ b/drivers/usb/host/ohci-cns3xxx.c @@ -100,7 +100,7 @@ static int cns3xxx_ohci_probe(struct platform_device *pdev) goto err1; } hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_len = resource_size(res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, driver->description)) { diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index d22fb4d577b7..6aca2c4453f7 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -322,7 +322,7 @@ static int usb_hcd_da8xx_probe(const struct hc_driver *driver, goto err2; } hcd->rsrc_start = mem->start; - hcd->rsrc_len = mem->end - mem->start + 1; + hcd->rsrc_len = resource_size(mem); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { dev_dbg(&pdev->dev, "request_mem_region failed\n"); diff --git a/drivers/usb/host/ohci-octeon.c b/drivers/usb/host/ohci-octeon.c index e4ddfaf8870f..d8b45647d1dc 100644 --- a/drivers/usb/host/ohci-octeon.c +++ b/drivers/usb/host/ohci-octeon.c @@ -135,7 +135,7 @@ static int ohci_octeon_drv_probe(struct platform_device *pdev) return -ENOMEM; hcd->rsrc_start = res_mem->start; - hcd->rsrc_len = res_mem->end - res_mem->start + 1; + hcd->rsrc_len = resource_size(res_mem); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, OCTEON_OHCI_HCD_NAME)) { diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 1ca1821320f4..0c12f4e14dcd 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -110,7 +110,7 @@ static int __devinit ohci_hcd_ppc_of_probe(struct platform_device *op) return -ENOMEM; hcd->rsrc_start = res.start; - hcd->rsrc_len = res.end - res.start + 1; + hcd->rsrc_len = resource_size(&res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c index 89e670e38c10..c0f595c44487 100644 --- a/drivers/usb/host/ohci-ppc-soc.c +++ b/drivers/usb/host/ohci-ppc-soc.c @@ -56,7 +56,7 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver, if (!hcd) return -ENOMEM; hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_len = resource_size(res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { pr_debug("%s: request_mem_region failed\n", __FILE__); diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index d8eb3bdafabb..4204d9720d23 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -131,7 +131,7 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver, if (!hcd) return -ENOMEM; hcd->rsrc_start = dev->res.start; - hcd->rsrc_len = dev->res.end - dev->res.start + 1; + hcd->rsrc_len = resource_size(&dev->res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { dbg("request_mem_region failed"); diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index 041d30f30c10..78918ca0da23 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -103,8 +103,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) goto err0; } - if (!request_mem_region(mem->start, mem->end - mem->start + 1, - pdev->name)) { + if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { dev_err(dev, "request_mem_region failed\n"); retval = -EBUSY; goto err0; @@ -126,7 +125,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) if (!dma_declare_coherent_memory(dev, mem->start, mem->start - mem->parent->start, - (mem->end - mem->start) + 1, + resource_size(mem), DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE)) { dev_err(dev, "cannot declare coherent memory\n"); @@ -149,7 +148,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) } hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_len = resource_size(res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, pdev->name)) { dev_err(dev, "request_mem_region failed\n"); @@ -185,7 +184,7 @@ err3: err2: dma_release_declared_memory(dev); err1: - release_mem_region(mem->start, mem->end - mem->start + 1); + release_mem_region(mem->start, resource_size(mem)); err0: return retval; } @@ -201,7 +200,7 @@ static int ohci_hcd_sm501_drv_remove(struct platform_device *pdev) dma_release_declared_memory(&pdev->dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (mem) - release_mem_region(mem->start, mem->end - mem->start + 1); + release_mem_region(mem->start, resource_size(mem)); /* mask interrupts and disable power */ diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index 3558491dd87d..57ad1271fc9b 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -208,13 +208,13 @@ static int __devinit ohci_hcd_tmio_drv_probe(struct platform_device *dev) } hcd->rsrc_start = regs->start; - hcd->rsrc_len = regs->end - regs->start + 1; + hcd->rsrc_len = resource_size(regs); tmio = hcd_to_tmio(hcd); spin_lock_init(&tmio->lock); - tmio->ccr = ioremap(config->start, config->end - config->start + 1); + tmio->ccr = ioremap(config->start, resource_size(config)); if (!tmio->ccr) { ret = -ENOMEM; goto err_ioremap_ccr; @@ -228,7 +228,7 @@ static int __devinit ohci_hcd_tmio_drv_probe(struct platform_device *dev) if (!dma_declare_coherent_memory(&dev->dev, sram->start, sram->start, - sram->end - sram->start + 1, + resource_size(sram), DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE)) { ret = -EBUSY; goto err_dma_declare; diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index 5fbe997dc6df..dcd889803f0f 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -3828,7 +3828,7 @@ static int oxu_drv_probe(struct platform_device *pdev) return -ENODEV; } memstart = res->start; - memlen = res->end - res->start + 1; + memlen = resource_size(res); dev_dbg(&pdev->dev, "MEM resource %lx-%lx\n", memstart, memlen); if (!request_mem_region(memstart, memlen, oxu_hc_driver.description)) { diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c index d01c1e227681..f7a62138e3e0 100644 --- a/drivers/usb/host/uhci-grlib.c +++ b/drivers/usb/host/uhci-grlib.c @@ -111,7 +111,7 @@ static int __devinit uhci_hcd_grlib_probe(struct platform_device *op) return -ENOMEM; hcd->rsrc_start = res.start; - hcd->rsrc_len = res.end - res.start + 1; + hcd->rsrc_len = resource_size(&res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); diff --git a/drivers/usb/host/whci/init.c b/drivers/usb/host/whci/init.c index f7582e8e2169..d3e13b640d4b 100644 --- a/drivers/usb/host/whci/init.c +++ b/drivers/usb/host/whci/init.c @@ -178,7 +178,7 @@ void whc_clean_up(struct whc *whc) if (whc->qset_pool) dma_pool_destroy(whc->qset_pool); - len = whc->umc->resource.end - whc->umc->resource.start + 1; + len = resource_size(&whc->umc->resource); if (whc->base) iounmap(whc->base); if (whc->base_phys) diff --git a/drivers/uwb/whc-rc.c b/drivers/uwb/whc-rc.c index 70a004aa19db..3ae3c702500d 100644 --- a/drivers/uwb/whc-rc.c +++ b/drivers/uwb/whc-rc.c @@ -222,7 +222,7 @@ int whcrc_setup_rc_umc(struct whcrc *whcrc) struct umc_dev *umc_dev = whcrc->umc_dev; whcrc->area = umc_dev->resource.start; - whcrc->rc_len = umc_dev->resource.end - umc_dev->resource.start + 1; + whcrc->rc_len = resource_size(&umc_dev->resource); result = -EBUSY; if (request_mem_region(whcrc->area, whcrc->rc_len, KBUILD_MODNAME) == NULL) { dev_err(dev, "can't request URC region (%zu bytes @ 0x%lx): %d\n", diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 4484c721f0f9..817ab60f7537 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -906,7 +906,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) if (map) { /* use a pre-allocated memory buffer */ info->fix.smem_start = map->start; - info->fix.smem_len = map->end - map->start + 1; + info->fix.smem_len = resource_size(map); if (!request_mem_region(info->fix.smem_start, info->fix.smem_len, pdev->name)) { ret = -EBUSY; @@ -932,7 +932,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) /* LCDC registers */ info->fix.mmio_start = regs->start; - info->fix.mmio_len = regs->end - regs->start + 1; + info->fix.mmio_len = resource_size(regs); if (!request_mem_region(info->fix.mmio_start, info->fix.mmio_len, pdev->name)) { diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index ebb893c49e90..ad41f508b423 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -3460,9 +3460,10 @@ static int __devinit atyfb_setup_generic(struct pci_dev *pdev, raddr = addr + 0x7ff000UL; rrp = &pdev->resource[2]; - if ((rrp->flags & IORESOURCE_MEM) && request_mem_region(rrp->start, rrp->end - rrp->start + 1, "atyfb")) { + if ((rrp->flags & IORESOURCE_MEM) && + request_mem_region(rrp->start, resource_size(rrp), "atyfb")) { par->aux_start = rrp->start; - par->aux_size = rrp->end - rrp->start + 1; + par->aux_size = resource_size(rrp); raddr = rrp->start; PRINTKI("using auxiliary register aperture\n"); } @@ -3552,7 +3553,7 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, /* Reserve space */ res_start = rp->start; - res_size = rp->end - rp->start + 1; + res_size = resource_size(rp); if (!request_mem_region(res_start, res_size, "atyfb")) return -EBUSY; diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c index 34b2fc472fe8..01a8fde67f20 100644 --- a/drivers/video/au1100fb.c +++ b/drivers/video/au1100fb.c @@ -486,7 +486,7 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) } au1100fb_fix.mmio_start = regs_res->start; - au1100fb_fix.mmio_len = regs_res->end - regs_res->start + 1; + au1100fb_fix.mmio_len = resource_size(regs_res); if (!request_mem_region(au1100fb_fix.mmio_start, au1100fb_fix.mmio_len, DRIVER_NAME)) { diff --git a/drivers/video/cobalt_lcdfb.c b/drivers/video/cobalt_lcdfb.c index 42fe155aba0e..e02764319ff7 100644 --- a/drivers/video/cobalt_lcdfb.c +++ b/drivers/video/cobalt_lcdfb.c @@ -303,7 +303,7 @@ static int __devinit cobalt_lcdfb_probe(struct platform_device *dev) return -EBUSY; } - info->screen_size = res->end - res->start + 1; + info->screen_size = resource_size(res); info->screen_base = ioremap(res->start, info->screen_size); info->fbops = &cobalt_lcd_fbops; info->fix = cobalt_lcdfb_fix; diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index c225dcce89e7..9075bea55879 100644 --- a/drivers/video/controlfb.c +++ b/drivers/video/controlfb.c @@ -709,11 +709,11 @@ static int __init control_of_init(struct device_node *dp) /* Map in frame buffer and registers */ p->fb_orig_base = fb_res.start; - p->fb_orig_size = fb_res.end - fb_res.start + 1; + p->fb_orig_size = resource_size(&fb_res); /* use the big-endian aperture (??) */ p->frame_buffer_phys = fb_res.start + 0x800000; p->control_regs_phys = reg_res.start; - p->control_regs_size = reg_res.end - reg_res.start + 1; + p->control_regs_size = resource_size(®_res); if (!p->fb_orig_base || !request_mem_region(p->fb_orig_base,p->fb_orig_size,"controlfb")) { diff --git a/drivers/video/mb862xx/mb862xxfbdrv.c b/drivers/video/mb862xx/mb862xxfbdrv.c index f70bd63b0187..ee1de3e26dec 100644 --- a/drivers/video/mb862xx/mb862xxfbdrv.c +++ b/drivers/video/mb862xx/mb862xxfbdrv.c @@ -697,7 +697,7 @@ static int __devinit of_platform_mb862xx_probe(struct platform_device *ofdev) goto fbrel; } - res_size = 1 + res.end - res.start; + res_size = resource_size(&res); par->res = request_mem_region(res.start, res_size, DRV_NAME); if (par->res == NULL) { dev_err(dev, "Cannot claim framebuffer/mmio\n"); @@ -787,7 +787,7 @@ static int __devexit of_platform_mb862xx_remove(struct platform_device *ofdev) { struct fb_info *fbi = dev_get_drvdata(&ofdev->dev); struct mb862xxfb_par *par = fbi->par; - resource_size_t res_size = 1 + par->res->end - par->res->start; + resource_size_t res_size = resource_size(par->res); unsigned long reg; dev_dbg(fbi->dev, "%s release\n", fbi->fix.id); diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index c3636d55a3c5..243d16f09b8a 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -406,8 +406,7 @@ int mdp_probe(struct platform_device *pdev) goto error_get_irq; } - mdp->base = ioremap(resource->start, - resource->end - resource->start); + mdp->base = ioremap(resource->start, resource_size(resource)); if (mdp->base == 0) { printk(KERN_ERR "msmfb: cannot allocate mdp regs!\n"); ret = -ENOMEM; diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index ec351309e607..c6e3b4fcdd68 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -525,10 +525,9 @@ static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev) return -ENOMEM; } fb->fix.smem_start = resource->start; - fb->fix.smem_len = resource->end - resource->start; - fbram = ioremap(resource->start, - resource->end - resource->start); - if (fbram == 0) { + fb->fix.smem_len = resource_size(resource); + fbram = ioremap(resource->start, resource_size(resource)); + if (fbram == NULL) { printk(KERN_ERR "msmfb: cannot allocate fbram!\n"); return -ENOMEM; } diff --git a/drivers/video/nuc900fb.c b/drivers/video/nuc900fb.c index f838d9e277f0..0fff59782e45 100644 --- a/drivers/video/nuc900fb.c +++ b/drivers/video/nuc900fb.c @@ -551,7 +551,7 @@ static int __devinit nuc900fb_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - size = (res->end - res->start) + 1; + size = resource_size(res); fbi->mem = request_mem_region(res->start, size, pdev->name); if (fbi->mem == NULL) { dev_err(&pdev->dev, "failed to alloc memory region\n"); diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index ef532d9d3c99..f27ae16ead2e 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -567,7 +567,7 @@ static int __devinit platinumfb_probe(struct platform_device* odev) * northbridge and that can fail. Only request framebuffer */ if (!request_mem_region(pinfo->rsrc_fb.start, - pinfo->rsrc_fb.end - pinfo->rsrc_fb.start + 1, + resource_size(&pinfo->rsrc_fb), "platinumfb framebuffer")) { printk(KERN_ERR "platinumfb: Can't request framebuffer !\n"); framebuffer_release(info); @@ -658,8 +658,7 @@ static int __devexit platinumfb_remove(struct platform_device* odev) iounmap(pinfo->cmap_regs); release_mem_region(pinfo->rsrc_fb.start, - pinfo->rsrc_fb.end - - pinfo->rsrc_fb.start + 1); + resource_size(&pinfo->rsrc_fb)); release_mem_region(pinfo->cmap_regs_phys, 0x1000); diff --git a/drivers/video/pxa168fb.c b/drivers/video/pxa168fb.c index bb95ec56d25d..18ead6f0184d 100644 --- a/drivers/video/pxa168fb.c +++ b/drivers/video/pxa168fb.c @@ -662,7 +662,7 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev) info->fix.ypanstep = 0; info->fix.ywrapstep = 0; info->fix.mmio_start = res->start; - info->fix.mmio_len = res->end - res->start + 1; + info->fix.mmio_len = resource_size(res); info->fix.accel = FB_ACCEL_NONE; info->fbops = &pxa168fb_ops; info->pseudo_palette = fbi->pseudo_palette; diff --git a/include/linux/dio.h b/include/linux/dio.h index b2dd31ca1710..2cc0fd00463f 100644 --- a/include/linux/dio.h +++ b/include/linux/dio.h @@ -254,7 +254,7 @@ static inline struct dio_driver *dio_dev_driver(const struct dio_dev *d) #define dio_resource_start(d) ((d)->resource.start) #define dio_resource_end(d) ((d)->resource.end) -#define dio_resource_len(d) ((d)->resource.end-(d)->resource.start+1) +#define dio_resource_len(d) (resource_size(&(d)->resource)) #define dio_resource_flags(d) ((d)->resource.flags) #define dio_request_device(d, name) \ diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 1bc1338b817b..195aafc6cd07 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -50,7 +50,7 @@ static inline resource_size_t pnp_resource_len(struct resource *res) { if (res->start == 0 && res->end == 0) return 0; - return res->end - res->start + 1; + return resource_size(res); } diff --git a/include/linux/zorro.h b/include/linux/zorro.h index 7bf9db525e9e..dff42025649b 100644 --- a/include/linux/zorro.h +++ b/include/linux/zorro.h @@ -187,7 +187,7 @@ extern struct zorro_dev *zorro_find_device(zorro_id id, #define zorro_resource_start(z) ((z)->resource.start) #define zorro_resource_end(z) ((z)->resource.end) -#define zorro_resource_len(z) ((z)->resource.end-(z)->resource.start+1) +#define zorro_resource_len(z) (resource_size(&(z)->resource)) #define zorro_resource_flags(z) ((z)->resource.flags) #define zorro_request_device(z, name) \ diff --git a/kernel/kexec.c b/kernel/kexec.c index 8d814cbc8109..296fbc84d659 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -1095,7 +1095,7 @@ size_t crash_get_memory_size(void) size_t size = 0; mutex_lock(&kexec_mutex); if (crashk_res.end != crashk_res.start) - size = crashk_res.end - crashk_res.start + 1; + size = resource_size(&crashk_res); mutex_unlock(&kexec_mutex); return size; } diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index 3ff8cc5f487a..010658335881 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -262,8 +262,7 @@ static int i2sbus_add_dev(struct macio_dev *macio, */ dev->allocated_resource[i] = request_mem_region(dev->resources[i].start, - dev->resources[i].end - - dev->resources[i].start + 1, + resource_size(&dev->resources[i]), dev->rnames[i]); if (!dev->allocated_resource[i]) { printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i); @@ -272,19 +271,19 @@ static int i2sbus_add_dev(struct macio_dev *macio, } r = &dev->resources[aoa_resource_i2smmio]; - rlen = r->end - r->start + 1; + rlen = resource_size(r); if (rlen < sizeof(struct i2s_interface_regs)) goto err; dev->intfregs = ioremap(r->start, rlen); r = &dev->resources[aoa_resource_txdbdma]; - rlen = r->end - r->start + 1; + rlen = resource_size(r); if (rlen < sizeof(struct dbdma_regs)) goto err; dev->out.dbdma = ioremap(r->start, rlen); r = &dev->resources[aoa_resource_rxdbdma]; - rlen = r->end - r->start + 1; + rlen = resource_size(r); if (rlen < sizeof(struct dbdma_regs)) goto err; dev->in.dbdma = ioremap(r->start, rlen); diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c index 6e2409181895..30468b31cad8 100644 --- a/sound/atmel/abdac.c +++ b/sound/atmel/abdac.c @@ -448,7 +448,7 @@ static int __devinit atmel_abdac_probe(struct platform_device *pdev) goto out_free_card; } - dac->regs = ioremap(regs->start, regs->end - regs->start + 1); + dac->regs = ioremap(regs->start, resource_size(regs)); if (!dac->regs) { dev_dbg(&pdev->dev, "could not remap register memory\n"); goto out_free_card; diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index b310702c646e..41b901bde5c7 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -971,7 +971,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev) chip->card = card; chip->pclk = pclk; chip->pdev = pdev; - chip->regs = ioremap(regs->start, regs->end - regs->start + 1); + chip->regs = ioremap(regs->start, resource_size(regs)); if (!chip->regs) { dev_dbg(&pdev->dev, "could not remap register memory\n"); diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 3ecbd67f88c9..ab96cde7417b 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -881,8 +881,7 @@ static int snd_pmac_free(struct snd_pmac *chip) for (i = 0; i < 3; i++) { if (chip->requested & (1 << i)) release_mem_region(chip->rsrc[i].start, - chip->rsrc[i].end - - chip->rsrc[i].start + 1); + resource_size(&chip->rsrc[i])); } } @@ -1228,8 +1227,7 @@ int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) goto __error; } if (request_mem_region(chip->rsrc[i].start, - chip->rsrc[i].end - - chip->rsrc[i].start + 1, + resource_size(&chip->rsrc[i]), rnames[i]) == NULL) { printk(KERN_ERR "snd: can't request rsrc " " %d (%s: %pR)\n", @@ -1254,8 +1252,7 @@ int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) goto __error; } if (request_mem_region(chip->rsrc[i].start, - chip->rsrc[i].end - - chip->rsrc[i].start + 1, + resource_size(&chip->rsrc[i]), rnames[i]) == NULL) { printk(KERN_ERR "snd: can't request rsrc " " %d (%s: %pR)\n", diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 313e0ccedd5b..6a882aa55530 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -678,7 +678,7 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev) kfree(ssi_private); return ret; } - ssi_private->ssi = ioremap(res.start, 1 + res.end - res.start); + ssi_private->ssi = ioremap(res.start, resource_size(&res)); ssi_private->ssi_phys = res.start; ssi_private->irq = irq_of_parse_and_map(np, 0); diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index fff695ccdd3e..86023142a4cb 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -384,7 +384,7 @@ static int mpc5200_hpcd_probe(struct of_device *op) dev_err(&op->dev, "Missing reg property\n"); return -ENODEV; } - regs = ioremap(res.start, 1 + res.end - res.start); + regs = ioremap(res.start, resource_size(&res)); if (!regs) { dev_err(&op->dev, "Could not map registers\n"); return -ENODEV; -- cgit v1.2.3 From bdd4e85dc36cdbcfc1608a5b2a17c80a9db8986a Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 8 Jun 2011 01:13:27 +0200 Subject: sched: Isolate preempt counting in its own config option Create a new CONFIG_PREEMPT_COUNT that handles the inc/dec of preempt count offset independently. So that the offset can be updated by preempt_disable() and preempt_enable() even without the need for CONFIG_PREEMPT beeing set. This prepares to make CONFIG_DEBUG_SPINLOCK_SLEEP working with !CONFIG_PREEMPT where it currently doesn't detect code that sleeps inside explicit preemption disabled sections. Signed-off-by: Frederic Weisbecker Acked-by: Paul E. McKenney Cc: Ingo Molnar Cc: Peter Zijlstra --- include/linux/bit_spinlock.h | 2 +- include/linux/hardirq.h | 4 ++-- include/linux/pagemap.h | 4 ++-- include/linux/preempt.h | 26 +++++++++++++++++--------- include/linux/rcupdate.h | 12 ++++++------ include/linux/sched.h | 2 +- kernel/Kconfig.preempt | 3 +++ kernel/sched.c | 2 +- 8 files changed, 33 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/linux/bit_spinlock.h b/include/linux/bit_spinlock.h index b4326bfa684f..564d997e2168 100644 --- a/include/linux/bit_spinlock.h +++ b/include/linux/bit_spinlock.h @@ -88,7 +88,7 @@ static inline int bit_spin_is_locked(int bitnum, unsigned long *addr) { #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) return test_bit(bitnum, addr); -#elif defined CONFIG_PREEMPT +#elif defined CONFIG_PREEMPT_COUNT return preempt_count(); #else return 1; diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h index ba362171e8ae..f743883f769e 100644 --- a/include/linux/hardirq.h +++ b/include/linux/hardirq.h @@ -93,7 +93,7 @@ */ #define in_nmi() (preempt_count() & NMI_MASK) -#if defined(CONFIG_PREEMPT) +#if defined(CONFIG_PREEMPT_COUNT) # define PREEMPT_CHECK_OFFSET 1 #else # define PREEMPT_CHECK_OFFSET 0 @@ -115,7 +115,7 @@ #define in_atomic_preempt_off() \ ((preempt_count() & ~PREEMPT_ACTIVE) != PREEMPT_CHECK_OFFSET) -#ifdef CONFIG_PREEMPT +#ifdef CONFIG_PREEMPT_COUNT # define preemptible() (preempt_count() == 0 && !irqs_disabled()) # define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) #else diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 716875e53520..8e38d4c140ff 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -134,7 +134,7 @@ static inline int page_cache_get_speculative(struct page *page) VM_BUG_ON(in_interrupt()); #if !defined(CONFIG_SMP) && defined(CONFIG_TREE_RCU) -# ifdef CONFIG_PREEMPT +# ifdef CONFIG_PREEMPT_COUNT VM_BUG_ON(!in_atomic()); # endif /* @@ -172,7 +172,7 @@ static inline int page_cache_add_speculative(struct page *page, int count) VM_BUG_ON(in_interrupt()); #if !defined(CONFIG_SMP) && defined(CONFIG_TREE_RCU) -# ifdef CONFIG_PREEMPT +# ifdef CONFIG_PREEMPT_COUNT VM_BUG_ON(!in_atomic()); # endif VM_BUG_ON(page_count(page) == 0); diff --git a/include/linux/preempt.h b/include/linux/preempt.h index 2e681d9555bd..58969b2a8a82 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -27,6 +27,21 @@ asmlinkage void preempt_schedule(void); +#define preempt_check_resched() \ +do { \ + if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ + preempt_schedule(); \ +} while (0) + +#else /* !CONFIG_PREEMPT */ + +#define preempt_check_resched() do { } while (0) + +#endif /* CONFIG_PREEMPT */ + + +#ifdef CONFIG_PREEMPT_COUNT + #define preempt_disable() \ do { \ inc_preempt_count(); \ @@ -39,12 +54,6 @@ do { \ dec_preempt_count(); \ } while (0) -#define preempt_check_resched() \ -do { \ - if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ - preempt_schedule(); \ -} while (0) - #define preempt_enable() \ do { \ preempt_enable_no_resched(); \ @@ -80,18 +89,17 @@ do { \ preempt_check_resched(); \ } while (0) -#else +#else /* !CONFIG_PREEMPT_COUNT */ #define preempt_disable() do { } while (0) #define preempt_enable_no_resched() do { } while (0) #define preempt_enable() do { } while (0) -#define preempt_check_resched() do { } while (0) #define preempt_disable_notrace() do { } while (0) #define preempt_enable_no_resched_notrace() do { } while (0) #define preempt_enable_notrace() do { } while (0) -#endif +#endif /* CONFIG_PREEMPT_COUNT */ #ifdef CONFIG_PREEMPT_NOTIFIERS diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 99f9aa7c2804..8f4f881a0ad8 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -239,7 +239,7 @@ extern int rcu_read_lock_bh_held(void); * Check debug_lockdep_rcu_enabled() to prevent false positives during boot * and while lockdep is disabled. */ -#ifdef CONFIG_PREEMPT +#ifdef CONFIG_PREEMPT_COUNT static inline int rcu_read_lock_sched_held(void) { int lockdep_opinion = 0; @@ -250,12 +250,12 @@ static inline int rcu_read_lock_sched_held(void) lockdep_opinion = lock_is_held(&rcu_sched_lock_map); return lockdep_opinion || preempt_count() != 0 || irqs_disabled(); } -#else /* #ifdef CONFIG_PREEMPT */ +#else /* #ifdef CONFIG_PREEMPT_COUNT */ static inline int rcu_read_lock_sched_held(void) { return 1; } -#endif /* #else #ifdef CONFIG_PREEMPT */ +#endif /* #else #ifdef CONFIG_PREEMPT_COUNT */ #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ @@ -276,17 +276,17 @@ static inline int rcu_read_lock_bh_held(void) return 1; } -#ifdef CONFIG_PREEMPT +#ifdef CONFIG_PREEMPT_COUNT static inline int rcu_read_lock_sched_held(void) { return preempt_count() != 0 || irqs_disabled(); } -#else /* #ifdef CONFIG_PREEMPT */ +#else /* #ifdef CONFIG_PREEMPT_COUNT */ static inline int rcu_read_lock_sched_held(void) { return 1; } -#endif /* #else #ifdef CONFIG_PREEMPT */ +#endif /* #else #ifdef CONFIG_PREEMPT_COUNT */ #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 483c1ed5bc4d..4ecd5cbe7e24 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2502,7 +2502,7 @@ extern int _cond_resched(void); extern int __cond_resched_lock(spinlock_t *lock); -#ifdef CONFIG_PREEMPT +#ifdef CONFIG_PREEMPT_COUNT #define PREEMPT_LOCK_OFFSET PREEMPT_OFFSET #else #define PREEMPT_LOCK_OFFSET 0 diff --git a/kernel/Kconfig.preempt b/kernel/Kconfig.preempt index bf987b95b356..24e7cb0ba26a 100644 --- a/kernel/Kconfig.preempt +++ b/kernel/Kconfig.preempt @@ -35,6 +35,7 @@ config PREEMPT_VOLUNTARY config PREEMPT bool "Preemptible Kernel (Low-Latency Desktop)" + select PREEMPT_COUNT help This option reduces the latency of the kernel by making all kernel code (that is not executing in a critical section) @@ -52,3 +53,5 @@ config PREEMPT endchoice +config PREEMPT_COUNT + bool \ No newline at end of file diff --git a/kernel/sched.c b/kernel/sched.c index 01d9536aaa8e..90ad7cf2b290 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2843,7 +2843,7 @@ void sched_fork(struct task_struct *p) #if defined(CONFIG_SMP) p->on_cpu = 0; #endif -#ifdef CONFIG_PREEMPT +#ifdef CONFIG_PREEMPT_COUNT /* Want to start with kernel preemption disabled. */ task_thread_info(p)->preempt_count = 1; #endif -- cgit v1.2.3 From e5ea3f12d41d96edf4ad9374db2b1725e457acec Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 10 Jun 2011 13:44:49 +0100 Subject: gpio/basic_mmio: add missing include of spinlock_types.h include/linux/basic_mmio_gpio.h uses a spinlock_t without including any of the spinlock headers resulting in this compiler warning. include/linux/basic_mmio_gpio.h:51:2: error: expected specifier-qualifier-list before 'spinlock_t' Explicitly include linux/spinlock_types.h to fix it. Signed-off-by: Jamie Iles Signed-off-by: Grant Likely --- include/linux/basic_mmio_gpio.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/basic_mmio_gpio.h b/include/linux/basic_mmio_gpio.h index 1ae12710d732..98999cf107ce 100644 --- a/include/linux/basic_mmio_gpio.h +++ b/include/linux/basic_mmio_gpio.h @@ -16,6 +16,7 @@ #include #include #include +#include struct bgpio_pdata { int base; -- cgit v1.2.3 From 1d34d108e07680e2c07847d5e69a334cb4f96ab3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 6 Jun 2011 12:59:29 +0300 Subject: mac80211: add ieee80211_get_operstate() function Add ieee80211_get_operstate() function to get the operstate of the netdevice. This is needed for drivers that need to know when the interface is IF_OPER_UP (e.g. wl12xx), and block notifiers can't be used (e.g. because the interface is already IF_OPER_UP, like after resuming from suspend) Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- include/net/mac80211.h | 10 ++++++++++ net/mac80211/mlme.c | 7 +++++++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3b31ec95dd8e..e33fe795a3a4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2919,6 +2919,16 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp); +/** + * ieee80211_get_operstate - get the operstate of the vif + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * The driver might need to know the operstate of the net_device + * (specifically, whether the link is IF_OPER_UP after resume) + */ +unsigned char ieee80211_get_operstate(struct ieee80211_vif *vif); + /** * ieee80211_chswitch_done - Complete channel switch process * @vif: &struct ieee80211_vif pointer from the add_interface callback. diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d595265d6c22..0c6e9ef8c7f8 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2652,3 +2652,10 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp); } EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); + +unsigned char ieee80211_get_operstate(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + return sdata->dev->operstate; +} +EXPORT_SYMBOL(ieee80211_get_operstate); -- cgit v1.2.3 From e3ae0cac00042d7fb76914c30c5f991f918e65b4 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 9 Jun 2011 20:07:20 +0200 Subject: drivers: bcma: export bcma_core_disable() function In the brcm80211 driver we disable the 80211 core when the driver is 'down'. The bcma_core_disable() function exactly does the same as our implementation so exporting this function makes sense. Cc: linux-wireless@vger.kernel.org Cc: Rafal Milecki Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/bcma/core.c | 3 ++- include/linux/bcma/bcma.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/bcma/core.c b/drivers/bcma/core.c index ced379f7b371..1ec7d4528dd0 100644 --- a/drivers/bcma/core.c +++ b/drivers/bcma/core.c @@ -19,7 +19,7 @@ bool bcma_core_is_enabled(struct bcma_device *core) } EXPORT_SYMBOL_GPL(bcma_core_is_enabled); -static void bcma_core_disable(struct bcma_device *core, u32 flags) +void bcma_core_disable(struct bcma_device *core, u32 flags) { if (bcma_aread32(core, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET) return; @@ -31,6 +31,7 @@ static void bcma_core_disable(struct bcma_device *core, u32 flags) bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET); udelay(1); } +EXPORT_SYMBOL_GPL(bcma_core_disable); int bcma_core_enable(struct bcma_device *core, u32 flags) { diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 6ff080eac0b2..3895aeb494a3 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -244,6 +244,7 @@ void bcma_awrite32(struct bcma_device *core, u16 offset, u32 value) } 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); #endif /* LINUX_BCMA_H_ */ -- cgit v1.2.3 From 8de111e27688798623b9e9062235bb0cac29f599 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 27 May 2011 16:56:50 -0300 Subject: [media] lirc_dev: store cdev in irctl, up maxdevs Store the cdev pointer in struct irctl, allocated dynamically as needed, rather than having a static array. At the same time, recycle some of the saved memory to nudge the maximum number of lirc devices supported up a ways -- its not that uncommon these days, now that we have the rc-core lirc bridge driver, to see a system with at least 4 raw IR receivers. (consider a mythtv backend with several video capture devices and the possible need for IR transmit hardware). Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 37 +++++++++++++++++++++++++++---------- include/media/lirc_dev.h | 2 +- 2 files changed, 28 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index fd237ab120bb..27997a9ceb0d 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -55,6 +55,8 @@ struct irctl { struct lirc_buffer *buf; unsigned int chunk_size; + struct cdev *cdev; + struct task_struct *task; long jiffies_to_wait; }; @@ -62,7 +64,6 @@ struct irctl { static DEFINE_MUTEX(lirc_dev_lock); static struct irctl *irctls[MAX_IRCTL_DEVICES]; -static struct cdev cdevs[MAX_IRCTL_DEVICES]; /* Only used for sysfs but defined to void otherwise */ static struct class *lirc_class; @@ -167,9 +168,13 @@ static struct file_operations lirc_dev_fops = { static int lirc_cdev_add(struct irctl *ir) { - int retval; + int retval = -ENOMEM; struct lirc_driver *d = &ir->d; - struct cdev *cdev = &cdevs[d->minor]; + struct cdev *cdev; + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) + goto err_out; if (d->fops) { cdev_init(cdev, d->fops); @@ -180,12 +185,20 @@ static int lirc_cdev_add(struct irctl *ir) } retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor); if (retval) - return retval; + goto err_out; retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1); - if (retval) + if (retval) { kobject_put(&cdev->kobj); + goto err_out; + } + + ir->cdev = cdev; + + return 0; +err_out: + kfree(cdev); return retval; } @@ -214,7 +227,7 @@ int lirc_register_driver(struct lirc_driver *d) if (MAX_IRCTL_DEVICES <= d->minor) { dev_err(d->dev, "lirc_dev: lirc_register_driver: " "\"minor\" must be between 0 and %d (%d)!\n", - MAX_IRCTL_DEVICES-1, d->minor); + MAX_IRCTL_DEVICES - 1, d->minor); err = -EBADRQC; goto out; } @@ -369,7 +382,7 @@ int lirc_unregister_driver(int minor) if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between " - "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES-1); + "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES - 1); return -EBADRQC; } @@ -380,7 +393,7 @@ int lirc_unregister_driver(int minor) return -ENOENT; } - cdev = &cdevs[minor]; + cdev = ir->cdev; mutex_lock(&lirc_dev_lock); @@ -410,6 +423,7 @@ int lirc_unregister_driver(int minor) } else { lirc_irctl_cleanup(ir); cdev_del(cdev); + kfree(cdev); kfree(ir); irctls[minor] = NULL; } @@ -453,7 +467,7 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) goto error; } - cdev = &cdevs[iminor(inode)]; + cdev = ir->cdev; if (try_module_get(cdev->owner)) { ir->open++; retval = ir->d.set_use_inc(ir->d.data); @@ -484,13 +498,15 @@ EXPORT_SYMBOL(lirc_dev_fop_open); int lirc_dev_fop_close(struct inode *inode, struct file *file) { struct irctl *ir = irctls[iminor(inode)]; - struct cdev *cdev = &cdevs[iminor(inode)]; + struct cdev *cdev; if (!ir) { printk(KERN_ERR "%s: called with invalid irctl\n", __func__); return -EINVAL; } + cdev = ir->cdev; + dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor); WARN_ON(mutex_lock_killable(&lirc_dev_lock)); @@ -503,6 +519,7 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) lirc_irctl_cleanup(ir); cdev_del(cdev); irctls[ir->d.minor] = NULL; + kfree(cdev); kfree(ir); } diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index 630e702c9511..168dd0b1bae2 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -9,7 +9,7 @@ #ifndef _LINUX_LIRC_DEV_H #define _LINUX_LIRC_DEV_H -#define MAX_IRCTL_DEVICES 4 +#define MAX_IRCTL_DEVICES 8 #define BUFLEN 16 #define mod(n, div) ((n) % (div)) -- cgit v1.2.3 From c30701130cf7bff4f97a148b1bc96f878c046a40 Mon Sep 17 00:00:00 2001 From: "HeungJun, Kim" Date: Tue, 7 Jun 2011 02:00:58 -0300 Subject: [media] m5mols: Use proper email address format Signed-off-by: HeungJun, Kim Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/m5mols/m5mols.h | 4 ++-- drivers/media/video/m5mols/m5mols_capture.c | 4 ++-- drivers/media/video/m5mols/m5mols_controls.c | 4 ++-- drivers/media/video/m5mols/m5mols_core.c | 4 ++-- drivers/media/video/m5mols/m5mols_reg.h | 4 ++-- include/media/m5mols.h | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index 9ae170935871..89d09a8914f8 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -2,10 +2,10 @@ * Header for M-5MOLS 8M Pixel camera sensor with ISP * * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim, riverful.kim@samsung.com + * Author: HeungJun Kim * * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com + * Author: Dongsoo Nathaniel Kim * * 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 diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c index 751f4593da9b..d9471928369d 100644 --- a/drivers/media/video/m5mols/m5mols_capture.c +++ b/drivers/media/video/m5mols/m5mols_capture.c @@ -2,10 +2,10 @@ * The Capture code for Fujitsu M-5MOLS ISP * * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim, riverful.kim@samsung.com + * Author: HeungJun Kim * * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com + * Author: Dongsoo Nathaniel Kim * * 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 diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index d392c83fbf05..d135d20d09cf 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -2,10 +2,10 @@ * Controls for M-5MOLS 8M Pixel camera sensor with ISP * * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim, riverful.kim@samsung.com + * Author: HeungJun Kim * * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com + * Author: Dongsoo Nathaniel Kim * * 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 diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c index 9815f2c75e83..43c68f51c5ce 100644 --- a/drivers/media/video/m5mols/m5mols_core.c +++ b/drivers/media/video/m5mols/m5mols_core.c @@ -2,10 +2,10 @@ * Driver for M-5MOLS 8M Pixel camera sensor with ISP * * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim, riverful.kim@samsung.com + * Author: HeungJun Kim * * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com + * Author: Dongsoo Nathaniel Kim * * 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 diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h index 5f5bdcf608b6..c755bd6edfe9 100644 --- a/drivers/media/video/m5mols/m5mols_reg.h +++ b/drivers/media/video/m5mols/m5mols_reg.h @@ -2,10 +2,10 @@ * Register map for M-5MOLS 8M Pixel camera sensor with ISP * * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim, riverful.kim@samsung.com + * Author: HeungJun Kim * * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com + * Author: Dongsoo Nathaniel Kim * * 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 diff --git a/include/media/m5mols.h b/include/media/m5mols.h index 2d7e7ca2313d..aac2c0e06d5e 100644 --- a/include/media/m5mols.h +++ b/include/media/m5mols.h @@ -2,10 +2,10 @@ * Driver header for M-5MOLS 8M Pixel camera sensor with ISP * * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim, riverful.kim@samsung.com + * Author: HeungJun Kim * * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com + * Author: Dongsoo Nathaniel Kim * * 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 -- cgit v1.2.3 From 56a210526adb2854d5b7c398a40260720390ee05 Mon Sep 17 00:00:00 2001 From: David Howells Date: Sat, 11 Jun 2011 12:29:58 +0100 Subject: linux/seqlock.h should #include asm/processor.h for cpu_relax() It uses cpu_relax(), and so needs Without this patch, I see: CC arch/mn10300/kernel/asm-offsets.s In file included from include/linux/time.h:8, from include/linux/timex.h:56, from include/linux/sched.h:57, from arch/mn10300/kernel/asm-offsets.c:7: include/linux/seqlock.h: In function 'read_seqbegin': include/linux/seqlock.h:91: error: implicit declaration of function 'cpu_relax' whilst building asb2364_defconfig on MN10300. Signed-off-by: David Howells Signed-off-by: Linus Torvalds --- include/linux/seqlock.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index e9811892844f..c6db9fb33c44 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -28,6 +28,7 @@ #include #include +#include typedef struct { unsigned sequence; -- cgit v1.2.3 From 10a8d94a95742bb15b4e617ee9884bb4381362be Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 10 Jun 2011 00:56:17 +0000 Subject: virtio_net: introduce VIRTIO_NET_HDR_F_DATA_VALID There's no need for the guest to validate the checksum if it have been validated by host nics. So this patch introduces a new flag - VIRTIO_NET_HDR_F_DATA_VALID which is used to bypass the checksum examing in guest. The backend (tap/macvtap) may set this flag when met skbs with CHECKSUM_UNNECESSARY to save cpu utilization. No feature negotiation is needed as old driver just ignore this flag. Iperf shows 12%-30% performance improvement for UDP traffic. For TCP, when gro is on no difference as it produces skb with partial checksum. But when gro is disabled, 20% or even higher improvement could be measured by netperf. Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 2 ++ drivers/net/tun.c | 2 ++ drivers/net/virtio_net.c | 2 ++ include/linux/virtio_net.h | 1 + net/packet/af_packet.c | 2 ++ 5 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 6696e56e6320..ecee0fe65a97 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -508,6 +508,8 @@ static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb, vnet_hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; vnet_hdr->csum_start = skb_checksum_start_offset(skb); vnet_hdr->csum_offset = skb->csum_offset; + } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { + vnet_hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID; } /* else everything is zero */ return 0; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index ef68e13c042d..4dab85eecb60 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -788,6 +788,8 @@ static ssize_t tun_put_user(struct tun_struct *tun, gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; gso.csum_start = skb_checksum_start_offset(skb); gso.csum_offset = skb->csum_offset; + } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { + gso.flags = VIRTIO_NET_HDR_F_DATA_VALID; } /* else everything is zero */ if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total, diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index f6853247a620..be3686a298da 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -274,6 +274,8 @@ static void receive_buf(struct net_device *dev, void *buf, unsigned int len) hdr->hdr.csum_start, hdr->hdr.csum_offset)) goto frame_err; + } else if (hdr->hdr.flags & VIRTIO_NET_HDR_F_DATA_VALID) { + skb->ip_summed = CHECKSUM_UNNECESSARY; } skb->protocol = eth_type_trans(skb, dev); diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 136040bba3e3..970d5a2a9047 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -63,6 +63,7 @@ struct virtio_net_config { * specify GSO or CSUM features, you can simply ignore the header. */ struct virtio_net_hdr { #define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset +#define VIRTIO_NET_HDR_F_DATA_VALID 2 // Csum is valid __u8 flags; #define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame #define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 67f6749a0a45..b54ec41adea9 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1685,6 +1685,8 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, vnet_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; vnet_hdr.csum_start = skb_checksum_start_offset(skb); vnet_hdr.csum_offset = skb->csum_offset; + } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { + vnet_hdr.flags = VIRTIO_NET_HDR_F_DATA_VALID; } /* else everything is zero */ err = memcpy_toiovec(msg->msg_iov, (void *)&vnet_hdr, -- cgit v1.2.3 From 0b5c9db1b11d3175bb42b80663a9f072f801edf5 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 10 Jun 2011 06:56:58 +0000 Subject: vlan: Fix the ingress VLAN_FLAG_REORDER_HDR check Testing of VLAN_FLAG_REORDER_HDR does not belong in vlan_untag but rather in vlan_do_receive. Otherwise the vlan header will not be properly put on the packet in the case of vlan header accelleration. As we remove the check from vlan_check_reorder_header rename it vlan_reorder_header to keep the naming clean. Fix up the skb->pkt_type early so we don't look at the packet after adding the vlan tag, which guarantees we don't goof and look at the wrong field. Use a simple if statement instead of a complicated switch statement to decided that we need to increment rx_stats for a multicast packet. Hopefully at somepoint we will just declare the case where VLAN_FLAG_REORDER_HDR is cleared as unsupported and remove the code. Until then this keeps it working correctly. Signed-off-by: Eric W. Biederman Signed-off-by: Jiri Pirko Acked-by: Changli Gao Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 25 ++++++++++++++++++--- include/linux/skbuff.h | 5 +++++ net/8021q/vlan_core.c | 60 +++++++++++++++++++++++++++---------------------- net/core/dev.c | 2 +- 4 files changed, 61 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index dc01681fbb42..affa27380b72 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -225,7 +225,7 @@ static inline int vlan_hwaccel_receive_skb(struct sk_buff *skb, } /** - * __vlan_put_tag - regular VLAN tag inserting + * vlan_insert_tag - regular VLAN tag inserting * @skb: skbuff to tag * @vlan_tci: VLAN TCI to insert * @@ -234,8 +234,10 @@ static inline int vlan_hwaccel_receive_skb(struct sk_buff *skb, * * Following the skb_unshare() example, in case of error, the calling function * doesn't have to worry about freeing the original skb. + * + * Does not change skb->protocol so this function can be used during receive. */ -static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) +static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb, u16 vlan_tci) { struct vlan_ethhdr *veth; @@ -255,8 +257,25 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) /* now, the TCI */ veth->h_vlan_TCI = htons(vlan_tci); - skb->protocol = htons(ETH_P_8021Q); + return skb; +} +/** + * __vlan_put_tag - regular VLAN tag inserting + * @skb: skbuff to tag + * @vlan_tci: VLAN TCI to insert + * + * Inserts the VLAN tag into @skb as part of the payload + * Returns a VLAN tagged skb. If a new skb is created, @skb is freed. + * + * Following the skb_unshare() example, in case of error, the calling function + * doesn't have to worry about freeing the original skb. + */ +static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) +{ + skb = vlan_insert_tag(skb, vlan_tci); + if (skb) + skb->protocol = htons(ETH_P_8021Q); return skb; } diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index e8b78ce14474..c0a4f3ab0cc0 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1256,6 +1256,11 @@ static inline void skb_reserve(struct sk_buff *skb, int len) skb->tail += len; } +static inline void skb_reset_mac_len(struct sk_buff *skb) +{ + skb->mac_len = skb->network_header - skb->mac_header; +} + #ifdef NET_SKBUFF_DATA_USES_OFFSET static inline unsigned char *skb_transport_header(const struct sk_buff *skb) { diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 41495dc2a4c9..fcc684678af6 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -23,6 +23,31 @@ bool vlan_do_receive(struct sk_buff **skbp) return false; skb->dev = vlan_dev; + if (skb->pkt_type == PACKET_OTHERHOST) { + /* Our lower layer thinks this is not local, let's make sure. + * This allows the VLAN to have a different MAC than the + * underlying device, and still route correctly. */ + if (!compare_ether_addr(eth_hdr(skb)->h_dest, + vlan_dev->dev_addr)) + skb->pkt_type = PACKET_HOST; + } + + if (!(vlan_dev_info(vlan_dev)->flags & VLAN_FLAG_REORDER_HDR)) { + unsigned int offset = skb->data - skb_mac_header(skb); + + /* + * vlan_insert_tag expect skb->data pointing to mac header. + * So change skb->data before calling it and change back to + * original position later + */ + skb_push(skb, offset); + skb = *skbp = vlan_insert_tag(skb, skb->vlan_tci); + if (!skb) + return false; + skb_pull(skb, offset + VLAN_HLEN); + skb_reset_mac_len(skb); + } + skb->priority = vlan_get_ingress_priority(vlan_dev, skb->vlan_tci); skb->vlan_tci = 0; @@ -31,22 +56,8 @@ bool vlan_do_receive(struct sk_buff **skbp) u64_stats_update_begin(&rx_stats->syncp); rx_stats->rx_packets++; rx_stats->rx_bytes += skb->len; - - switch (skb->pkt_type) { - case PACKET_BROADCAST: - break; - case PACKET_MULTICAST: + if (skb->pkt_type == PACKET_MULTICAST) rx_stats->rx_multicast++; - break; - case PACKET_OTHERHOST: - /* Our lower layer thinks this is not local, let's make sure. - * This allows the VLAN to have a different MAC than the - * underlying device, and still route correctly. */ - if (!compare_ether_addr(eth_hdr(skb)->h_dest, - vlan_dev->dev_addr)) - skb->pkt_type = PACKET_HOST; - break; - } u64_stats_update_end(&rx_stats->syncp); return true; @@ -89,18 +100,13 @@ gro_result_t vlan_gro_frags(struct napi_struct *napi, struct vlan_group *grp, } EXPORT_SYMBOL(vlan_gro_frags); -static struct sk_buff *vlan_check_reorder_header(struct sk_buff *skb) +static struct sk_buff *vlan_reorder_header(struct sk_buff *skb) { - if (vlan_dev_info(skb->dev)->flags & VLAN_FLAG_REORDER_HDR) { - if (skb_cow(skb, skb_headroom(skb)) < 0) - skb = NULL; - if (skb) { - /* Lifted from Gleb's VLAN code... */ - memmove(skb->data - ETH_HLEN, - skb->data - VLAN_ETH_HLEN, 12); - skb->mac_header += VLAN_HLEN; - } - } + if (skb_cow(skb, skb_headroom(skb)) < 0) + return NULL; + memmove(skb->data - ETH_HLEN, skb->data - VLAN_ETH_HLEN, 2 * ETH_ALEN); + skb->mac_header += VLAN_HLEN; + skb_reset_mac_len(skb); return skb; } @@ -161,7 +167,7 @@ struct sk_buff *vlan_untag(struct sk_buff *skb) skb_pull_rcsum(skb, VLAN_HLEN); vlan_set_encap_proto(skb, vhdr); - skb = vlan_check_reorder_header(skb); + skb = vlan_reorder_header(skb); if (unlikely(!skb)) goto err_free; diff --git a/net/core/dev.c b/net/core/dev.c index a54c9f87ddbb..9c58c1ec41a9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3114,7 +3114,7 @@ static int __netif_receive_skb(struct sk_buff *skb) skb_reset_network_header(skb); skb_reset_transport_header(skb); - skb->mac_len = skb->network_header - skb->mac_header; + skb_reset_mac_len(skb); pt_prev = NULL; -- cgit v1.2.3 From 8f0ea0fe3a036a47767f9c80e81b13e379a1f43b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 10 Jun 2011 19:45:51 +0000 Subject: snmp: reduce percpu needs by 50% SNMP mibs use two percpu arrays, one used in BH context, another in USER context. With increasing number of cpus in machines, and fact that ipv6 uses per network device ipstats_mib, this is consuming a lot of memory if many network devices are registered. commit be281e554e2a (ipv6: reduce per device ICMP mib sizes) shrinked percpu needs for ipv6, but we can reduce memory use a bit more. With recent percpu infrastructure (irqsafe_cpu_inc() ...), we no longer need this BH/USER separation since we can update counters in a single x86 instruction, regardless of the BH/USER context. Other arches than x86 might need to disable irq in their irqsafe_cpu_inc() implementation : If this happens to be a problem, we can make SNMP_ARRAY_SZ arch dependent, but a previous poll ( https://lkml.org/lkml/2011/3/17/174 ) to arch maintainers did not raise strong opposition. Only on 32bit arches, we need to disable BH for 64bit counters updates done from USER context (currently used for IP MIB) This also reduces vmlinux size : 1) x86_64 build $ size vmlinux.before vmlinux.after text data bss dec hex filename 7853650 1293772 1896448 11043870 a8841e vmlinux.before 7850578 1293772 1896448 11040798 a8781e vmlinux.after 2) i386 build $ size vmlinux.before vmlinux.afterpatch text data bss dec hex filename 6039335 635076 3670016 10344427 9dd7eb vmlinux.before 6037342 635076 3670016 10342434 9dd022 vmlinux.afterpatch Signed-off-by: Eric Dumazet CC: Andi Kleen CC: Ingo Molnar CC: Tejun Heo CC: Christoph Lameter CC: Benjamin Herrenschmidt --- include/net/snmp.h | 86 +++++++++++++++++++++--------------------------------- net/ipv4/af_inet.c | 52 +++++++++++++++------------------ 2 files changed, 56 insertions(+), 82 deletions(-) (limited to 'include') diff --git a/include/net/snmp.h b/include/net/snmp.h index 479083a78b0c..8f0f9ac0307f 100644 --- a/include/net/snmp.h +++ b/include/net/snmp.h @@ -116,57 +116,51 @@ struct linux_xfrm_mib { unsigned long mibs[LINUX_MIB_XFRMMAX]; }; -/* - * FIXME: On x86 and some other CPUs the split into user and softirq parts - * is not needed because addl $1,memory is atomic against interrupts (but - * atomic_inc would be overkill because of the lock cycles). Wants new - * nonlocked_atomic_inc() primitives -AK - */ +#define SNMP_ARRAY_SZ 1 + #define DEFINE_SNMP_STAT(type, name) \ - __typeof__(type) __percpu *name[2] + __typeof__(type) __percpu *name[SNMP_ARRAY_SZ] #define DEFINE_SNMP_STAT_ATOMIC(type, name) \ __typeof__(type) *name #define DECLARE_SNMP_STAT(type, name) \ - extern __typeof__(type) __percpu *name[2] - -#define SNMP_STAT_BHPTR(name) (name[0]) -#define SNMP_STAT_USRPTR(name) (name[1]) + extern __typeof__(type) __percpu *name[SNMP_ARRAY_SZ] #define SNMP_INC_STATS_BH(mib, field) \ __this_cpu_inc(mib[0]->mibs[field]) + #define SNMP_INC_STATS_USER(mib, field) \ - this_cpu_inc(mib[1]->mibs[field]) + irqsafe_cpu_inc(mib[0]->mibs[field]) + #define SNMP_INC_STATS_ATOMIC_LONG(mib, field) \ atomic_long_inc(&mib->mibs[field]) + #define SNMP_INC_STATS(mib, field) \ - this_cpu_inc(mib[!in_softirq()]->mibs[field]) + irqsafe_cpu_inc(mib[0]->mibs[field]) + #define SNMP_DEC_STATS(mib, field) \ - this_cpu_dec(mib[!in_softirq()]->mibs[field]) + irqsafe_cpu_dec(mib[0]->mibs[field]) + #define SNMP_ADD_STATS_BH(mib, field, addend) \ __this_cpu_add(mib[0]->mibs[field], addend) + #define SNMP_ADD_STATS_USER(mib, field, addend) \ - this_cpu_add(mib[1]->mibs[field], addend) + irqsafe_cpu_add(mib[0]->mibs[field], addend) + #define SNMP_ADD_STATS(mib, field, addend) \ - this_cpu_add(mib[!in_softirq()]->mibs[field], addend) + irqsafe_cpu_add(mib[0]->mibs[field], addend) /* * Use "__typeof__(*mib[0]) *ptr" instead of "__typeof__(mib[0]) ptr" * to make @ptr a non-percpu pointer. */ #define SNMP_UPD_PO_STATS(mib, basefield, addend) \ do { \ - __typeof__(*mib[0]) *ptr; \ - preempt_disable(); \ - ptr = this_cpu_ptr((mib)[!in_softirq()]); \ - ptr->mibs[basefield##PKTS]++; \ - ptr->mibs[basefield##OCTETS] += addend;\ - preempt_enable(); \ + irqsafe_cpu_inc(mib[0]->mibs[basefield##PKTS]); \ + irqsafe_cpu_add(mib[0]->mibs[basefield##OCTETS], addend); \ } while (0) #define SNMP_UPD_PO_STATS_BH(mib, basefield, addend) \ do { \ - __typeof__(*mib[0]) *ptr = \ - __this_cpu_ptr((mib)[0]); \ - ptr->mibs[basefield##PKTS]++; \ - ptr->mibs[basefield##OCTETS] += addend;\ + __this_cpu_inc(mib[0]->mibs[basefield##PKTS]); \ + __this_cpu_add(mib[0]->mibs[basefield##OCTETS], addend); \ } while (0) @@ -179,40 +173,20 @@ struct linux_xfrm_mib { ptr->mibs[field] += addend; \ u64_stats_update_end(&ptr->syncp); \ } while (0) + #define SNMP_ADD_STATS64_USER(mib, field, addend) \ do { \ - __typeof__(*mib[0]) *ptr; \ - preempt_disable(); \ - ptr = __this_cpu_ptr((mib)[1]); \ - u64_stats_update_begin(&ptr->syncp); \ - ptr->mibs[field] += addend; \ - u64_stats_update_end(&ptr->syncp); \ - preempt_enable(); \ + local_bh_disable(); \ + SNMP_ADD_STATS64_BH(mib, field, addend); \ + local_bh_enable(); \ } while (0) + #define SNMP_ADD_STATS64(mib, field, addend) \ - do { \ - __typeof__(*mib[0]) *ptr; \ - preempt_disable(); \ - ptr = __this_cpu_ptr((mib)[!in_softirq()]); \ - u64_stats_update_begin(&ptr->syncp); \ - ptr->mibs[field] += addend; \ - u64_stats_update_end(&ptr->syncp); \ - preempt_enable(); \ - } while (0) + SNMP_ADD_STATS64_USER(mib, field, addend) + #define SNMP_INC_STATS64_BH(mib, field) SNMP_ADD_STATS64_BH(mib, field, 1) #define SNMP_INC_STATS64_USER(mib, field) SNMP_ADD_STATS64_USER(mib, field, 1) #define SNMP_INC_STATS64(mib, field) SNMP_ADD_STATS64(mib, field, 1) -#define SNMP_UPD_PO_STATS64(mib, basefield, addend) \ - do { \ - __typeof__(*mib[0]) *ptr; \ - preempt_disable(); \ - ptr = __this_cpu_ptr((mib)[!in_softirq()]); \ - u64_stats_update_begin(&ptr->syncp); \ - ptr->mibs[basefield##PKTS]++; \ - ptr->mibs[basefield##OCTETS] += addend; \ - u64_stats_update_end(&ptr->syncp); \ - preempt_enable(); \ - } while (0) #define SNMP_UPD_PO_STATS64_BH(mib, basefield, addend) \ do { \ __typeof__(*mib[0]) *ptr; \ @@ -222,6 +196,12 @@ struct linux_xfrm_mib { ptr->mibs[basefield##OCTETS] += addend; \ u64_stats_update_end(&ptr->syncp); \ } while (0) +#define SNMP_UPD_PO_STATS64(mib, basefield, addend) \ + do { \ + local_bh_disable(); \ + SNMP_UPD_PO_STATS64_BH(mib, basefield, addend); \ + local_bh_enable(); \ + } while (0) #else #define SNMP_INC_STATS64_BH(mib, field) SNMP_INC_STATS_BH(mib, field) #define SNMP_INC_STATS64_USER(mib, field) SNMP_INC_STATS_USER(mib, field) diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 9c1926027a26..83673d23d4dd 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1437,11 +1437,11 @@ EXPORT_SYMBOL_GPL(inet_ctl_sock_create); unsigned long snmp_fold_field(void __percpu *mib[], int offt) { unsigned long res = 0; - int i; + int i, j; for_each_possible_cpu(i) { - res += *(((unsigned long *) per_cpu_ptr(mib[0], i)) + offt); - res += *(((unsigned long *) per_cpu_ptr(mib[1], i)) + offt); + for (j = 0; j < SNMP_ARRAY_SZ; j++) + res += *(((unsigned long *) per_cpu_ptr(mib[j], i)) + offt); } return res; } @@ -1455,28 +1455,19 @@ u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset) int cpu; for_each_possible_cpu(cpu) { - void *bhptr, *userptr; + void *bhptr; struct u64_stats_sync *syncp; - u64 v_bh, v_user; + u64 v; unsigned int start; - /* first mib used by softirq context, we must use _bh() accessors */ - bhptr = per_cpu_ptr(SNMP_STAT_BHPTR(mib), cpu); + bhptr = per_cpu_ptr(mib[0], cpu); syncp = (struct u64_stats_sync *)(bhptr + syncp_offset); do { start = u64_stats_fetch_begin_bh(syncp); - v_bh = *(((u64 *) bhptr) + offt); + v = *(((u64 *) bhptr) + offt); } while (u64_stats_fetch_retry_bh(syncp, start)); - /* second mib used in USER context */ - userptr = per_cpu_ptr(SNMP_STAT_USRPTR(mib), cpu); - syncp = (struct u64_stats_sync *)(userptr + syncp_offset); - do { - start = u64_stats_fetch_begin(syncp); - v_user = *(((u64 *) userptr) + offt); - } while (u64_stats_fetch_retry(syncp, start)); - - res += v_bh + v_user; + res += v; } return res; } @@ -1488,25 +1479,28 @@ int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align) BUG_ON(ptr == NULL); ptr[0] = __alloc_percpu(mibsize, align); if (!ptr[0]) - goto err0; + return -ENOMEM; +#if SNMP_ARRAY_SZ == 2 ptr[1] = __alloc_percpu(mibsize, align); - if (!ptr[1]) - goto err1; + if (!ptr[1]) { + free_percpu(ptr[0]); + ptr[0] = NULL; + return -ENOMEM; + } +#endif return 0; -err1: - free_percpu(ptr[0]); - ptr[0] = NULL; -err0: - return -ENOMEM; } EXPORT_SYMBOL_GPL(snmp_mib_init); -void snmp_mib_free(void __percpu *ptr[2]) +void snmp_mib_free(void __percpu *ptr[SNMP_ARRAY_SZ]) { + int i; + BUG_ON(ptr == NULL); - free_percpu(ptr[0]); - free_percpu(ptr[1]); - ptr[0] = ptr[1] = NULL; + for (i = 0; i < SNMP_ARRAY_SZ; i++) { + free_percpu(ptr[i]); + ptr[i] = NULL; + } } EXPORT_SYMBOL_GPL(snmp_mib_free); -- cgit v1.2.3 From a685e08987d1edf1995b76511d4c98ea0e905377 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 8 Jun 2011 21:13:01 -0400 Subject: Delay struct net freeing while there's a sysfs instance refering to it * new refcount in struct net, controlling actual freeing of the memory * new method in kobj_ns_type_operations (->drop_ns()) * ->current_ns() semantics change - it's supposed to be followed by corresponding ->drop_ns(). For struct net in case of CONFIG_NET_NS it bumps the new refcount; net_drop_ns() decrements it and calls net_free() if the last reference has been dropped. Method renamed to ->grab_current_ns(). * old net_free() callers call net_drop_ns() instead. * sysfs_exit_ns() is gone, along with a large part of callchain leading to it; now that the references stored in ->ns[...] stay valid we do not need to hunt them down and replace them with NULL. That fixes problems in sysfs_lookup() and sysfs_readdir(), along with getting rid of sb->s_instances abuse. Note that struct net *shutdown* logics has not changed - net_cleanup() is called exactly when it used to be called. The only thing postponed by having a sysfs instance refering to that struct net is actual freeing of memory occupied by struct net. Signed-off-by: Al Viro --- fs/sysfs/mount.c | 37 +++++++++++-------------------------- fs/sysfs/sysfs.h | 2 +- include/linux/kobject_ns.h | 10 ++++++---- include/linux/sysfs.h | 7 ------- include/net/net_namespace.h | 10 +++++++++- lib/kobject.c | 26 +++++++++----------------- net/core/net-sysfs.c | 23 +++++++++-------------- net/core/net_namespace.c | 12 ++++++++++-- 8 files changed, 55 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 266895783b47..e34f0d99ea4e 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -95,6 +95,14 @@ static int sysfs_set_super(struct super_block *sb, void *data) return error; } +static void free_sysfs_super_info(struct sysfs_super_info *info) +{ + int type; + for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) + kobj_ns_drop(type, info->ns[type]); + kfree(info); +} + static struct dentry *sysfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { @@ -108,11 +116,11 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, return ERR_PTR(-ENOMEM); for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) - info->ns[type] = kobj_ns_current(type); + info->ns[type] = kobj_ns_grab_current(type); sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info); if (IS_ERR(sb) || sb->s_fs_info != info) - kfree(info); + free_sysfs_super_info(info); if (IS_ERR(sb)) return ERR_CAST(sb); if (!sb->s_root) { @@ -131,12 +139,11 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, static void sysfs_kill_sb(struct super_block *sb) { struct sysfs_super_info *info = sysfs_info(sb); - /* Remove the superblock from fs_supers/s_instances * so we can't find it, before freeing sysfs_super_info. */ kill_anon_super(sb); - kfree(info); + free_sysfs_super_info(info); } static struct file_system_type sysfs_fs_type = { @@ -145,28 +152,6 @@ static struct file_system_type sysfs_fs_type = { .kill_sb = sysfs_kill_sb, }; -void sysfs_exit_ns(enum kobj_ns_type type, const void *ns) -{ - struct super_block *sb; - - mutex_lock(&sysfs_mutex); - spin_lock(&sb_lock); - list_for_each_entry(sb, &sysfs_fs_type.fs_supers, s_instances) { - struct sysfs_super_info *info = sysfs_info(sb); - /* - * If we see a superblock on the fs_supers/s_instances - * list the unmount has not completed and sb->s_fs_info - * points to a valid struct sysfs_super_info. - */ - /* Ignore superblocks with the wrong ns */ - if (info->ns[type] != ns) - continue; - info->ns[type] = NULL; - } - spin_unlock(&sb_lock); - mutex_unlock(&sysfs_mutex); -} - int __init sysfs_init(void) { int err = -ENOMEM; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 3d28af31d863..2ed2404f3113 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -136,7 +136,7 @@ struct sysfs_addrm_cxt { * instance). */ struct sysfs_super_info { - const void *ns[KOBJ_NS_TYPES]; + void *ns[KOBJ_NS_TYPES]; }; #define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) extern struct sysfs_dirent sysfs_root; diff --git a/include/linux/kobject_ns.h b/include/linux/kobject_ns.h index 82cb5bf461fb..f66b065a8b5f 100644 --- a/include/linux/kobject_ns.h +++ b/include/linux/kobject_ns.h @@ -32,15 +32,17 @@ enum kobj_ns_type { /* * Callbacks so sysfs can determine namespaces - * @current_ns: return calling task's namespace + * @grab_current_ns: return a new reference to calling task's namespace * @netlink_ns: return namespace to which a sock belongs (right?) * @initial_ns: return the initial namespace (i.e. init_net_ns) + * @drop_ns: drops a reference to namespace */ struct kobj_ns_type_operations { enum kobj_ns_type type; - const void *(*current_ns)(void); + void *(*grab_current_ns)(void); const void *(*netlink_ns)(struct sock *sk); const void *(*initial_ns)(void); + void (*drop_ns)(void *); }; int kobj_ns_type_register(const struct kobj_ns_type_operations *ops); @@ -48,9 +50,9 @@ int kobj_ns_type_registered(enum kobj_ns_type type); const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent); const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj); -const void *kobj_ns_current(enum kobj_ns_type type); +void *kobj_ns_grab_current(enum kobj_ns_type type); const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk); const void *kobj_ns_initial(enum kobj_ns_type type); -void kobj_ns_exit(enum kobj_ns_type type, const void *ns); +void kobj_ns_drop(enum kobj_ns_type type, void *ns); #endif /* _LINUX_KOBJECT_NS_H */ diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index c3acda60eee0..e2696d76a599 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -177,9 +177,6 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd); void sysfs_put(struct sysfs_dirent *sd); -/* Called to clear a ns tag when it is no longer valid */ -void sysfs_exit_ns(enum kobj_ns_type type, const void *tag); - int __must_check sysfs_init(void); #else /* CONFIG_SYSFS */ @@ -338,10 +335,6 @@ static inline void sysfs_put(struct sysfs_dirent *sd) { } -static inline void sysfs_exit_ns(int type, const void *tag) -{ -} - static inline int __must_check sysfs_init(void) { return 0; diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 2bf9ed9ef26b..aef430d779bd 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -35,8 +35,11 @@ struct netns_ipvs; #define NETDEV_HASHENTRIES (1 << NETDEV_HASHBITS) struct net { + atomic_t passive; /* To decided when the network + * namespace should be freed. + */ atomic_t count; /* To decided when the network - * namespace should be freed. + * namespace should be shut down. */ #ifdef NETNS_REFCNT_DEBUG atomic_t use_count; /* To track references we @@ -154,6 +157,9 @@ int net_eq(const struct net *net1, const struct net *net2) { return net1 == net2; } + +extern void net_drop_ns(void *); + #else static inline struct net *get_net(struct net *net) @@ -175,6 +181,8 @@ int net_eq(const struct net *net1, const struct net *net2) { return 1; } + +#define net_drop_ns NULL #endif diff --git a/lib/kobject.c b/lib/kobject.c index 82dc34c095c2..640bd98a4c8a 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -948,14 +948,14 @@ const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj) } -const void *kobj_ns_current(enum kobj_ns_type type) +void *kobj_ns_grab_current(enum kobj_ns_type type) { - const void *ns = NULL; + void *ns = NULL; spin_lock(&kobj_ns_type_lock); if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && kobj_ns_ops_tbl[type]) - ns = kobj_ns_ops_tbl[type]->current_ns(); + ns = kobj_ns_ops_tbl[type]->grab_current_ns(); spin_unlock(&kobj_ns_type_lock); return ns; @@ -987,23 +987,15 @@ const void *kobj_ns_initial(enum kobj_ns_type type) return ns; } -/* - * kobj_ns_exit - invalidate a namespace tag - * - * @type: the namespace type (i.e. KOBJ_NS_TYPE_NET) - * @ns: the actual namespace being invalidated - * - * This is called when a tag is no longer valid. For instance, - * when a network namespace exits, it uses this helper to - * make sure no sb's sysfs_info points to the now-invalidated - * netns. - */ -void kobj_ns_exit(enum kobj_ns_type type, const void *ns) +void kobj_ns_drop(enum kobj_ns_type type, void *ns) { - sysfs_exit_ns(type, ns); + spin_lock(&kobj_ns_type_lock); + if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && + kobj_ns_ops_tbl[type] && kobj_ns_ops_tbl[type]->drop_ns) + kobj_ns_ops_tbl[type]->drop_ns(ns); + spin_unlock(&kobj_ns_type_lock); } - EXPORT_SYMBOL(kobject_get); EXPORT_SYMBOL(kobject_put); EXPORT_SYMBOL(kobject_del); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 11b98bc2aa8f..33d2a1fba131 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1179,9 +1179,14 @@ static void remove_queue_kobjects(struct net_device *net) #endif } -static const void *net_current_ns(void) +static void *net_grab_current_ns(void) { - return current->nsproxy->net_ns; + struct net *ns = current->nsproxy->net_ns; +#ifdef CONFIG_NET_NS + if (ns) + atomic_inc(&ns->passive); +#endif + return ns; } static const void *net_initial_ns(void) @@ -1196,22 +1201,13 @@ static const void *net_netlink_ns(struct sock *sk) struct kobj_ns_type_operations net_ns_type_operations = { .type = KOBJ_NS_TYPE_NET, - .current_ns = net_current_ns, + .grab_current_ns = net_grab_current_ns, .netlink_ns = net_netlink_ns, .initial_ns = net_initial_ns, + .drop_ns = net_drop_ns, }; EXPORT_SYMBOL_GPL(net_ns_type_operations); -static void net_kobj_ns_exit(struct net *net) -{ - kobj_ns_exit(KOBJ_NS_TYPE_NET, net); -} - -static struct pernet_operations kobj_net_ops = { - .exit = net_kobj_ns_exit, -}; - - #ifdef CONFIG_HOTPLUG static int netdev_uevent(struct device *d, struct kobj_uevent_env *env) { @@ -1339,6 +1335,5 @@ EXPORT_SYMBOL(netdev_class_remove_file); int netdev_kobject_init(void) { kobj_ns_type_register(&net_ns_type_operations); - register_pernet_subsys(&kobj_net_ops); return class_register(&net_class); } diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 6c6b86d0da15..cdcbc3cb00a9 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -128,6 +128,7 @@ static __net_init int setup_net(struct net *net) LIST_HEAD(net_exit_list); atomic_set(&net->count, 1); + atomic_set(&net->passive, 1); #ifdef NETNS_REFCNT_DEBUG atomic_set(&net->use_count, 0); @@ -210,6 +211,13 @@ static void net_free(struct net *net) kmem_cache_free(net_cachep, net); } +void net_drop_ns(void *p) +{ + struct net *ns = p; + if (ns && atomic_dec_and_test(&ns->passive)) + net_free(ns); +} + struct net *copy_net_ns(unsigned long flags, struct net *old_net) { struct net *net; @@ -230,7 +238,7 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net) } mutex_unlock(&net_mutex); if (rv < 0) { - net_free(net); + net_drop_ns(net); return ERR_PTR(rv); } return net; @@ -286,7 +294,7 @@ static void cleanup_net(struct work_struct *work) /* Finally it is safe to free my network namespace structure */ list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) { list_del_init(&net->exit_list); - net_free(net); + net_drop_ns(net); } } static DECLARE_WORK(net_cleanup_work, cleanup_net); -- cgit v1.2.3 From ed78bec4d6873e8971f56557f8ca3bcda38044ea Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Fri, 10 Jun 2011 10:19:09 +0200 Subject: IPVS remove unused var from migration to netns Remove variable ctl_key from struct netns_ipvs, it's a leftover from early netns work. Signed-off-by: Hans Schillstrom Signed-off-by: Simon Horman --- include/net/ip_vs.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 481f856c650f..34a6fa825167 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -836,8 +836,6 @@ struct netns_ipvs { int num_services; /* no of virtual services */ rwlock_t rs_lock; /* real services table */ - /* semaphore for IPVS sockopts. And, [gs]etsockopt may sleep. */ - struct lock_class_key ctl_key; /* ctl_mutex debuging */ /* Trash for destinations */ struct list_head dest_trash; /* Service counters */ -- cgit v1.2.3 From 503cf15a5ecc0f3f7a05ffe04c89fb7496100ee7 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Sun, 1 May 2011 18:50:16 +0200 Subject: IPVS: rename of netns init and cleanup functions. Make it more clear what the functions does, on request by Julian. Signed-off-by: Hans Schillstrom Signed-off-by: Hans Schillstrom Signed-off-by: Simon Horman --- include/net/ip_vs.h | 26 +++++++++++++------------- net/netfilter/ipvs/ip_vs_app.c | 4 ++-- net/netfilter/ipvs/ip_vs_conn.c | 4 ++-- net/netfilter/ipvs/ip_vs_core.c | 36 ++++++++++++++++++------------------ net/netfilter/ipvs/ip_vs_ctl.c | 20 ++++++++++---------- net/netfilter/ipvs/ip_vs_est.c | 4 ++-- net/netfilter/ipvs/ip_vs_proto.c | 4 ++-- net/netfilter/ipvs/ip_vs_sync.c | 4 ++-- 8 files changed, 51 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 34a6fa825167..1b0985fca1e2 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1087,19 +1087,19 @@ ip_vs_control_add(struct ip_vs_conn *cp, struct ip_vs_conn *ctl_cp) /* * IPVS netns init & cleanup functions */ -extern int __ip_vs_estimator_init(struct net *net); -extern int __ip_vs_control_init(struct net *net); -extern int __ip_vs_protocol_init(struct net *net); -extern int __ip_vs_app_init(struct net *net); -extern int __ip_vs_conn_init(struct net *net); -extern int __ip_vs_sync_init(struct net *net); -extern void __ip_vs_conn_cleanup(struct net *net); -extern void __ip_vs_app_cleanup(struct net *net); -extern void __ip_vs_protocol_cleanup(struct net *net); -extern void __ip_vs_control_cleanup(struct net *net); -extern void __ip_vs_estimator_cleanup(struct net *net); -extern void __ip_vs_sync_cleanup(struct net *net); -extern void __ip_vs_service_cleanup(struct net *net); +extern int ip_vs_estimator_net_init(struct net *net); +extern int ip_vs_control_net_init(struct net *net); +extern int ip_vs_protocol_net_init(struct net *net); +extern int ip_vs_app_net_init(struct net *net); +extern int ip_vs_conn_net_init(struct net *net); +extern int ip_vs_sync_net_init(struct net *net); +extern void ip_vs_conn_net_cleanup(struct net *net); +extern void ip_vs_app_net_cleanup(struct net *net); +extern void ip_vs_protocol_net_cleanup(struct net *net); +extern void ip_vs_control_net_cleanup(struct net *net); +extern void ip_vs_estimator_net_cleanup(struct net *net); +extern void ip_vs_sync_net_cleanup(struct net *net); +extern void ip_vs_service_net_cleanup(struct net *net); /* * IPVS application functions diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index 059af3120be7..e223fb749ddf 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -576,7 +576,7 @@ static const struct file_operations ip_vs_app_fops = { }; #endif -int __net_init __ip_vs_app_init(struct net *net) +int __net_init ip_vs_app_net_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -585,7 +585,7 @@ int __net_init __ip_vs_app_init(struct net *net) return 0; } -void __net_exit __ip_vs_app_cleanup(struct net *net) +void __net_exit ip_vs_app_net_cleanup(struct net *net) { proc_net_remove(net, "ip_vs_app"); } diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index bf28ac2fc99b..77c61b0b6d68 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1247,7 +1247,7 @@ flush_again: /* * per netns init and exit */ -int __net_init __ip_vs_conn_init(struct net *net) +int __net_init ip_vs_conn_net_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -1258,7 +1258,7 @@ int __net_init __ip_vs_conn_init(struct net *net) return 0; } -void __net_exit __ip_vs_conn_cleanup(struct net *net) +void __net_exit ip_vs_conn_net_cleanup(struct net *net) { /* flush all the connection entries first */ ip_vs_conn_flush(net); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index bfa808f4da13..7c2c72699c8d 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1891,22 +1891,22 @@ static int __net_init __ip_vs_init(struct net *net) atomic_inc(&ipvs_netns_cnt); net->ipvs = ipvs; - if (__ip_vs_estimator_init(net) < 0) + if (ip_vs_estimator_net_init(net) < 0) goto estimator_fail; - if (__ip_vs_control_init(net) < 0) + if (ip_vs_control_net_init(net) < 0) goto control_fail; - if (__ip_vs_protocol_init(net) < 0) + if (ip_vs_protocol_net_init(net) < 0) goto protocol_fail; - if (__ip_vs_app_init(net) < 0) + if (ip_vs_app_net_init(net) < 0) goto app_fail; - if (__ip_vs_conn_init(net) < 0) + if (ip_vs_conn_net_init(net) < 0) goto conn_fail; - if (__ip_vs_sync_init(net) < 0) + if (ip_vs_sync_net_init(net) < 0) goto sync_fail; printk(KERN_INFO "IPVS: Creating netns size=%zu id=%d\n", @@ -1917,27 +1917,27 @@ static int __net_init __ip_vs_init(struct net *net) */ sync_fail: - __ip_vs_conn_cleanup(net); + ip_vs_conn_net_cleanup(net); conn_fail: - __ip_vs_app_cleanup(net); + ip_vs_app_net_cleanup(net); app_fail: - __ip_vs_protocol_cleanup(net); + ip_vs_protocol_net_cleanup(net); protocol_fail: - __ip_vs_control_cleanup(net); + ip_vs_control_net_cleanup(net); control_fail: - __ip_vs_estimator_cleanup(net); + ip_vs_estimator_net_cleanup(net); estimator_fail: return -ENOMEM; } static void __net_exit __ip_vs_cleanup(struct net *net) { - __ip_vs_service_cleanup(net); /* ip_vs_flush() with locks */ - __ip_vs_conn_cleanup(net); - __ip_vs_app_cleanup(net); - __ip_vs_protocol_cleanup(net); - __ip_vs_control_cleanup(net); - __ip_vs_estimator_cleanup(net); + ip_vs_service_net_cleanup(net); /* ip_vs_flush() with locks */ + ip_vs_conn_net_cleanup(net); + ip_vs_app_net_cleanup(net); + ip_vs_protocol_net_cleanup(net); + ip_vs_control_net_cleanup(net); + ip_vs_estimator_net_cleanup(net); IP_VS_DBG(2, "ipvs netns %d released\n", net_ipvs(net)->gen); } @@ -1945,7 +1945,7 @@ static void __net_exit __ip_vs_dev_cleanup(struct net *net) { EnterFunction(2); net_ipvs(net)->enable = 0; /* Disable packet reception */ - __ip_vs_sync_cleanup(net); + ip_vs_sync_net_cleanup(net); LeaveFunction(2); } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 699c79a55657..6bedea120b10 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1483,7 +1483,7 @@ static int ip_vs_flush(struct net *net) * Delete service by {netns} in the service table. * Called by __ip_vs_cleanup() */ -void __ip_vs_service_cleanup(struct net *net) +void ip_vs_service_net_cleanup(struct net *net) { EnterFunction(2); /* Check for "full" addressed entries */ @@ -1662,7 +1662,7 @@ proc_do_sync_mode(ctl_table *table, int write, /* * IPVS sysctl table (under the /proc/sys/net/ipv4/vs/) * Do not change order or insert new entries without - * align with netns init in __ip_vs_control_init() + * align with netns init in ip_vs_control_net_init() */ static struct ctl_table vs_vars[] = { @@ -3595,7 +3595,7 @@ static void ip_vs_genl_unregister(void) * per netns intit/exit func. */ #ifdef CONFIG_SYSCTL -int __net_init __ip_vs_control_init_sysctl(struct net *net) +int __net_init ip_vs_control_net_init_sysctl(struct net *net) { int idx; struct netns_ipvs *ipvs = net_ipvs(net); @@ -3654,7 +3654,7 @@ int __net_init __ip_vs_control_init_sysctl(struct net *net) return 0; } -void __net_init __ip_vs_control_cleanup_sysctl(struct net *net) +void __net_init ip_vs_control_net_cleanup_sysctl(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -3665,8 +3665,8 @@ void __net_init __ip_vs_control_cleanup_sysctl(struct net *net) #else -int __net_init __ip_vs_control_init_sysctl(struct net *net) { return 0; } -void __net_init __ip_vs_control_cleanup_sysctl(struct net *net) { } +int __net_init ip_vs_control_net_init_sysctl(struct net *net) { return 0; } +void __net_init ip_vs_control_net_cleanup_sysctl(struct net *net) { } #endif @@ -3674,7 +3674,7 @@ static struct notifier_block ip_vs_dst_notifier = { .notifier_call = ip_vs_dst_event, }; -int __net_init __ip_vs_control_init(struct net *net) +int __net_init ip_vs_control_net_init(struct net *net) { int idx; struct netns_ipvs *ipvs = net_ipvs(net); @@ -3702,7 +3702,7 @@ int __net_init __ip_vs_control_init(struct net *net) proc_net_fops_create(net, "ip_vs_stats_percpu", 0, &ip_vs_stats_percpu_fops); - if (__ip_vs_control_init_sysctl(net)) + if (ip_vs_control_net_init_sysctl(net)) goto err; return 0; @@ -3712,13 +3712,13 @@ err: return -ENOMEM; } -void __net_exit __ip_vs_control_cleanup(struct net *net) +void __net_exit ip_vs_control_net_cleanup(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); ip_vs_trash_cleanup(net); ip_vs_stop_estimator(net, &ipvs->tot_stats); - __ip_vs_control_cleanup_sysctl(net); + ip_vs_control_net_cleanup_sysctl(net); proc_net_remove(net, "ip_vs_stats_percpu"); proc_net_remove(net, "ip_vs_stats"); proc_net_remove(net, "ip_vs"); diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 508cce98777c..f5d2a01d69d4 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -192,7 +192,7 @@ void ip_vs_read_estimator(struct ip_vs_stats_user *dst, dst->outbps = (e->outbps + 0xF) >> 5; } -int __net_init __ip_vs_estimator_init(struct net *net) +int __net_init ip_vs_estimator_net_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -203,7 +203,7 @@ int __net_init __ip_vs_estimator_init(struct net *net) return 0; } -void __net_exit __ip_vs_estimator_cleanup(struct net *net) +void __net_exit ip_vs_estimator_net_cleanup(struct net *net) { del_timer_sync(&net_ipvs(net)->est_timer); } diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index eb86028536fc..52d073c105e9 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -316,7 +316,7 @@ ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp, /* * per network name-space init */ -int __net_init __ip_vs_protocol_init(struct net *net) +int __net_init ip_vs_protocol_net_init(struct net *net) { #ifdef CONFIG_IP_VS_PROTO_TCP register_ip_vs_proto_netns(net, &ip_vs_protocol_tcp); @@ -336,7 +336,7 @@ int __net_init __ip_vs_protocol_init(struct net *net) return 0; } -void __net_exit __ip_vs_protocol_cleanup(struct net *net) +void __net_exit ip_vs_protocol_net_cleanup(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_proto_data *pd; diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index e292e5bddc70..58bfabbe7446 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -1663,7 +1663,7 @@ int stop_sync_thread(struct net *net, int state) /* * Initialize data struct for each netns */ -int __net_init __ip_vs_sync_init(struct net *net) +int __net_init ip_vs_sync_net_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -1677,7 +1677,7 @@ int __net_init __ip_vs_sync_init(struct net *net) return 0; } -void __ip_vs_sync_cleanup(struct net *net) +void ip_vs_sync_net_cleanup(struct net *net) { int retc; -- cgit v1.2.3 From 08e8138adebdd511e0955e8d6c051904bb4082af Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 13 Jun 2011 10:42:49 +0200 Subject: block: Add __attribute__((format(printf...) and fix fallout Use the compiler to verify format strings and arguments. Fix fallout. Signed-off-by: Joe Perches Signed-off-by: Jens Axboe --- block/blk-throttle.c | 4 ++-- block/cfq-iosched.c | 11 ++++++----- include/linux/blktrace_api.h | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/block/blk-throttle.c b/block/blk-throttle.c index a62be8d0dc1b..3689f833afdc 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -927,7 +927,7 @@ static int throtl_dispatch(struct request_queue *q) bio_list_init(&bio_list_on_stack); - throtl_log(td, "dispatch nr_queued=%lu read=%u write=%u", + throtl_log(td, "dispatch nr_queued=%d read=%u write=%u", total_nr_queued(td), td->nr_queued[READ], td->nr_queued[WRITE]); @@ -1204,7 +1204,7 @@ int blk_throtl_bio(struct request_queue *q, struct bio **biop) } queue_bio: - throtl_log_tg(td, tg, "[%c] bio. bdisp=%u sz=%u bps=%llu" + throtl_log_tg(td, tg, "[%c] bio. bdisp=%llu sz=%u bps=%llu" " iodisp=%u iops=%u queued=%d/%d", rw == READ ? 'R' : 'W', tg->bytes_disp[rw], bio->bi_size, tg->bps[rw], diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 545b8d4c829d..f3799432676d 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -988,9 +988,10 @@ static void cfq_group_served(struct cfq_data *cfqd, struct cfq_group *cfqg, cfq_log_cfqg(cfqd, cfqg, "served: vt=%llu min_vt=%llu", cfqg->vdisktime, st->min_vdisktime); - cfq_log_cfqq(cfqq->cfqd, cfqq, "sl_used=%u disp=%u charge=%u iops=%u" - " sect=%u", used_sl, cfqq->slice_dispatch, charge, - iops_mode(cfqd), cfqq->nr_sectors); + cfq_log_cfqq(cfqq->cfqd, cfqq, + "sl_used=%u disp=%u charge=%u iops=%u sect=%lu", + used_sl, cfqq->slice_dispatch, charge, + iops_mode(cfqd), cfqq->nr_sectors); cfq_blkiocg_update_timeslice_used(&cfqg->blkg, used_sl, unaccounted_sl); cfq_blkiocg_set_start_empty_time(&cfqg->blkg); @@ -2023,8 +2024,8 @@ static void cfq_arm_slice_timer(struct cfq_data *cfqd) */ if (sample_valid(cic->ttime_samples) && (cfqq->slice_end - jiffies < cic->ttime_mean)) { - cfq_log_cfqq(cfqd, cfqq, "Not idling. think_time:%d", - cic->ttime_mean); + cfq_log_cfqq(cfqd, cfqq, "Not idling. think_time:%lu", + cic->ttime_mean); return; } diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index b22fb0d3db0f..8c7c2de7631a 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -169,7 +169,8 @@ extern void blk_trace_shutdown(struct request_queue *); extern int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev, struct block_device *bdev, struct blk_user_trace_setup *buts); -extern void __trace_note_message(struct blk_trace *, const char *fmt, ...); +extern __attribute__((format(printf, 2, 3))) +void __trace_note_message(struct blk_trace *, const char *fmt, ...); /** * blk_add_trace_msg - Add a (simple) message to the blktrace stream -- cgit v1.2.3 From 4d0d98b60eba726e0a4f3e6617628b070c444707 Mon Sep 17 00:00:00 2001 From: Wanlong Gao Date: Mon, 13 Jun 2011 10:45:38 +0200 Subject: block:fix the comment error in blkdev.h There is not a function rq_init but blk_rq_init in block/blk-core.c. Signed-off-by: Wanlong Gao Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index ae9091a68480..4ce6e68da2bd 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -73,7 +73,7 @@ enum rq_cmd_type_bits { /* * try to put the fields that are referenced together in the same cacheline. - * if you modify this structure, be sure to check block/blk-core.c:rq_init() + * if you modify this structure, be sure to check block/blk-core.c:blk_rq_init() * as well! */ struct request { -- cgit v1.2.3 From 80808e431e1ef25856457de82ce141bed6a6313a Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 16 May 2011 17:24:37 -0300 Subject: Bluetooth: Add l2cap_chan_ops abstraction Add an abstraction layer between L2CAP core and its users (only l2cap_sock.c now). The first function implemented is new_connection() that replaces calls to l2cap_sock_alloc() in l2cap_core.c Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 12 +++++++++--- net/bluetooth/l2cap_core.c | 17 +++++++---------- net/bluetooth/l2cap_sock.c | 29 +++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 0529d278e068..d3a150955c44 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -354,6 +354,15 @@ struct l2cap_chan { struct list_head list; struct list_head global_l; + + void *data; + struct l2cap_ops *ops; +}; + +struct l2cap_ops { + char *name; + + struct l2cap_chan *(*new_connection) (void *data); }; struct l2cap_conn { @@ -460,9 +469,6 @@ int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); void l2cap_sock_kill(struct sock *sk); -void l2cap_sock_init(struct sock *sk, struct sock *parent); -struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, - int proto, gfp_t prio); struct l2cap_chan *l2cap_chan_create(struct sock *sk); void l2cap_chan_close(struct l2cap_chan *chan, int reason); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 49f890bce312..8369f5680391 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -842,18 +842,16 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) goto clean; } - sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC); - if (!sk) + chan = pchan->ops->new_connection(pchan->data); + if (!chan) goto clean; - chan = l2cap_pi(sk)->chan; + sk = chan->sk; write_lock_bh(&conn->chan_lock); hci_conn_hold(conn->hcon); - l2cap_sock_init(sk, parent); - bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); @@ -2329,10 +2327,12 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd goto response; } - sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC); - if (!sk) + chan = pchan->ops->new_connection(pchan->data); + if (!chan) goto response; + sk = chan->sk; + write_lock_bh(&conn->chan_lock); /* Check if we already have channel with that dcid */ @@ -2345,9 +2345,6 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd hci_conn_hold(conn->hcon); - chan = l2cap_pi(sk)->chan; - - l2cap_sock_init(sk, parent); bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); chan->psm = psm; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 2f4fd5751a46..4050edeaf78b 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -31,6 +31,8 @@ #include static const struct proto_ops l2cap_sock_ops; +static void l2cap_sock_init(struct sock *sk, struct sock *parent); +static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { @@ -773,6 +775,25 @@ static int l2cap_sock_release(struct socket *sock) return err; } +static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data) +{ + struct sock *sk, *parent = data; + + sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, + GFP_ATOMIC); + if (!sk) + return NULL; + + l2cap_sock_init(sk, parent); + + return l2cap_pi(sk)->chan; +} + +static struct l2cap_ops l2cap_chan_ops = { + .name = "L2CAP Socket Interface", + .new_connection = l2cap_sock_new_connection_cb, +}; + static void l2cap_sock_destruct(struct sock *sk) { BT_DBG("sk %p", sk); @@ -781,7 +802,7 @@ static void l2cap_sock_destruct(struct sock *sk) skb_queue_purge(&sk->sk_write_queue); } -void l2cap_sock_init(struct sock *sk, struct sock *parent) +static void l2cap_sock_init(struct sock *sk, struct sock *parent) { struct l2cap_pinfo *pi = l2cap_pi(sk); struct l2cap_chan *chan = pi->chan; @@ -838,10 +859,14 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->force_reliable = 0; chan->flushable = BT_FLUSHABLE_OFF; chan->force_active = BT_POWER_FORCE_ACTIVE_ON; + } /* Default config options */ chan->flush_to = L2CAP_DEFAULT_FLUSH_TO; + + chan->data = sk; + chan->ops = &l2cap_chan_ops; } static struct proto l2cap_proto = { @@ -850,7 +875,7 @@ static struct proto l2cap_proto = { .obj_size = sizeof(struct l2cap_pinfo) }; -struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) +static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) { struct sock *sk; struct l2cap_chan *chan; -- cgit v1.2.3 From 230704942283cb3990584ddd6955ac8decfa6a2c Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 16 May 2011 17:57:22 -0300 Subject: Bluetooth: add recv() callback to l2cap_chan_ops This abstracts the call to sock_queue_recv_skb() into l2cap_chan_ops->recv(). Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 16 ++++++++-------- net/bluetooth/l2cap_sock.c | 8 ++++++++ 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d3a150955c44..05bb254c9ebd 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -363,6 +363,7 @@ struct l2cap_ops { char *name; struct l2cap_chan *(*new_connection) (void *data); + int (*recv) (void *data, struct sk_buff *skb); }; struct l2cap_conn { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8369f5680391..4cbb48d523e4 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1698,7 +1698,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) if (!nskb) continue; - if (sock_queue_rcv_skb(sk, nskb)) + if (chan->ops->recv(chan->data, nskb)) kfree_skb(nskb); } read_unlock(&conn->chan_lock); @@ -3124,7 +3124,7 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk if (chan->conn_state & L2CAP_CONN_SAR_SDU) goto drop; - return sock_queue_rcv_skb(chan->sk, skb); + return chan->ops->recv(chan->data, skb); case L2CAP_SDU_START: if (chan->conn_state & L2CAP_CONN_SAR_SDU) @@ -3190,7 +3190,7 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk return -ENOMEM; } - err = sock_queue_rcv_skb(chan->sk, _skb); + err = chan->ops->recv(chan->data, _skb); if (err < 0) { kfree_skb(_skb); chan->conn_state |= L2CAP_CONN_SAR_RETRY; @@ -3358,7 +3358,7 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf break; } - err = sock_queue_rcv_skb(chan->sk, skb); + err = chan->ops->recv(chan->data, skb); if (!err) return 0; @@ -3419,7 +3419,7 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf if (chan->partial_sdu_len == chan->sdu_len) { _skb = skb_clone(chan->sdu, GFP_ATOMIC); - err = sock_queue_rcv_skb(chan->sk, _skb); + err = chan->ops->recv(chan->data, _skb); if (err < 0) kfree_skb(_skb); } @@ -3886,7 +3886,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (chan->imtu < skb->len) goto drop; - if (!sock_queue_rcv_skb(sk, skb)) + if (!chan->ops->recv(chan->data, skb)) goto done; break; @@ -3964,7 +3964,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str if (l2cap_pi(sk)->chan->imtu < skb->len) goto drop; - if (!sock_queue_rcv_skb(sk, skb)) + if (!chan->ops->recv(chan->data, skb)) goto done; drop: @@ -3997,7 +3997,7 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct if (l2cap_pi(sk)->chan->imtu < skb->len) goto drop; - if (!sock_queue_rcv_skb(sk, skb)) + if (!chan->ops->recv(chan->data, skb)) goto done; drop: diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 4050edeaf78b..28cdc7e6df54 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -789,9 +789,17 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data) return l2cap_pi(sk)->chan; } +static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb) +{ + struct sock *sk = data; + + return sock_queue_rcv_skb(sk, skb); +} + static struct l2cap_ops l2cap_chan_ops = { .name = "L2CAP Socket Interface", .new_connection = l2cap_sock_new_connection_cb, + .recv = l2cap_sock_recv_cb, }; static void l2cap_sock_destruct(struct sock *sk) -- cgit v1.2.3 From ba3bd0ee3946d9300512e685e8d5573dfa10c060 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 16 May 2011 18:23:24 -0300 Subject: Bluetooth: add close() callback to l2cap_chan_ops close() calls l2cap_sock_kill() on l2cap_sock.c Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 3 +-- net/bluetooth/l2cap_core.c | 17 +++++++++-------- net/bluetooth/l2cap_sock.c | 10 +++++++++- 3 files changed, 19 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 05bb254c9ebd..0ad61d069686 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -364,6 +364,7 @@ struct l2cap_ops { struct l2cap_chan *(*new_connection) (void *data); int (*recv) (void *data, struct sk_buff *skb); + void (*close) (void *data); }; struct l2cap_conn { @@ -469,8 +470,6 @@ int __l2cap_wait_ack(struct sock *sk); int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); -void l2cap_sock_kill(struct sock *sk); - 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); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 4cbb48d523e4..3a121ac6b310 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -254,7 +254,7 @@ static void l2cap_chan_timeout(unsigned long arg) bh_unlock_sock(sk); - l2cap_sock_kill(sk); + chan->ops->close(chan->data); sock_put(sk); } @@ -391,11 +391,12 @@ static void l2cap_chan_cleanup_listen(struct sock *parent) /* Close not yet accepted channels */ while ((sk = bt_accept_dequeue(parent, NULL))) { - l2cap_chan_clear_timer(l2cap_pi(sk)->chan); + struct l2cap_chan *chan = l2cap_pi(sk)->chan; + l2cap_chan_clear_timer(chan); lock_sock(sk); - l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET); + l2cap_chan_close(chan, ECONNRESET); release_sock(sk); - l2cap_sock_kill(sk); + chan->ops->close(chan->data); } parent->sk_state = BT_CLOSED; @@ -993,7 +994,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) bh_lock_sock(sk); l2cap_chan_del(chan, err); bh_unlock_sock(sk); - l2cap_sock_kill(sk); + chan->ops->close(chan->data); } if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) @@ -2339,7 +2340,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd if (__l2cap_get_chan_by_dcid(conn, scid)) { write_unlock_bh(&conn->chan_lock); sock_set_flag(sk, SOCK_ZAPPED); - l2cap_sock_kill(sk); + chan->ops->close(chan->data); goto response; } @@ -2712,7 +2713,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd l2cap_chan_del(chan, ECONNRESET); bh_unlock_sock(sk); - l2cap_sock_kill(sk); + chan->ops->close(chan->data); return 0; } @@ -2746,7 +2747,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd l2cap_chan_del(chan, 0); bh_unlock_sock(sk); - l2cap_sock_kill(sk); + chan->ops->close(chan->data); return 0; } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 28cdc7e6df54..9f15a164993a 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -713,7 +713,7 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms /* Kill socket (only if zapped and orphan) * Must be called on unlocked socket. */ -void l2cap_sock_kill(struct sock *sk) +static void l2cap_sock_kill(struct sock *sk) { if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) return; @@ -796,10 +796,18 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb) return sock_queue_rcv_skb(sk, skb); } +static void l2cap_sock_close_cb(void *data) +{ + struct sock *sk = data; + + l2cap_sock_kill(sk); +} + 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, }; static void l2cap_sock_destruct(struct sock *sk) -- cgit v1.2.3 From 89bc500e41fc5b48e0573e6b0d927fc97b8951dc Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 3 Jun 2011 00:19:47 -0300 Subject: Bluetooth: Add state tracking to struct l2cap_chan Now socket state is tracked by struct sock and channel state is tracked by chan->state. At this point both says the same, but this is going to change when we add AMP Support for example. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 3 ++ net/bluetooth/l2cap_core.c | 113 ++++++++++++++++++++++-------------------- net/bluetooth/l2cap_sock.c | 12 +++++ 3 files changed, 74 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 0ad61d069686..68c87244eafc 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -287,6 +287,8 @@ struct l2cap_chan { struct l2cap_conn *conn; + __u8 state; + __le16 psm; __u16 dcid; __u16 scid; @@ -365,6 +367,7 @@ struct l2cap_ops { struct l2cap_chan *(*new_connection) (void *data); int (*recv) (void *data, struct sk_buff *skb); void (*close) (void *data); + void (*state_change) (void *data, int state); }; struct l2cap_conn { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3a121ac6b310..58fe03abee66 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -210,27 +210,33 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout) { - BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->sk->sk_state, - timeout); + BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); + if (!mod_timer(&chan->chan_timer, jiffies + timeout)) sock_hold(chan->sk); } static void l2cap_chan_clear_timer(struct l2cap_chan *chan) { - BT_DBG("chan %p state %d", chan, chan->sk->sk_state); + BT_DBG("chan %p state %d", chan, chan->state); if (timer_pending(&chan->chan_timer) && del_timer(&chan->chan_timer)) __sock_put(chan->sk); } +static void l2cap_state_change(struct l2cap_chan *chan, int state) +{ + chan->state = state; + chan->ops->state_change(chan->data, state); +} + static void l2cap_chan_timeout(unsigned long arg) { struct l2cap_chan *chan = (struct l2cap_chan *) arg; struct sock *sk = chan->sk; int reason; - BT_DBG("chan %p state %d", chan, sk->sk_state); + BT_DBG("chan %p state %d", chan, chan->state); bh_lock_sock(sk); @@ -242,9 +248,9 @@ static void l2cap_chan_timeout(unsigned long arg) return; } - if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) + if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG) reason = ECONNREFUSED; - else if (sk->sk_state == BT_CONNECT && + else if (chan->state == BT_CONNECT && chan->sec_level != BT_SECURITY_SDP) reason = ECONNREFUSED; else @@ -274,6 +280,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk) setup_timer(&chan->chan_timer, l2cap_chan_timeout, (unsigned long) chan); + chan->state = BT_OPEN; + return chan; } @@ -348,7 +356,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) hci_conn_put(conn->hcon); } - sk->sk_state = BT_CLOSED; + l2cap_state_change(chan, BT_CLOSED); sock_set_flag(sk, SOCK_ZAPPED); if (err) @@ -398,9 +406,6 @@ static void l2cap_chan_cleanup_listen(struct sock *parent) release_sock(sk); chan->ops->close(chan->data); } - - parent->sk_state = BT_CLOSED; - sock_set_flag(parent, SOCK_ZAPPED); } void l2cap_chan_close(struct l2cap_chan *chan, int reason) @@ -408,11 +413,14 @@ 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, sk->sk_state, sk->sk_socket); + BT_DBG("chan %p state %d socket %p", chan, chan->state, sk->sk_socket); - switch (sk->sk_state) { + switch (chan->state) { case BT_LISTEN: l2cap_chan_cleanup_listen(sk); + + l2cap_state_change(chan, BT_CLOSED); + sock_set_flag(sk, SOCK_ZAPPED); break; case BT_CONNECTED: @@ -436,7 +444,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) result = L2CAP_CR_SEC_BLOCK; else result = L2CAP_CR_BAD_PSM; - sk->sk_state = BT_DISCONN; + l2cap_state_change(chan, BT_DISCONN); rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); @@ -548,13 +556,11 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) { struct sk_buff *skb; struct l2cap_hdr *lh; - struct l2cap_pinfo *pi = l2cap_pi(chan->sk); struct l2cap_conn *conn = chan->conn; - struct sock *sk = (struct sock *)pi; int count, hlen = L2CAP_HDR_SIZE + 2; u8 flags; - if (sk->sk_state != BT_CONNECTED) + if (chan->state != BT_CONNECTED) return; if (chan->fcs == L2CAP_FCS_CRC16) @@ -689,7 +695,7 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ, sizeof(req), &req); - sk->sk_state = BT_DISCONN; + l2cap_state_change(chan, BT_DISCONN); sk->sk_err = err; } @@ -712,7 +718,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) continue; } - if (sk->sk_state == BT_CONNECT) { + if (chan->state == BT_CONNECT) { struct l2cap_conn_req req; if (!l2cap_check_security(chan) || @@ -728,7 +734,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) /* l2cap_chan_close() calls list_del(chan) * so release the lock */ read_unlock_bh(&conn->chan_lock); - l2cap_chan_close(chan, ECONNRESET); + l2cap_chan_close(chan, ECONNRESET); read_lock_bh(&conn->chan_lock); bh_unlock_sock(sk); continue; @@ -743,7 +749,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); - } else if (sk->sk_state == BT_CONNECT2) { + } else if (chan->state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; char buf[128]; rsp.scid = cpu_to_le16(chan->dcid); @@ -757,7 +763,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) parent->sk_data_ready(parent, 0); } else { - sk->sk_state = BT_CONFIG; + l2cap_state_change(chan, BT_CONFIG); rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); } @@ -799,7 +805,7 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, __le16 cid, bdadd list_for_each_entry(c, &chan_list, global_l) { struct sock *sk = c->sk; - if (state && sk->sk_state != state) + if (state && c->state != state) continue; if (c->scid == cid) { @@ -862,7 +868,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) l2cap_chan_set_timer(chan, sk->sk_sndtimeo); - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); parent->sk_data_ready(parent, 0); write_unlock_bh(&conn->chan_lock); @@ -889,15 +895,15 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) if (conn->hcon->type == LE_LINK) { l2cap_chan_clear_timer(chan); - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); } if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { l2cap_chan_clear_timer(chan); - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); - } else if (sk->sk_state == BT_CONNECT) + } else if (chan->state == BT_CONNECT) l2cap_do_start(chan); bh_unlock_sock(sk); @@ -1025,7 +1031,7 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr list_for_each_entry(c, &chan_list, global_l) { struct sock *sk = c->sk; - if (state && sk->sk_state != state) + if (state && c->state != state) continue; if (c->psm == psm) { @@ -1092,14 +1098,14 @@ int l2cap_chan_connect(struct l2cap_chan *chan) l2cap_chan_add(conn, chan); - sk->sk_state = BT_CONNECT; + l2cap_state_change(chan, BT_CONNECT); l2cap_chan_set_timer(chan, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { l2cap_chan_clear_timer(chan); if (l2cap_check_security(chan)) - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); } else l2cap_do_start(chan); } @@ -1288,11 +1294,10 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) int l2cap_ertm_send(struct l2cap_chan *chan) { struct sk_buff *skb, *tx_skb; - struct sock *sk = chan->sk; u16 control, fcs; int nsent = 0; - if (sk->sk_state != BT_CONNECTED) + if (chan->state != BT_CONNECTED) return -ENOTCONN; while ((skb = chan->tx_send_head) && (!l2cap_tx_window_full(chan))) { @@ -1668,7 +1673,7 @@ static void l2cap_chan_ready(struct sock *sk) /* Outgoing channel. * Wake up socket sleeping on connect. */ - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); } else { /* Incoming channel. @@ -2364,22 +2369,22 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { if (l2cap_check_security(chan)) { if (bt_sk(sk)->defer_setup) { - sk->sk_state = BT_CONNECT2; + l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_AUTHOR_PEND; parent->sk_data_ready(parent, 0); } else { - sk->sk_state = BT_CONFIG; + l2cap_state_change(chan, BT_CONFIG); result = L2CAP_CR_SUCCESS; status = L2CAP_CS_NO_INFO; } } else { - sk->sk_state = BT_CONNECT2; + l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_AUTHEN_PEND; } } else { - sk->sk_state = BT_CONNECT2; + l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_NO_INFO; } @@ -2451,7 +2456,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd switch (result) { case L2CAP_CR_SUCCESS: - sk->sk_state = BT_CONFIG; + l2cap_state_change(chan, BT_CONFIG); chan->ident = 0; chan->dcid = dcid; chan->conf_state &= ~L2CAP_CONF_CONNECT_PEND; @@ -2473,7 +2478,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd default: /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { - sk->sk_state = BT_DISCONN; + l2cap_state_change(chan, BT_DISCONN); l2cap_chan_clear_timer(chan); l2cap_chan_set_timer(chan, HZ / 5); break; @@ -2520,7 +2525,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr sk = chan->sk; - if (sk->sk_state != BT_CONFIG) { + if (chan->state != BT_CONFIG) { struct l2cap_cmd_rej rej; rej.reason = cpu_to_le16(0x0002); @@ -2569,7 +2574,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (chan->conf_state & L2CAP_CONF_INPUT_DONE) { set_default_fcs(chan); - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); chan->next_tx_seq = 0; chan->expected_tx_seq = 0; @@ -2661,7 +2666,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (chan->conf_state & L2CAP_CONF_OUTPUT_DONE) { set_default_fcs(chan); - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); chan->next_tx_seq = 0; chan->expected_tx_seq = 0; skb_queue_head_init(&chan->tx_q); @@ -2703,7 +2708,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { - sk->sk_state = BT_DISCONN; + l2cap_state_change(chan, BT_DISCONN); l2cap_chan_clear_timer(chan); l2cap_chan_set_timer(chan, HZ / 5); bh_unlock_sock(sk); @@ -2737,7 +2742,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { - sk->sk_state = BT_DISCONN; + l2cap_state_change(chan,BT_DISCONN); l2cap_chan_clear_timer(chan); l2cap_chan_set_timer(chan, HZ / 5); bh_unlock_sock(sk); @@ -3874,7 +3879,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk BT_DBG("chan %p, len %d", chan, skb->len); - if (sk->sk_state != BT_CONNECTED) + if (chan->state != BT_CONNECTED) goto drop; switch (chan->mode) { @@ -3959,7 +3964,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str BT_DBG("sk %p, len %d", sk, skb->len); - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED) + if (chan->state != BT_BOUND && chan->state != BT_CONNECTED) goto drop; if (l2cap_pi(sk)->chan->imtu < skb->len) @@ -3992,7 +3997,7 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct BT_DBG("sk %p, len %d", sk, skb->len); - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED) + if (chan->state != BT_BOUND && chan->state != BT_CONNECTED) goto drop; if (l2cap_pi(sk)->chan->imtu < skb->len) @@ -4066,7 +4071,7 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) list_for_each_entry(c, &chan_list, global_l) { struct sock *sk = c->sk; - if (sk->sk_state != BT_LISTEN) + if (c->state != BT_LISTEN) continue; if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { @@ -4167,14 +4172,14 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) continue; } - if (!status && (sk->sk_state == BT_CONNECTED || - sk->sk_state == BT_CONFIG)) { + if (!status && (chan->state == BT_CONNECTED || + chan->state == BT_CONFIG)) { l2cap_check_encryption(chan, encrypt); bh_unlock_sock(sk); continue; } - if (sk->sk_state == BT_CONNECT) { + if (chan->state == BT_CONNECT) { if (!status) { struct l2cap_conn_req req; req.scid = cpu_to_le16(chan->scid); @@ -4189,15 +4194,15 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) l2cap_chan_clear_timer(chan); l2cap_chan_set_timer(chan, HZ / 10); } - } else if (sk->sk_state == BT_CONNECT2) { + } else if (chan->state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; __u16 result; if (!status) { - sk->sk_state = BT_CONFIG; + l2cap_state_change(chan, BT_CONFIG); result = L2CAP_CR_SUCCESS; } else { - sk->sk_state = BT_DISCONN; + l2cap_state_change(chan, BT_DISCONN); l2cap_chan_set_timer(chan, HZ / 10); result = L2CAP_CR_SEC_BLOCK; } @@ -4341,7 +4346,7 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p) seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n", batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), - sk->sk_state, __le16_to_cpu(c->psm), + c->state, __le16_to_cpu(c->psm), c->scid, c->dcid, c->imtu, c->omtu, c->sec_level, c->mode); } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 9f15a164993a..1d9c36509d7b 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -89,6 +89,8 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) chan->sec_level = BT_SECURITY_SDP; bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); + + chan->state = BT_BOUND; sk->sk_state = BT_BOUND; done: @@ -214,6 +216,8 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; + + chan->state = BT_LISTEN; sk->sk_state = BT_LISTEN; done: @@ -803,11 +807,19 @@ static void l2cap_sock_close_cb(void *data) l2cap_sock_kill(sk); } +static void l2cap_sock_state_change_cb(void *data, int state) +{ + struct sock *sk = data; + + sk->sk_state = state; +} + 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, }; static void l2cap_sock_destruct(struct sock *sk) -- cgit v1.2.3 From 71ba0e569bb43ab99a07ccbb514f8b0f732140c3 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 17 May 2011 14:34:52 -0300 Subject: Bluetooth: Add refcnt to struct l2cap_chan struct l2cap_chan has now its own refcnt that is compatible with the socket refcnt, i.e., we won't see sk_refcnt = 0 and chan->refcnt > 0. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 ++ net/bluetooth/l2cap_core.c | 30 +++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 68c87244eafc..b3d953b5f399 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -289,6 +289,8 @@ struct l2cap_chan { __u8 state; + atomic_t refcnt; + __le16 psm; __u16 dcid; __u16 scid; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 58fe03abee66..f24022c39869 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -78,6 +78,18 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb); /* ---- L2CAP channels ---- */ + +static inline void chan_hold(struct l2cap_chan *c) +{ + atomic_inc(&c->refcnt); +} + +static inline void chan_put(struct l2cap_chan *c) +{ + if (atomic_dec_and_test(&c->refcnt)) + kfree(c); +} + static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid) { struct l2cap_chan *c; @@ -213,7 +225,7 @@ static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout) BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); if (!mod_timer(&chan->chan_timer, jiffies + timeout)) - sock_hold(chan->sk); + chan_hold(chan); } static void l2cap_chan_clear_timer(struct l2cap_chan *chan) @@ -221,7 +233,7 @@ static void l2cap_chan_clear_timer(struct l2cap_chan *chan) BT_DBG("chan %p state %d", chan, chan->state); if (timer_pending(&chan->chan_timer) && del_timer(&chan->chan_timer)) - __sock_put(chan->sk); + chan_put(chan); } static void l2cap_state_change(struct l2cap_chan *chan, int state) @@ -244,7 +256,7 @@ static void l2cap_chan_timeout(unsigned long arg) /* sk is owned by user. Try again later */ l2cap_chan_set_timer(chan, HZ / 5); bh_unlock_sock(sk); - sock_put(sk); + chan_put(chan); return; } @@ -261,7 +273,7 @@ static void l2cap_chan_timeout(unsigned long arg) bh_unlock_sock(sk); chan->ops->close(chan->data); - sock_put(sk); + chan_put(chan); } struct l2cap_chan *l2cap_chan_create(struct sock *sk) @@ -282,6 +294,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk) chan->state = BT_OPEN; + atomic_set(&chan->refcnt, 1); + return chan; } @@ -291,13 +305,11 @@ void l2cap_chan_destroy(struct l2cap_chan *chan) list_del(&chan->global_l); write_unlock_bh(&chan_list_lock); - kfree(chan); + chan_put(chan); } static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { - struct sock *sk = chan->sk; - BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, chan->psm, chan->dcid); @@ -328,7 +340,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->omtu = L2CAP_DEFAULT_MTU; } - sock_hold(sk); + chan_hold(chan); list_add(&chan->list, &conn->chan_l); } @@ -350,7 +362,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) write_lock_bh(&conn->chan_lock); list_del(&chan->list); write_unlock_bh(&conn->chan_lock); - __sock_put(sk); + chan_put(chan); chan->conn = NULL; hci_conn_put(conn->hcon); -- cgit v1.2.3 From c9b66675373e6edb2dc291562ce1fa05f7980102 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 17 May 2011 14:59:01 -0300 Subject: Bluetooth: Make timer functions generic We now plan to use l2cap_set_timer and l2cap_clear_timer in ERTM timers. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 ++ net/bluetooth/l2cap_core.c | 58 +++++++++++++++++++++---------------------- 2 files changed, 31 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index b3d953b5f399..7aaf7f78ddb1 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -439,6 +439,8 @@ struct l2cap_pinfo { #define L2CAP_CONN_RNR_SENT 0x0200 #define L2CAP_CONN_SAR_RETRY 0x0400 +#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 __mod_retrans_timer() mod_timer(&chan->retrans_timer, \ jiffies + msecs_to_jiffies(L2CAP_DEFAULT_RETRANS_TO)); #define __mod_monitor_timer() mod_timer(&chan->monitor_timer, \ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f24022c39869..5076976960eb 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -220,19 +220,19 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) return 0; } -static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout) +static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout) { BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); - if (!mod_timer(&chan->chan_timer, jiffies + timeout)) + if (!mod_timer(timer, jiffies + timeout)) chan_hold(chan); } -static void l2cap_chan_clear_timer(struct l2cap_chan *chan) +static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer) { BT_DBG("chan %p state %d", chan, chan->state); - if (timer_pending(&chan->chan_timer) && del_timer(&chan->chan_timer)) + if (timer_pending(timer) && del_timer(timer)) chan_put(chan); } @@ -254,7 +254,7 @@ static void l2cap_chan_timeout(unsigned long arg) if (sock_owned_by_user(sk)) { /* sk is owned by user. Try again later */ - l2cap_chan_set_timer(chan, HZ / 5); + __set_chan_timer(chan, HZ / 5); bh_unlock_sock(sk); chan_put(chan); return; @@ -353,7 +353,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) struct l2cap_conn *conn = chan->conn; struct sock *parent = bt_sk(sk)->parent; - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); BT_DBG("chan %p, conn %p, err %d", chan, conn, err); @@ -412,7 +412,7 @@ static void l2cap_chan_cleanup_listen(struct sock *parent) /* Close not yet accepted channels */ while ((sk = bt_accept_dequeue(parent, NULL))) { struct l2cap_chan *chan = l2cap_pi(sk)->chan; - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); lock_sock(sk); l2cap_chan_close(chan, ECONNRESET); release_sock(sk); @@ -439,8 +439,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) case BT_CONFIG: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { - l2cap_chan_clear_timer(chan); - l2cap_chan_set_timer(chan, sk->sk_sndtimeo); + __clear_chan_timer(chan); + __set_chan_timer(chan, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, chan, reason); } else l2cap_chan_del(chan, reason); @@ -878,7 +878,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) __l2cap_chan_add(conn, chan); - l2cap_chan_set_timer(chan, sk->sk_sndtimeo); + __set_chan_timer(chan, sk->sk_sndtimeo); l2cap_state_change(chan, BT_CONNECTED); parent->sk_data_ready(parent, 0); @@ -906,13 +906,13 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) bh_lock_sock(sk); if (conn->hcon->type == LE_LINK) { - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); } if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); } else if (chan->state == BT_CONNECT) @@ -1111,11 +1111,11 @@ int l2cap_chan_connect(struct l2cap_chan *chan) l2cap_chan_add(conn, chan); l2cap_state_change(chan, BT_CONNECT); - l2cap_chan_set_timer(chan, sk->sk_sndtimeo); + __set_chan_timer(chan, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); if (l2cap_check_security(chan)) l2cap_state_change(chan, BT_CONNECTED); } else @@ -1679,7 +1679,7 @@ static void l2cap_chan_ready(struct sock *sk) BT_DBG("sk %p, parent %p", sk, parent); chan->conf_state = 0; - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); if (!parent) { /* Outgoing channel. @@ -2374,7 +2374,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd dcid = chan->scid; - l2cap_chan_set_timer(chan, sk->sk_sndtimeo); + __set_chan_timer(chan, sk->sk_sndtimeo); chan->ident = cmd->ident; @@ -2491,8 +2491,8 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { l2cap_state_change(chan, BT_DISCONN); - l2cap_chan_clear_timer(chan); - l2cap_chan_set_timer(chan, HZ / 5); + __clear_chan_timer(chan); + __set_chan_timer(chan, HZ / 5); break; } @@ -2665,7 +2665,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr default: sk->sk_err = ECONNRESET; - l2cap_chan_set_timer(chan, HZ * 5); + __set_chan_timer(chan, HZ * 5); l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } @@ -2721,8 +2721,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { l2cap_state_change(chan, BT_DISCONN); - l2cap_chan_clear_timer(chan); - l2cap_chan_set_timer(chan, HZ / 5); + __clear_chan_timer(chan); + __set_chan_timer(chan, HZ / 5); bh_unlock_sock(sk); return 0; } @@ -2755,8 +2755,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { l2cap_state_change(chan,BT_DISCONN); - l2cap_chan_clear_timer(chan); - l2cap_chan_set_timer(chan, HZ / 5); + __clear_chan_timer(chan); + __set_chan_timer(chan, HZ / 5); bh_unlock_sock(sk); return 0; } @@ -4152,13 +4152,13 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) if (encrypt == 0x00) { if (chan->sec_level == BT_SECURITY_MEDIUM) { - l2cap_chan_clear_timer(chan); - l2cap_chan_set_timer(chan, HZ * 5); + __clear_chan_timer(chan); + __set_chan_timer(chan, HZ * 5); } else if (chan->sec_level == BT_SECURITY_HIGH) l2cap_chan_close(chan, ECONNREFUSED); } else { if (chan->sec_level == BT_SECURITY_MEDIUM) - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); } } @@ -4203,8 +4203,8 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); } else { - l2cap_chan_clear_timer(chan); - l2cap_chan_set_timer(chan, HZ / 10); + __clear_chan_timer(chan); + __set_chan_timer(chan, HZ / 10); } } else if (chan->state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; @@ -4215,7 +4215,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) result = L2CAP_CR_SUCCESS; } else { l2cap_state_change(chan, BT_DISCONN); - l2cap_chan_set_timer(chan, HZ / 10); + __set_chan_timer(chan, HZ / 10); result = L2CAP_CR_SEC_BLOCK; } -- cgit v1.2.3 From 1a09bcb97ca1b4eb9a6ea381fbc3beb7a9d2895d Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 17 May 2011 15:13:19 -0300 Subject: Bluetooth: keep reference if any ERTM timer is enabled ERTM use the generic L2CAP timer functions to keep a reference to the channel. This is useful for avoiding crashes. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 15 +++++++++------ net/bluetooth/l2cap_core.c | 44 +++++++++++++++++++++---------------------- 2 files changed, 31 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7aaf7f78ddb1..c284be027d9f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -441,12 +441,15 @@ struct l2cap_pinfo { #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 __mod_retrans_timer() mod_timer(&chan->retrans_timer, \ - jiffies + msecs_to_jiffies(L2CAP_DEFAULT_RETRANS_TO)); -#define __mod_monitor_timer() mod_timer(&chan->monitor_timer, \ - jiffies + msecs_to_jiffies(L2CAP_DEFAULT_MONITOR_TO)); -#define __mod_ack_timer() mod_timer(&chan->ack_timer, \ - jiffies + msecs_to_jiffies(L2CAP_DEFAULT_ACK_TO)); +#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \ + 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); +#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); +#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer) static inline int l2cap_tx_window_full(struct l2cap_chan *ch) { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 5076976960eb..3b31a1f1f020 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -389,9 +389,9 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) if (chan->mode == L2CAP_MODE_ERTM) { struct srej_list *l, *tmp; - del_timer(&chan->retrans_timer); - del_timer(&chan->monitor_timer); - del_timer(&chan->ack_timer); + __clear_retrans_timer(chan); + __clear_monitor_timer(chan); + __clear_ack_timer(chan); skb_queue_purge(&chan->srej_q); skb_queue_purge(&chan->busy_q); @@ -697,9 +697,9 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c sk = chan->sk; if (chan->mode == L2CAP_MODE_ERTM) { - del_timer(&chan->retrans_timer); - del_timer(&chan->monitor_timer); - del_timer(&chan->ack_timer); + __clear_retrans_timer(chan); + __clear_monitor_timer(chan); + __clear_ack_timer(chan); } req.dcid = cpu_to_le16(chan->dcid); @@ -1177,7 +1177,7 @@ static void l2cap_monitor_timeout(unsigned long arg) } chan->retry_count++; - __mod_monitor_timer(); + __set_monitor_timer(chan); l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); bh_unlock_sock(sk); @@ -1192,7 +1192,7 @@ static void l2cap_retrans_timeout(unsigned long arg) bh_lock_sock(sk); chan->retry_count = 1; - __mod_monitor_timer(); + __set_monitor_timer(chan); chan->conn_state |= L2CAP_CONN_WAIT_F; @@ -1216,7 +1216,7 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan) } if (!chan->unacked_frames) - del_timer(&chan->retrans_timer); + __clear_retrans_timer(chan); } void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) @@ -1343,7 +1343,7 @@ int l2cap_ertm_send(struct l2cap_chan *chan) l2cap_do_send(chan, tx_skb); - __mod_retrans_timer(); + __set_retrans_timer(chan); bt_cb(skb)->tx_seq = chan->next_tx_seq; chan->next_tx_seq = (chan->next_tx_seq + 1) % 64; @@ -3260,8 +3260,8 @@ static int l2cap_try_push_rx_skb(struct l2cap_chan *chan) l2cap_send_sframe(chan, control); chan->retry_count = 1; - del_timer(&chan->retrans_timer); - __mod_monitor_timer(); + __clear_retrans_timer(chan); + __set_monitor_timer(chan); chan->conn_state |= L2CAP_CONN_WAIT_F; @@ -3352,7 +3352,7 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c chan->conn_state |= L2CAP_CONN_RNR_SENT; - del_timer(&chan->ack_timer); + __clear_ack_timer(chan); queue_work(_busy_wq, &chan->busy_work); @@ -3521,9 +3521,9 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont if (L2CAP_CTRL_FINAL & rx_control && chan->conn_state & L2CAP_CONN_WAIT_F) { - del_timer(&chan->monitor_timer); + __clear_monitor_timer(chan); if (chan->unacked_frames > 0) - __mod_retrans_timer(); + __set_retrans_timer(chan); chan->conn_state &= ~L2CAP_CONN_WAIT_F; } @@ -3604,7 +3604,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont l2cap_send_srejframe(chan, tx_seq); - del_timer(&chan->ack_timer); + __clear_ack_timer(chan); } return 0; @@ -3629,7 +3629,7 @@ expected: l2cap_retransmit_frames(chan); } - __mod_ack_timer(); + __set_ack_timer(chan); chan->num_acked = (chan->num_acked + 1) % num_to_ack; if (chan->num_acked == num_to_ack - 1) @@ -3655,7 +3655,7 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co if (chan->conn_state & L2CAP_CONN_SREJ_SENT) { if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && (chan->unacked_frames > 0)) - __mod_retrans_timer(); + __set_retrans_timer(chan); chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; l2cap_send_srejtail(chan); @@ -3674,7 +3674,7 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co } else { if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && (chan->unacked_frames > 0)) - __mod_retrans_timer(); + __set_retrans_timer(chan); chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; if (chan->conn_state & L2CAP_CONN_SREJ_SENT) @@ -3757,7 +3757,7 @@ static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_c chan->conn_state |= L2CAP_CONN_SEND_FBIT; if (!(chan->conn_state & L2CAP_CONN_SREJ_SENT)) { - del_timer(&chan->retrans_timer); + __clear_retrans_timer(chan); if (rx_control & L2CAP_CTRL_POLL) l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL); return; @@ -3775,9 +3775,9 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont if (L2CAP_CTRL_FINAL & rx_control && chan->conn_state & L2CAP_CONN_WAIT_F) { - del_timer(&chan->monitor_timer); + __clear_monitor_timer(chan); if (chan->unacked_frames > 0) - __mod_retrans_timer(); + __set_retrans_timer(chan); chan->conn_state &= ~L2CAP_CONN_WAIT_F; } -- cgit v1.2.3 From eb492e0169974ac6d168f11d1fc1e2753fe1f3b4 Mon Sep 17 00:00:00 2001 From: Anderson Briglia Date: Thu, 9 Jun 2011 18:50:40 -0300 Subject: Bluetooth: Implement the first SMP commands These simple commands will allow the SMP procedure to be started and terminated with a not supported error. This is the first step toward something useful. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Anderson Briglia Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/smp.h | 26 ++++++++ net/bluetooth/Makefile | 2 +- net/bluetooth/smp.c | 146 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 net/bluetooth/smp.c (limited to 'include') diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index 8f2edbf979dc..36bdd6eb6aa7 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -1,3 +1,25 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + + 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 __SMP_H #define __SMP_H @@ -73,4 +95,8 @@ struct smp_cmd_security_req { #define SMP_UNSPECIFIED 0x08 #define SMP_REPEATED_ATTEMPTS 0x09 +/* SMP Commands */ +int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level); +int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); + #endif /* __SMP_H */ diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index f04fe9a9d634..9b67f3d08fa4 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -9,5 +9,5 @@ obj-$(CONFIG_BT_CMTP) += cmtp/ obj-$(CONFIG_BT_HIDP) += hidp/ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o -bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o +bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o smp.o bluetooth-$(CONFIG_BT_SCO) += sco.o diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c new file mode 100644 index 000000000000..aa0434f4aa1a --- /dev/null +++ b/net/bluetooth/smp.c @@ -0,0 +1,146 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + + 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. +*/ + +#include +#include +#include +#include + +static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code, + u16 dlen, void *data) +{ + struct sk_buff *skb; + struct l2cap_hdr *lh; + int len; + + len = L2CAP_HDR_SIZE + sizeof(code) + dlen; + + if (len > conn->mtu) + return NULL; + + skb = bt_skb_alloc(len, GFP_ATOMIC); + if (!skb) + return NULL; + + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->len = cpu_to_le16(sizeof(code) + dlen); + lh->cid = cpu_to_le16(L2CAP_CID_SMP); + + memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code)); + + memcpy(skb_put(skb, dlen), data, dlen); + + return skb; +} + +static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) +{ + struct sk_buff *skb = smp_build_cmd(conn, code, len, data); + + BT_DBG("code 0x%2.2x", code); + + if (!skb) + return; + + hci_send_acl(conn->hcon, skb, 0); +} + +int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) +{ + __u8 authreq; + + BT_DBG("conn %p hcon %p level 0x%2.2x", conn, conn->hcon, sec_level); + + switch (sec_level) { + case BT_SECURITY_MEDIUM: + /* Encrypted, no MITM protection */ + authreq = HCI_AT_NO_BONDING_MITM; + break; + + case BT_SECURITY_HIGH: + /* Bonding, MITM protection */ + authreq = HCI_AT_GENERAL_BONDING_MITM; + break; + + case BT_SECURITY_LOW: + default: + return 1; + } + + if (conn->hcon->link_mode & HCI_LM_MASTER) { + struct smp_cmd_pairing cp; + cp.io_capability = 0x00; + cp.oob_flag = 0x00; + cp.max_key_size = 16; + cp.init_key_dist = 0x00; + cp.resp_key_dist = 0x00; + cp.auth_req = authreq; + smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); + } else { + struct smp_cmd_security_req cp; + cp.auth_req = authreq; + smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp); + } + + return 0; +} + +int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) +{ + __u8 code = skb->data[0]; + __u8 reason; + int err = 0; + + skb_pull(skb, sizeof(code)); + + switch (code) { + case SMP_CMD_PAIRING_REQ: + reason = SMP_PAIRING_NOTSUPP; + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), + &reason); + err = -EOPNOTSUPP; + break; + + case SMP_CMD_PAIRING_FAIL: + break; + + case SMP_CMD_PAIRING_RSP: + case SMP_CMD_PAIRING_CONFIRM: + case SMP_CMD_PAIRING_RANDOM: + case SMP_CMD_ENCRYPT_INFO: + case SMP_CMD_MASTER_IDENT: + case SMP_CMD_IDENT_INFO: + case SMP_CMD_IDENT_ADDR_INFO: + case SMP_CMD_SIGN_INFO: + case SMP_CMD_SECURITY_REQ: + default: + BT_DBG("Unknown command code 0x%2.2x", code); + + reason = SMP_CMD_NOTSUPP; + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), + &reason); + err = -EOPNOTSUPP; + } + + kfree_skb(skb); + return err; +} -- cgit v1.2.3 From fd16d263194aa6b50b215eb593a567b59d744d6e Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 13 Jun 2011 10:42:49 +0200 Subject: block: Add __attribute__((format(printf...) and fix fallout Use the compiler to verify format strings and arguments. Fix fallout. Signed-off-by: Joe Perches Signed-off-by: Jens Axboe --- block/blk-throttle.c | 4 ++-- block/cfq-iosched.c | 11 ++++++----- include/linux/blktrace_api.h | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/block/blk-throttle.c b/block/blk-throttle.c index a62be8d0dc1b..3689f833afdc 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -927,7 +927,7 @@ static int throtl_dispatch(struct request_queue *q) bio_list_init(&bio_list_on_stack); - throtl_log(td, "dispatch nr_queued=%lu read=%u write=%u", + throtl_log(td, "dispatch nr_queued=%d read=%u write=%u", total_nr_queued(td), td->nr_queued[READ], td->nr_queued[WRITE]); @@ -1204,7 +1204,7 @@ int blk_throtl_bio(struct request_queue *q, struct bio **biop) } queue_bio: - throtl_log_tg(td, tg, "[%c] bio. bdisp=%u sz=%u bps=%llu" + throtl_log_tg(td, tg, "[%c] bio. bdisp=%llu sz=%u bps=%llu" " iodisp=%u iops=%u queued=%d/%d", rw == READ ? 'R' : 'W', tg->bytes_disp[rw], bio->bi_size, tg->bps[rw], diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 2b2d7a9a42ab..3d403a128971 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -988,9 +988,10 @@ static void cfq_group_served(struct cfq_data *cfqd, struct cfq_group *cfqg, cfq_log_cfqg(cfqd, cfqg, "served: vt=%llu min_vt=%llu", cfqg->vdisktime, st->min_vdisktime); - cfq_log_cfqq(cfqq->cfqd, cfqq, "sl_used=%u disp=%u charge=%u iops=%u" - " sect=%u", used_sl, cfqq->slice_dispatch, charge, - iops_mode(cfqd), cfqq->nr_sectors); + cfq_log_cfqq(cfqq->cfqd, cfqq, + "sl_used=%u disp=%u charge=%u iops=%u sect=%lu", + used_sl, cfqq->slice_dispatch, charge, + iops_mode(cfqd), cfqq->nr_sectors); cfq_blkiocg_update_timeslice_used(&cfqg->blkg, used_sl, unaccounted_sl); cfq_blkiocg_set_start_empty_time(&cfqg->blkg); @@ -2018,8 +2019,8 @@ static void cfq_arm_slice_timer(struct cfq_data *cfqd) */ if (sample_valid(cic->ttime_samples) && (cfqq->slice_end - jiffies < cic->ttime_mean)) { - cfq_log_cfqq(cfqd, cfqq, "Not idling. think_time:%d", - cic->ttime_mean); + cfq_log_cfqq(cfqd, cfqq, "Not idling. think_time:%lu", + cic->ttime_mean); return; } diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index b22fb0d3db0f..8c7c2de7631a 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -169,7 +169,8 @@ extern void blk_trace_shutdown(struct request_queue *); extern int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev, struct block_device *bdev, struct blk_user_trace_setup *buts); -extern void __trace_note_message(struct blk_trace *, const char *fmt, ...); +extern __attribute__((format(printf, 2, 3))) +void __trace_note_message(struct blk_trace *, const char *fmt, ...); /** * blk_add_trace_msg - Add a (simple) message to the blktrace stream -- cgit v1.2.3 From 88ba43b662b6b944c6278ad81a114fa559807776 Mon Sep 17 00:00:00 2001 From: Anderson Briglia Date: Thu, 9 Jun 2011 18:50:42 -0300 Subject: Bluetooth: Add simple SMP pairing negotiation This implementation only exchanges SMP messages between the Host and the Remote. No keys are being generated. TK and STK generation will be provided in further patches. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/smp.h | 17 +++++++ net/bluetooth/smp.c | 107 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 119 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index 36bdd6eb6aa7..111853ab239a 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -38,6 +38,23 @@ struct smp_cmd_pairing { __u8 resp_key_dist; } __packed; +#define SMP_IO_DISPLAY_ONLY 0x00 +#define SMP_IO_DISPLAY_YESNO 0x01 +#define SMP_IO_KEYBOARD_ONLY 0x02 +#define SMP_IO_NO_INPUT_OUTPUT 0x03 +#define SMP_IO_KEYBOARD_DISPLAY 0x04 + +#define SMP_OOB_NOT_PRESENT 0x00 +#define SMP_OOB_PRESENT 0x01 + +#define SMP_DIST_ENC_KEY 0x01 +#define SMP_DIST_ID_KEY 0x02 +#define SMP_DIST_SIGN 0x04 + +#define SMP_AUTH_NONE 0x00 +#define SMP_AUTH_BONDING 0x01 +#define SMP_AUTH_MITM 0x04 + #define SMP_CMD_PAIRING_CONFIRM 0x03 struct smp_cmd_pairing_confirm { __u8 confirm_val[16]; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index aa0434f4aa1a..2240e96552f1 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -64,6 +64,94 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) hci_send_acl(conn->hcon, skb, 0); } +static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct smp_cmd_pairing *rp = (void *) skb->data; + + BT_DBG("conn %p", conn); + + skb_pull(skb, sizeof(*rp)); + + rp->io_capability = 0x00; + rp->oob_flag = 0x00; + rp->max_key_size = 16; + rp->init_key_dist = 0x00; + rp->resp_key_dist = 0x00; + rp->auth_req &= (SMP_AUTH_BONDING | SMP_AUTH_MITM); + + smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp); +} + +static void smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct smp_cmd_pairing_confirm cp; + + BT_DBG("conn %p", conn); + + memset(&cp, 0, sizeof(cp)); + + smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); +} + +static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, + struct sk_buff *skb) +{ + BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); + + if (conn->hcon->out) { + struct smp_cmd_pairing_random random; + + memset(&random, 0, sizeof(random)); + + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random), + &random); + } else { + struct smp_cmd_pairing_confirm confirm; + + memset(&confirm, 0, sizeof(confirm)); + + smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(confirm), + &confirm); + } +} + +static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct smp_cmd_pairing_random cp; + + BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); + + skb_pull(skb, sizeof(cp)); + + if (conn->hcon->out) { + /* FIXME: start encryption */ + } else { + memset(&cp, 0, sizeof(cp)); + + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(cp), &cp); + } +} + +static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct smp_cmd_security_req *rp = (void *) skb->data; + struct smp_cmd_pairing cp; + + BT_DBG("conn %p", conn); + + skb_pull(skb, sizeof(*rp)); + memset(&cp, 0, sizeof(cp)); + + cp.io_capability = 0x00; + cp.oob_flag = 0x00; + cp.max_key_size = 16; + cp.init_key_dist = 0x00; + cp.resp_key_dist = 0x00; + cp.auth_req = rp->auth_req & (SMP_AUTH_BONDING | SMP_AUTH_MITM); + + smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); +} + int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) { __u8 authreq; @@ -114,24 +202,33 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) switch (code) { case SMP_CMD_PAIRING_REQ: - reason = SMP_PAIRING_NOTSUPP; - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), - &reason); - err = -EOPNOTSUPP; + smp_cmd_pairing_req(conn, skb); break; case SMP_CMD_PAIRING_FAIL: break; case SMP_CMD_PAIRING_RSP: + smp_cmd_pairing_rsp(conn, skb); + break; + + case SMP_CMD_SECURITY_REQ: + smp_cmd_security_req(conn, skb); + break; + case SMP_CMD_PAIRING_CONFIRM: + smp_cmd_pairing_confirm(conn, skb); + break; + case SMP_CMD_PAIRING_RANDOM: + smp_cmd_pairing_random(conn, skb); + break; + case SMP_CMD_ENCRYPT_INFO: case SMP_CMD_MASTER_IDENT: case SMP_CMD_IDENT_INFO: case SMP_CMD_IDENT_ADDR_INFO: case SMP_CMD_SIGN_INFO: - case SMP_CMD_SECURITY_REQ: default: BT_DBG("Unknown command code 0x%2.2x", code); -- cgit v1.2.3 From 3a0259bb80cec7595a2d085a150412d23ba28c81 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 9 Jun 2011 18:50:43 -0300 Subject: Bluetooth: Add support for using the crypto subsystem This will allow using the crypto subsystem for encrypting data. As SMP (Security Manager Protocol) is implemented almost entirely on the host side and the crypto module already implements the needed methods (AES-128), it makes sense to use it. There's now a new module option to enable/disable SMP support. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Anderson Briglia Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/Kconfig | 8 ++++++++ net/bluetooth/hci_core.c | 22 ++++++++++++++++++++++ net/bluetooth/smp.c | 22 ++++++++++++++++++---- 4 files changed, 50 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 836d3e8c4bf1..7837f3260863 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -177,6 +177,8 @@ struct hci_dev { __u16 init_last_cmd; + struct crypto_blkcipher *tfm; + struct inquiry_cache inq_cache; struct hci_conn_hash conn_hash; struct list_head blacklist; diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 6ae5ec508587..f495dea741e3 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -22,6 +22,7 @@ menuconfig BT BNEP Module (Bluetooth Network Encapsulation Protocol) CMTP Module (CAPI Message Transport Protocol) HIDP Module (Human Interface Device Protocol) + SMP Module (Security Manager Protocol) Say Y here to compile Bluetooth support into the kernel or say M to compile it as module (bluetooth). @@ -36,11 +37,18 @@ if BT != n config BT_L2CAP bool "L2CAP protocol support" select CRC16 + select CRYPTO + select CRYPTO_BLKCIPHER + select CRYPTO_AES + select CRYPTO_ECB help L2CAP (Logical Link Control and Adaptation Protocol) provides connection oriented and connection-less data transport. L2CAP support is required for most Bluetooth applications. + Also included is support for SMP (Security Manager Protocol) which + is the security layer on top of LE (Low Energy) links. + config BT_SCO bool "SCO links support" help diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e14e8a1cb04e..f62ca1935f5a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,8 @@ static void hci_tx_task(unsigned long arg); static DEFINE_RWLOCK(hci_task_lock); +static int enable_smp; + /* HCI device list */ LIST_HEAD(hci_dev_list); DEFINE_RWLOCK(hci_dev_list_lock); @@ -1274,6 +1277,14 @@ int hci_add_adv_entry(struct hci_dev *hdev, return 0; } +static struct crypto_blkcipher *alloc_cypher(void) +{ + if (enable_smp) + return crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); + + return ERR_PTR(-ENOTSUPP); +} + /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -1358,6 +1369,11 @@ int hci_register_dev(struct hci_dev *hdev) if (!hdev->workqueue) goto nomem; + hdev->tfm = alloc_cypher(); + if (IS_ERR(hdev->tfm)) + BT_INFO("Failed to load transform for ecb(aes): %ld", + PTR_ERR(hdev->tfm)); + hci_register_sysfs(hdev); hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, @@ -1406,6 +1422,9 @@ int hci_unregister_dev(struct hci_dev *hdev) !test_bit(HCI_SETUP, &hdev->flags)) mgmt_index_removed(hdev->id); + if (!IS_ERR(hdev->tfm)) + crypto_free_blkcipher(hdev->tfm); + hci_notify(hdev, HCI_DEV_UNREG); if (hdev->rfkill) { @@ -2242,3 +2261,6 @@ static void hci_cmd_task(unsigned long arg) } } } + +module_param(enable_smp, bool, 0644); +MODULE_PARM_DESC(enable_smp, "Enable SMP support (LE only)"); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 2240e96552f1..aa20bee97501 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -154,9 +154,13 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) { + struct hci_conn *hcon = conn->hcon; __u8 authreq; - BT_DBG("conn %p hcon %p level 0x%2.2x", conn, conn->hcon, sec_level); + BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level); + + if (IS_ERR(hcon->hdev->tfm)) + return 1; switch (sec_level) { case BT_SECURITY_MEDIUM: @@ -174,7 +178,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) return 1; } - if (conn->hcon->link_mode & HCI_LM_MASTER) { + if (hcon->link_mode & HCI_LM_MASTER) { struct smp_cmd_pairing cp; cp.io_capability = 0x00; cp.oob_flag = 0x00; @@ -198,6 +202,12 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) __u8 reason; int err = 0; + if (IS_ERR(conn->hcon->hdev->tfm)) { + err = PTR_ERR(conn->hcon->hdev->tfm); + reason = SMP_PAIRING_NOTSUPP; + goto done; + } + skb_pull(skb, sizeof(code)); switch (code) { @@ -233,11 +243,15 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("Unknown command code 0x%2.2x", code); reason = SMP_CMD_NOTSUPP; - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), - &reason); err = -EOPNOTSUPP; + goto done; } +done: + if (reason) + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), + &reason); + kfree_skb(skb); return err; } -- cgit v1.2.3 From f01ead315785768cdb6e928646f90a47640bcdd9 Mon Sep 17 00:00:00 2001 From: Anderson Briglia Date: Thu, 9 Jun 2011 18:50:45 -0300 Subject: Bluetooth: Add SMP confirmation structs This patch adds initial support for verifying the confirmation value that the remote side has sent. Signed-off-by: Anderson Briglia Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 5 +++++ net/bluetooth/smp.c | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index c284be027d9f..b03d9c4dfc78 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -395,6 +395,11 @@ struct l2cap_conn { __u8 disc_reason; + __u8 preq[7]; /* SMP Pairing Request */ + __u8 prsp[7]; /* SMP Pairing Response */ + __u8 prnd[16]; /* SMP Pairing Random */ + __u8 pcnf[16]; /* SMP Pairing Confirm */ + struct list_head chan_l; rwlock_t chan_lock; }; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 57fc7d0cadf3..fa22f4aa3b04 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -187,6 +187,8 @@ static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("conn %p", conn); + conn->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&conn->preq[1], rp, sizeof(*rp)); skb_pull(skb, sizeof(*rp)); rp->io_capability = 0x00; @@ -196,17 +198,25 @@ static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) rp->resp_key_dist = 0x00; rp->auth_req &= (SMP_AUTH_BONDING | SMP_AUTH_MITM); + conn->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&conn->prsp[1], rp, sizeof(*rp)); + smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp); } static void smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { + struct smp_cmd_pairing *rp = (void *) skb->data; struct smp_cmd_pairing_confirm cp; BT_DBG("conn %p", conn); memset(&cp, 0, sizeof(cp)); + conn->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&conn->prsp[1], rp, sizeof(*rp)); + skb_pull(skb, sizeof(*rp)); + smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); } @@ -266,6 +276,9 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) cp.resp_key_dist = 0x00; cp.auth_req = rp->auth_req & (SMP_AUTH_BONDING | SMP_AUTH_MITM); + conn->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&conn->preq[1], &cp, sizeof(cp)); + smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); } @@ -303,6 +316,10 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) cp.init_key_dist = 0x00; cp.resp_key_dist = 0x00; cp.auth_req = authreq; + + conn->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&conn->preq[1], &cp, sizeof(cp)); + smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); } else { struct smp_cmd_security_req cp; -- cgit v1.2.3 From 7d24ddcc1140d2f796436e476c8d69469610588b Mon Sep 17 00:00:00 2001 From: Anderson Briglia Date: Thu, 9 Jun 2011 18:50:46 -0300 Subject: Bluetooth: Add SMP confirmation checks methods This patch includes support for generating and sending the random value used to produce the confirmation value. Signed-off-by: Anderson Briglia Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/smp.c | 97 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index b03d9c4dfc78..01c993b6b263 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -399,6 +399,7 @@ struct l2cap_conn { __u8 prsp[7]; /* SMP Pairing Response */ __u8 prnd[16]; /* SMP Pairing Random */ __u8 pcnf[16]; /* SMP Pairing Confirm */ + __u8 tk[16]; /* SMP Temporary Key */ struct list_head chan_l; rwlock_t chan_lock; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index fa22f4aa3b04..7a9a195c27d3 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -198,6 +198,9 @@ static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) rp->resp_key_dist = 0x00; rp->auth_req &= (SMP_AUTH_BONDING | SMP_AUTH_MITM); + /* Just works */ + memset(conn->tk, 0, sizeof(conn->tk)); + conn->prsp[0] = SMP_CMD_PAIRING_RSP; memcpy(&conn->prsp[1], rp, sizeof(*rp)); @@ -208,54 +211,120 @@ static void smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *rp = (void *) skb->data; struct smp_cmd_pairing_confirm cp; + struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + int ret; + u8 res[16]; BT_DBG("conn %p", conn); - memset(&cp, 0, sizeof(cp)); + /* Just works */ + memset(conn->tk, 0, sizeof(conn->tk)); conn->prsp[0] = SMP_CMD_PAIRING_RSP; memcpy(&conn->prsp[1], rp, sizeof(*rp)); skb_pull(skb, sizeof(*rp)); + ret = smp_rand(conn->prnd); + if (ret) + return; + + ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0, + conn->src, conn->hcon->dst_type, conn->dst, res); + if (ret) + return; + + swap128(res, cp.confirm_val); + smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); } static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) { + struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - if (conn->hcon->out) { - struct smp_cmd_pairing_random random; + memcpy(conn->pcnf, skb->data, sizeof(conn->pcnf)); + skb_pull(skb, sizeof(conn->pcnf)); - memset(&random, 0, sizeof(random)); + if (conn->hcon->out) { + u8 random[16]; + swap128(conn->prnd, random); smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random), - &random); + random); } else { - struct smp_cmd_pairing_confirm confirm; + struct smp_cmd_pairing_confirm cp; + int ret; + u8 res[16]; - memset(&confirm, 0, sizeof(confirm)); + ret = smp_rand(conn->prnd); + if (ret) + return; - smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(confirm), - &confirm); + ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, + conn->hcon->dst_type, conn->dst, + 0, conn->src, res); + if (ret) + return; + + swap128(res, cp.confirm_val); + + smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); } } static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { - struct smp_cmd_pairing_random cp; + struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + int ret; + u8 key[16], res[16], random[16], confirm[16], buf[128]; + + swap128(skb->data, random); + skb_pull(skb, sizeof(random)); + + if (conn->hcon->out) + ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0, + conn->src, conn->hcon->dst_type, conn->dst, + res); + else + ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, + conn->hcon->dst_type, conn->dst, 0, conn->src, + res); + if (ret) + return; BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - skb_pull(skb, sizeof(cp)); + swap128(res, confirm); + + if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) { + struct smp_cmd_pairing_fail cp; + + BT_ERR("Pairing failed (confirmation values mismatch)"); + cp.reason = SMP_CONFIRM_FAILED; + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(cp), &cp); + return; + } if (conn->hcon->out) { - /* FIXME: start encryption */ + smp_s1(tfm, conn->tk, random, conn->prnd, key); + + hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, + sizeof(buf), 0); + BT_DBG("key %s", buf); } else { - memset(&cp, 0, sizeof(cp)); + u8 r[16]; + + swap128(conn->prnd, r); + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); + + smp_s1(tfm, conn->tk, conn->prnd, random, key); - smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(cp), &cp); + hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, + sizeof(buf), 0); + BT_DBG("key %s", buf); } } -- cgit v1.2.3 From a7a595f675f1b33dc73167147321dba5c4395acc Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 9 Jun 2011 18:50:47 -0300 Subject: Bluetooth: Add support for LE Start Encryption This adds support for starting SMP Phase 2 Encryption, when the initial SMP negotiation is successful. This adds the LE Start Encryption and LE Long Term Key Request commands and related events. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 34 ++++++++++++++++++++ include/net/bluetooth/hci_core.h | 6 ++++ net/bluetooth/hci_conn.c | 49 +++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 67 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/smp.c | 16 +++++++--- 5 files changed, 168 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index bd285c6a5509..65345cd215be 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -745,6 +745,33 @@ struct hci_cp_le_conn_update { __le16 max_ce_len; } __packed; +#define HCI_OP_LE_START_ENC 0x2019 +struct hci_cp_le_start_enc { + __le16 handle; + __u8 rand[8]; + __le16 ediv; + __u8 ltk[16]; +} __packed; + +#define HCI_OP_LE_LTK_REPLY 0x201a +struct hci_cp_le_ltk_reply { + __le16 handle; + __u8 ltk[16]; +} __packed; +struct hci_rp_le_ltk_reply { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_LTK_NEG_REPLY 0x201b +struct hci_cp_le_ltk_neg_reply { + __le16 handle; +} __packed; +struct hci_rp_le_ltk_neg_reply { + __u8 status; + __le16 handle; +} __packed; + /* ---- HCI Events ---- */ #define HCI_EV_INQUIRY_COMPLETE 0x01 @@ -1035,6 +1062,13 @@ struct hci_ev_le_conn_complete { __u8 clk_accurancy; } __packed; +#define HCI_EV_LE_LTK_REQ 0x05 +struct hci_ev_le_ltk_req { + __le16 handle; + __u8 random[8]; + __le16 ediv; +} __packed; + /* Advertising report event types */ #define ADV_IND 0x00 #define ADV_DIRECT_IND 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7837f3260863..ab6842c6e49a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -249,6 +249,7 @@ struct hci_conn { __u8 power_save; __u16 disc_timeout; unsigned long pend; + __u8 ltk[16]; __u8 remote_cap; __u8 remote_oob; @@ -861,4 +862,9 @@ void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result); void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); +void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], + __u8 ltk[16]); +void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]); +void hci_le_ltk_neg_reply(struct hci_conn *conn); + #endif /* __HCI_CORE_H */ diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 37f5a174f072..18193831bbf1 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -204,6 +204,55 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, } EXPORT_SYMBOL(hci_le_conn_update); +void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], + __u8 ltk[16]) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_le_start_enc cp; + + BT_DBG("%p", conn); + + memset(&cp, 0, sizeof(cp)); + + cp.handle = cpu_to_le16(conn->handle); + memcpy(cp.ltk, ltk, sizeof(cp.ltk)); + cp.ediv = ediv; + memcpy(cp.rand, rand, sizeof(rand)); + + hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp); +} +EXPORT_SYMBOL(hci_le_start_enc); + +void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_le_ltk_reply cp; + + BT_DBG("%p", conn); + + memset(&cp, 0, sizeof(cp)); + + cp.handle = cpu_to_le16(conn->handle); + memcpy(cp.ltk, ltk, sizeof(ltk)); + + hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); +} +EXPORT_SYMBOL(hci_le_ltk_reply); + +void hci_le_ltk_neg_reply(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_le_ltk_neg_reply cp; + + BT_DBG("%p", conn); + + memset(&cp, 0, sizeof(cp)); + + cp.handle = cpu_to_le16(conn->handle); + + hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(cp), &cp); +} + /* Device _must_ be locked */ void hci_sco_setup(struct hci_conn *conn, __u8 status) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0f643f84131f..166fa113721c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -868,6 +868,30 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, hci_dev_unlock(hdev); } +static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_le_ltk_reply *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status); +} + +static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status); +} + static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%x", hdev->name, status); @@ -1248,6 +1272,11 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) hci_dev_unlock(hdev); } +static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) +{ + BT_DBG("%s status 0x%x", hdev->name, status); +} + static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -1856,6 +1885,14 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_le_set_scan_enable(hdev, skb); break; + case HCI_OP_LE_LTK_REPLY: + hci_cc_le_ltk_reply(hdev, skb); + break; + + case HCI_OP_LE_LTK_NEG_REPLY: + hci_cc_le_ltk_neg_reply(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; @@ -1934,6 +1971,10 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_le_create_conn(hdev, ev->status); break; + case HCI_OP_LE_START_ENC: + hci_cs_le_start_enc(hdev, ev->status); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; @@ -2745,6 +2786,28 @@ static inline void hci_le_adv_report_evt(struct hci_dev *hdev, hci_dev_unlock(hdev); } +static inline void hci_le_ltk_request_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_le_ltk_req *ev = (void *) skb->data; + struct hci_cp_le_ltk_reply cp; + struct hci_conn *conn; + + BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle)); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + + memset(&cp, 0, sizeof(cp)); + cp.handle = cpu_to_le16(conn->handle); + memcpy(cp.ltk, conn->ltk, sizeof(conn->ltk)); + + hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); + + hci_dev_unlock(hdev); +} + static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_meta *le_ev = (void *) skb->data; @@ -2760,6 +2823,10 @@ static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_le_adv_report_evt(hdev, skb); break; + case HCI_EV_LE_LTK_REQ: + hci_le_ltk_request_evt(hdev, skb); + break; + default: break; } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 7a9a195c27d3..56828db68109 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -277,13 +277,16 @@ static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { - struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + struct hci_conn *hcon = conn->hcon; + struct crypto_blkcipher *tfm = hcon->hdev->tfm; int ret; u8 key[16], res[16], random[16], confirm[16], buf[128]; swap128(skb->data, random); skb_pull(skb, sizeof(random)); + memset(hcon->ltk, 0, sizeof(hcon->ltk)); + if (conn->hcon->out) ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0, conn->src, conn->hcon->dst_type, conn->dst, @@ -309,11 +312,15 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) } if (conn->hcon->out) { + __le16 ediv; + u8 rand[8]; + smp_s1(tfm, conn->tk, random, conn->prnd, key); + swap128(key, hcon->ltk); - hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, - sizeof(buf), 0); - BT_DBG("key %s", buf); + memset(rand, 0, sizeof(rand)); + ediv = 0; + hci_le_start_enc(hcon, ediv, rand, hcon->ltk); } else { u8 r[16]; @@ -321,6 +328,7 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); smp_s1(tfm, conn->tk, conn->prnd, random, key); + swap128(key, hcon->ltk); hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, sizeof(buf), 0); -- cgit v1.2.3 From 88167aed0d2e84364c8f0d9e48429c21fc7894c3 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 9 Jun 2011 18:50:51 -0300 Subject: Bluetooth: Update the security level when link is encrypted If the pending security level is greater than the current security level and the link is now encrypted, we should update the link security level. This is only useful for LE links, when the only event generated when SMP is sucessful in the Encrypt Change event. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ab6842c6e49a..fe05946c8f0b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -745,6 +745,9 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, if (conn->sec_level == BT_SECURITY_SDP) conn->sec_level = BT_SECURITY_LOW; + if (conn->pending_sec_level > conn->sec_level) + conn->sec_level = conn->pending_sec_level; + hci_proto_encrypt_cfm(conn, status, encrypt); read_lock_bh(&hci_cb_list_lock); -- cgit v1.2.3 From de1b794130b130e77ffa975bb58cb843744f9ae5 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 13 Jun 2011 15:38:22 -0400 Subject: jbd2: Fix oops in jbd2_journal_remove_journal_head() jbd2_journal_remove_journal_head() can oops when trying to access journal_head returned by bh2jh(). This is caused for example by the following race: TASK1 TASK2 jbd2_journal_commit_transaction() ... processing t_forget list __jbd2_journal_refile_buffer(jh); if (!jh->b_transaction) { jbd_unlock_bh_state(bh); jbd2_journal_try_to_free_buffers() jbd2_journal_grab_journal_head(bh) jbd_lock_bh_state(bh) __journal_try_to_free_buffer() jbd2_journal_put_journal_head(jh) jbd2_journal_remove_journal_head(bh); jbd2_journal_put_journal_head() in TASK2 sees that b_jcount == 0 and buffer is not part of any transaction and thus frees journal_head before TASK1 gets to doing so. Note that even buffer_head can be released by try_to_free_buffers() after jbd2_journal_put_journal_head() which adds even larger opportunity for oops (but I didn't see this happen in reality). Fix the problem by making transactions hold their own journal_head reference (in b_jcount). That way we don't have to remove journal_head explicitely via jbd2_journal_remove_journal_head() and instead just remove journal_head when b_jcount drops to zero. The result of this is that [__]jbd2_journal_refile_buffer(), [__]jbd2_journal_unfile_buffer(), and __jdb2_journal_remove_checkpoint() can free journal_head which needs modification of a few callers. Also we have to be careful because once journal_head is removed, buffer_head might be freed as well. So we have to get our own buffer_head reference where it matters. Signed-off-by: Jan Kara Signed-off-by: "Theodore Ts'o" --- fs/jbd2/checkpoint.c | 28 +++++++++------- fs/jbd2/commit.c | 33 +++++++++++-------- fs/jbd2/journal.c | 91 ++++++++++++++++----------------------------------- fs/jbd2/transaction.c | 67 +++++++++++++++++++------------------ include/linux/jbd2.h | 2 -- 5 files changed, 99 insertions(+), 122 deletions(-) (limited to 'include') diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 6a79fd0a1a32..2c62c5aae82f 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -97,10 +97,14 @@ static int __try_to_free_cp_buf(struct journal_head *jh) if (jh->b_jlist == BJ_None && !buffer_locked(bh) && !buffer_dirty(bh) && !buffer_write_io_error(bh)) { + /* + * Get our reference so that bh cannot be freed before + * we unlock it + */ + get_bh(bh); JBUFFER_TRACE(jh, "remove from checkpoint list"); ret = __jbd2_journal_remove_checkpoint(jh) + 1; jbd_unlock_bh_state(bh); - jbd2_journal_remove_journal_head(bh); BUFFER_TRACE(bh, "release"); __brelse(bh); } else { @@ -223,8 +227,8 @@ restart: spin_lock(&journal->j_list_lock); goto restart; } + get_bh(bh); if (buffer_locked(bh)) { - atomic_inc(&bh->b_count); spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); wait_on_buffer(bh); @@ -243,7 +247,6 @@ restart: */ released = __jbd2_journal_remove_checkpoint(jh); jbd_unlock_bh_state(bh); - jbd2_journal_remove_journal_head(bh); __brelse(bh); } @@ -284,7 +287,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, int ret = 0; if (buffer_locked(bh)) { - atomic_inc(&bh->b_count); + get_bh(bh); spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); wait_on_buffer(bh); @@ -316,12 +319,12 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, ret = 1; if (unlikely(buffer_write_io_error(bh))) ret = -EIO; + get_bh(bh); J_ASSERT_JH(jh, !buffer_jbddirty(bh)); BUFFER_TRACE(bh, "remove from checkpoint"); __jbd2_journal_remove_checkpoint(jh); spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); - jbd2_journal_remove_journal_head(bh); __brelse(bh); } else { /* @@ -554,7 +557,8 @@ int jbd2_cleanup_journal_tail(journal_t *journal) /* * journal_clean_one_cp_list * - * Find all the written-back checkpoint buffers in the given list and release them. + * Find all the written-back checkpoint buffers in the given list and + * release them. * * Called with the journal locked. * Called with j_list_lock held. @@ -663,8 +667,8 @@ out: * checkpoint lists. * * The function returns 1 if it frees the transaction, 0 otherwise. + * The function can free jh and bh. * - * This function is called with the journal locked. * This function is called with j_list_lock held. * This function is called with jbd_lock_bh_state(jh2bh(jh)) */ @@ -684,13 +688,14 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh) } journal = transaction->t_journal; + JBUFFER_TRACE(jh, "removing from transaction"); __buffer_unlink(jh); jh->b_cp_transaction = NULL; + jbd2_journal_put_journal_head(jh); if (transaction->t_checkpoint_list != NULL || transaction->t_checkpoint_io_list != NULL) goto out; - JBUFFER_TRACE(jh, "transaction has no more buffers"); /* * There is one special case to worry about: if we have just pulled the @@ -701,10 +706,8 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh) * The locking here around t_state is a bit sleazy. * See the comment at the end of jbd2_journal_commit_transaction(). */ - if (transaction->t_state != T_FINISHED) { - JBUFFER_TRACE(jh, "belongs to running/committing transaction"); + if (transaction->t_state != T_FINISHED) goto out; - } /* OK, that was the last buffer for the transaction: we can now safely remove this transaction from the log */ @@ -723,7 +726,6 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh) wake_up(&journal->j_wait_logspace); ret = 1; out: - JBUFFER_TRACE(jh, "exit"); return ret; } @@ -742,6 +744,8 @@ void __jbd2_journal_insert_checkpoint(struct journal_head *jh, J_ASSERT_JH(jh, buffer_dirty(jh2bh(jh)) || buffer_jbddirty(jh2bh(jh))); J_ASSERT_JH(jh, jh->b_cp_transaction == NULL); + /* Get reference for checkpointing transaction */ + jbd2_journal_grab_journal_head(jh2bh(jh)); jh->b_cp_transaction = transaction; if (!transaction->t_checkpoint_list) { diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 7f21cf3aaf92..eef6979821a4 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -848,10 +848,16 @@ restart_loop: while (commit_transaction->t_forget) { transaction_t *cp_transaction; struct buffer_head *bh; + int try_to_free = 0; jh = commit_transaction->t_forget; spin_unlock(&journal->j_list_lock); bh = jh2bh(jh); + /* + * Get a reference so that bh cannot be freed before we are + * done with it. + */ + get_bh(bh); jbd_lock_bh_state(bh); J_ASSERT_JH(jh, jh->b_transaction == commit_transaction); @@ -914,28 +920,27 @@ restart_loop: __jbd2_journal_insert_checkpoint(jh, commit_transaction); if (is_journal_aborted(journal)) clear_buffer_jbddirty(bh); - JBUFFER_TRACE(jh, "refile for checkpoint writeback"); - __jbd2_journal_refile_buffer(jh); - jbd_unlock_bh_state(bh); } else { J_ASSERT_BH(bh, !buffer_dirty(bh)); - /* The buffer on BJ_Forget list and not jbddirty means + /* + * The buffer on BJ_Forget list and not jbddirty means * it has been freed by this transaction and hence it * could not have been reallocated until this * transaction has committed. *BUT* it could be * reallocated once we have written all the data to * disk and before we process the buffer on BJ_Forget - * list. */ - JBUFFER_TRACE(jh, "refile or unfile freed buffer"); - __jbd2_journal_refile_buffer(jh); - if (!jh->b_transaction) { - jbd_unlock_bh_state(bh); - /* needs a brelse */ - jbd2_journal_remove_journal_head(bh); - release_buffer_page(bh); - } else - jbd_unlock_bh_state(bh); + * list. + */ + if (!jh->b_next_transaction) + try_to_free = 1; } + JBUFFER_TRACE(jh, "refile or unfile buffer"); + __jbd2_journal_refile_buffer(jh); + jbd_unlock_bh_state(bh); + if (try_to_free) + release_buffer_page(bh); /* Drops bh reference */ + else + __brelse(bh); cond_resched_lock(&journal->j_list_lock); } spin_unlock(&journal->j_list_lock); diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 9a7826990304..0dfa5b598e68 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -2078,10 +2078,9 @@ static void journal_free_journal_head(struct journal_head *jh) * When a buffer has its BH_JBD bit set it is immune from being released by * core kernel code, mainly via ->b_count. * - * A journal_head may be detached from its buffer_head when the journal_head's - * b_transaction, b_cp_transaction and b_next_transaction pointers are NULL. - * Various places in JBD call jbd2_journal_remove_journal_head() to indicate that the - * journal_head can be dropped if needed. + * A journal_head is detached from its buffer_head when the journal_head's + * b_jcount reaches zero. Running transaction (b_transaction) and checkpoint + * transaction (b_cp_transaction) hold their references to b_jcount. * * Various places in the kernel want to attach a journal_head to a buffer_head * _before_ attaching the journal_head to a transaction. To protect the @@ -2094,17 +2093,16 @@ static void journal_free_journal_head(struct journal_head *jh) * (Attach a journal_head if needed. Increments b_jcount) * struct journal_head *jh = jbd2_journal_add_journal_head(bh); * ... + * (Get another reference for transaction) + * jbd2_journal_grab_journal_head(bh); * jh->b_transaction = xxx; + * (Put original reference) * jbd2_journal_put_journal_head(jh); - * - * Now, the journal_head's b_jcount is zero, but it is safe from being released - * because it has a non-zero b_transaction. */ /* * Give a buffer_head a journal_head. * - * Doesn't need the journal lock. * May sleep. */ struct journal_head *jbd2_journal_add_journal_head(struct buffer_head *bh) @@ -2168,61 +2166,29 @@ static void __journal_remove_journal_head(struct buffer_head *bh) struct journal_head *jh = bh2jh(bh); J_ASSERT_JH(jh, jh->b_jcount >= 0); - - get_bh(bh); - if (jh->b_jcount == 0) { - if (jh->b_transaction == NULL && - jh->b_next_transaction == NULL && - jh->b_cp_transaction == NULL) { - J_ASSERT_JH(jh, jh->b_jlist == BJ_None); - J_ASSERT_BH(bh, buffer_jbd(bh)); - J_ASSERT_BH(bh, jh2bh(jh) == bh); - BUFFER_TRACE(bh, "remove journal_head"); - if (jh->b_frozen_data) { - printk(KERN_WARNING "%s: freeing " - "b_frozen_data\n", - __func__); - jbd2_free(jh->b_frozen_data, bh->b_size); - } - if (jh->b_committed_data) { - printk(KERN_WARNING "%s: freeing " - "b_committed_data\n", - __func__); - jbd2_free(jh->b_committed_data, bh->b_size); - } - bh->b_private = NULL; - jh->b_bh = NULL; /* debug, really */ - clear_buffer_jbd(bh); - __brelse(bh); - journal_free_journal_head(jh); - } else { - BUFFER_TRACE(bh, "journal_head was locked"); - } + J_ASSERT_JH(jh, jh->b_transaction == NULL); + J_ASSERT_JH(jh, jh->b_next_transaction == NULL); + J_ASSERT_JH(jh, jh->b_cp_transaction == NULL); + J_ASSERT_JH(jh, jh->b_jlist == BJ_None); + J_ASSERT_BH(bh, buffer_jbd(bh)); + J_ASSERT_BH(bh, jh2bh(jh) == bh); + BUFFER_TRACE(bh, "remove journal_head"); + if (jh->b_frozen_data) { + printk(KERN_WARNING "%s: freeing b_frozen_data\n", __func__); + jbd2_free(jh->b_frozen_data, bh->b_size); } + if (jh->b_committed_data) { + printk(KERN_WARNING "%s: freeing b_committed_data\n", __func__); + jbd2_free(jh->b_committed_data, bh->b_size); + } + bh->b_private = NULL; + jh->b_bh = NULL; /* debug, really */ + clear_buffer_jbd(bh); + journal_free_journal_head(jh); } /* - * jbd2_journal_remove_journal_head(): if the buffer isn't attached to a transaction - * and has a zero b_jcount then remove and release its journal_head. If we did - * see that the buffer is not used by any transaction we also "logically" - * decrement ->b_count. - * - * We in fact take an additional increment on ->b_count as a convenience, - * because the caller usually wants to do additional things with the bh - * after calling here. - * The caller of jbd2_journal_remove_journal_head() *must* run __brelse(bh) at some - * time. Once the caller has run __brelse(), the buffer is eligible for - * reaping by try_to_free_buffers(). - */ -void jbd2_journal_remove_journal_head(struct buffer_head *bh) -{ - jbd_lock_bh_journal_head(bh); - __journal_remove_journal_head(bh); - jbd_unlock_bh_journal_head(bh); -} - -/* - * Drop a reference on the passed journal_head. If it fell to zero then try to + * Drop a reference on the passed journal_head. If it fell to zero then * release the journal_head from the buffer_head. */ void jbd2_journal_put_journal_head(struct journal_head *jh) @@ -2232,11 +2198,12 @@ void jbd2_journal_put_journal_head(struct journal_head *jh) jbd_lock_bh_journal_head(bh); J_ASSERT_JH(jh, jh->b_jcount > 0); --jh->b_jcount; - if (!jh->b_jcount && !jh->b_transaction) { + if (!jh->b_jcount) { __journal_remove_journal_head(bh); + jbd_unlock_bh_journal_head(bh); __brelse(bh); - } - jbd_unlock_bh_journal_head(bh); + } else + jbd_unlock_bh_journal_head(bh); } /* diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 547b101049e5..2d7109414cdd 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -30,6 +30,7 @@ #include static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh); +static void __jbd2_journal_unfile_buffer(struct journal_head *jh); /* * jbd2_get_transaction: obtain a new transaction_t object. @@ -764,7 +765,6 @@ repeat: if (!jh->b_transaction) { JBUFFER_TRACE(jh, "no transaction"); J_ASSERT_JH(jh, !jh->b_next_transaction); - jh->b_transaction = transaction; JBUFFER_TRACE(jh, "file as BJ_Reserved"); spin_lock(&journal->j_list_lock); __jbd2_journal_file_buffer(jh, transaction, BJ_Reserved); @@ -895,8 +895,6 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh) * committed and so it's safe to clear the dirty bit. */ clear_buffer_dirty(jh2bh(jh)); - jh->b_transaction = transaction; - /* first access by this transaction */ jh->b_modified = 0; @@ -1230,8 +1228,6 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) __jbd2_journal_file_buffer(jh, transaction, BJ_Forget); } else { __jbd2_journal_unfile_buffer(jh); - jbd2_journal_remove_journal_head(bh); - __brelse(bh); if (!buffer_jbd(bh)) { spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); @@ -1554,19 +1550,32 @@ void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh) mark_buffer_dirty(bh); /* Expose it to the VM */ } -void __jbd2_journal_unfile_buffer(struct journal_head *jh) +/* + * Remove buffer from all transactions. + * + * Called with bh_state lock and j_list_lock + * + * jh and bh may be already freed when this function returns. + */ +static void __jbd2_journal_unfile_buffer(struct journal_head *jh) { __jbd2_journal_temp_unlink_buffer(jh); jh->b_transaction = NULL; + jbd2_journal_put_journal_head(jh); } void jbd2_journal_unfile_buffer(journal_t *journal, struct journal_head *jh) { - jbd_lock_bh_state(jh2bh(jh)); + struct buffer_head *bh = jh2bh(jh); + + /* Get reference so that buffer cannot be freed before we unlock it */ + get_bh(bh); + jbd_lock_bh_state(bh); spin_lock(&journal->j_list_lock); __jbd2_journal_unfile_buffer(jh); spin_unlock(&journal->j_list_lock); - jbd_unlock_bh_state(jh2bh(jh)); + jbd_unlock_bh_state(bh); + __brelse(bh); } /* @@ -1593,8 +1602,6 @@ __journal_try_to_free_buffer(journal_t *journal, struct buffer_head *bh) if (jh->b_jlist == BJ_None) { JBUFFER_TRACE(jh, "remove from checkpoint list"); __jbd2_journal_remove_checkpoint(jh); - jbd2_journal_remove_journal_head(bh); - __brelse(bh); } } spin_unlock(&journal->j_list_lock); @@ -1657,7 +1664,6 @@ int jbd2_journal_try_to_free_buffers(journal_t *journal, /* * We take our own ref against the journal_head here to avoid * having to add tons of locking around each instance of - * jbd2_journal_remove_journal_head() and * jbd2_journal_put_journal_head(). */ jh = jbd2_journal_grab_journal_head(bh); @@ -1695,10 +1701,9 @@ static int __dispose_buffer(struct journal_head *jh, transaction_t *transaction) int may_free = 1; struct buffer_head *bh = jh2bh(jh); - __jbd2_journal_unfile_buffer(jh); - if (jh->b_cp_transaction) { JBUFFER_TRACE(jh, "on running+cp transaction"); + __jbd2_journal_temp_unlink_buffer(jh); /* * We don't want to write the buffer anymore, clear the * bit so that we don't confuse checks in @@ -1709,8 +1714,7 @@ static int __dispose_buffer(struct journal_head *jh, transaction_t *transaction) may_free = 0; } else { JBUFFER_TRACE(jh, "on running transaction"); - jbd2_journal_remove_journal_head(bh); - __brelse(bh); + __jbd2_journal_unfile_buffer(jh); } return may_free; } @@ -1988,6 +1992,8 @@ void __jbd2_journal_file_buffer(struct journal_head *jh, if (jh->b_transaction) __jbd2_journal_temp_unlink_buffer(jh); + else + jbd2_journal_grab_journal_head(bh); jh->b_transaction = transaction; switch (jlist) { @@ -2039,9 +2045,10 @@ void jbd2_journal_file_buffer(struct journal_head *jh, * already started to be used by a subsequent transaction, refile the * buffer on that transaction's metadata list. * - * Called under journal->j_list_lock - * + * Called under j_list_lock * Called under jbd_lock_bh_state(jh2bh(jh)) + * + * jh and bh may be already free when this function returns */ void __jbd2_journal_refile_buffer(struct journal_head *jh) { @@ -2065,6 +2072,11 @@ void __jbd2_journal_refile_buffer(struct journal_head *jh) was_dirty = test_clear_buffer_jbddirty(bh); __jbd2_journal_temp_unlink_buffer(jh); + /* + * We set b_transaction here because b_next_transaction will inherit + * our jh reference and thus __jbd2_journal_file_buffer() must not + * take a new one. + */ jh->b_transaction = jh->b_next_transaction; jh->b_next_transaction = NULL; if (buffer_freed(bh)) @@ -2081,30 +2093,21 @@ void __jbd2_journal_refile_buffer(struct journal_head *jh) } /* - * For the unlocked version of this call, also make sure that any - * hanging journal_head is cleaned up if necessary. - * - * __jbd2_journal_refile_buffer is usually called as part of a single locked - * operation on a buffer_head, in which the caller is probably going to - * be hooking the journal_head onto other lists. In that case it is up - * to the caller to remove the journal_head if necessary. For the - * unlocked jbd2_journal_refile_buffer call, the caller isn't going to be - * doing anything else to the buffer so we need to do the cleanup - * ourselves to avoid a jh leak. - * - * *** The journal_head may be freed by this call! *** + * __jbd2_journal_refile_buffer() with necessary locking added. We take our + * bh reference so that we can safely unlock bh. + * + * The jh and bh may be freed by this call. */ void jbd2_journal_refile_buffer(journal_t *journal, struct journal_head *jh) { struct buffer_head *bh = jh2bh(jh); + /* Get reference so that buffer cannot be freed before we unlock it */ + get_bh(bh); jbd_lock_bh_state(bh); spin_lock(&journal->j_list_lock); - __jbd2_journal_refile_buffer(jh); jbd_unlock_bh_state(bh); - jbd2_journal_remove_journal_head(bh); - spin_unlock(&journal->j_list_lock); __brelse(bh); } diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 4ecb7b16b278..d087c2e7b2aa 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1024,7 +1024,6 @@ struct journal_s /* Filing buffers */ extern void jbd2_journal_unfile_buffer(journal_t *, struct journal_head *); -extern void __jbd2_journal_unfile_buffer(struct journal_head *); extern void __jbd2_journal_refile_buffer(struct journal_head *); extern void jbd2_journal_refile_buffer(journal_t *, struct journal_head *); extern void __jbd2_journal_file_buffer(struct journal_head *, transaction_t *, int); @@ -1165,7 +1164,6 @@ extern void jbd2_journal_release_jbd_inode(journal_t *journal, struct jbd2_in */ struct journal_head *jbd2_journal_add_journal_head(struct buffer_head *bh); struct journal_head *jbd2_journal_grab_journal_head(struct buffer_head *bh); -void jbd2_journal_remove_journal_head(struct buffer_head *bh); void jbd2_journal_put_journal_head(struct journal_head *jh); /* -- cgit v1.2.3 From 6c8f7949931854be360fcc7f008f2672dc17996f Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 13 Jun 2011 12:19:27 +0200 Subject: IPVS: remove unused init and cleanup functions. After restructuring, there is some unused or empty functions left to be removed. Signed-off-by: Hans Schillstrom Signed-off-by: Simon Horman --- include/net/ip_vs.h | 6 ------ net/netfilter/ipvs/ip_vs_app.c | 10 ---------- net/netfilter/ipvs/ip_vs_core.c | 29 ++++------------------------- net/netfilter/ipvs/ip_vs_est.c | 9 --------- net/netfilter/ipvs/ip_vs_sync.c | 9 --------- 5 files changed, 4 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 1b0985fca1e2..b1370c4015b6 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1117,8 +1117,6 @@ extern void ip_vs_app_inc_put(struct ip_vs_app *inc); extern int ip_vs_app_pkt_out(struct ip_vs_conn *, struct sk_buff *skb); extern int ip_vs_app_pkt_in(struct ip_vs_conn *, struct sk_buff *skb); -extern int ip_vs_app_init(void); -extern void ip_vs_app_cleanup(void); void ip_vs_bind_pe(struct ip_vs_service *svc, struct ip_vs_pe *pe); void ip_vs_unbind_pe(struct ip_vs_service *svc); @@ -1221,15 +1219,11 @@ extern int start_sync_thread(struct net *net, int state, char *mcast_ifn, __u8 syncid); extern int stop_sync_thread(struct net *net, int state); extern void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp); -extern int ip_vs_sync_init(void); -extern void ip_vs_sync_cleanup(void); /* * IPVS rate estimator prototypes (from ip_vs_est.c) */ -extern int ip_vs_estimator_init(void); -extern void ip_vs_estimator_cleanup(void); extern void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats); extern void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats); extern void ip_vs_zero_estimator(struct ip_vs_stats *stats); diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index e223fb749ddf..fe6cb4304d72 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -589,13 +589,3 @@ void __net_exit ip_vs_app_net_cleanup(struct net *net) { proc_net_remove(net, "ip_vs_app"); } - -int __init ip_vs_app_init(void) -{ - return 0; -} - - -void ip_vs_app_cleanup(void) -{ -} diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 6cefe322b044..2200bae1d4dd 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1967,36 +1967,23 @@ static int __init ip_vs_init(void) { int ret; - ip_vs_estimator_init(); ret = ip_vs_control_init(); if (ret < 0) { pr_err("can't setup control.\n"); - goto cleanup_estimator; + goto exit; } ip_vs_protocol_init(); - ret = ip_vs_app_init(); - if (ret < 0) { - pr_err("can't setup application helper.\n"); - goto cleanup_protocol; - } - ret = ip_vs_conn_init(); if (ret < 0) { pr_err("can't setup connection table.\n"); - goto cleanup_app; - } - - ret = ip_vs_sync_init(); - if (ret < 0) { - pr_err("can't setup sync data.\n"); - goto cleanup_conn; + goto cleanup_protocol; } ret = register_pernet_subsys(&ipvs_core_ops); /* Alloc ip_vs struct */ if (ret < 0) - goto cleanup_sync; + goto cleanup_conn; ret = register_pernet_device(&ipvs_core_dev_ops); if (ret < 0) @@ -2016,17 +2003,12 @@ cleanup_dev: unregister_pernet_device(&ipvs_core_dev_ops); cleanup_sub: unregister_pernet_subsys(&ipvs_core_ops); -cleanup_sync: - ip_vs_sync_cleanup(); cleanup_conn: ip_vs_conn_cleanup(); -cleanup_app: - ip_vs_app_cleanup(); cleanup_protocol: ip_vs_protocol_cleanup(); ip_vs_control_cleanup(); -cleanup_estimator: - ip_vs_estimator_cleanup(); +exit: return ret; } @@ -2035,12 +2017,9 @@ static void __exit ip_vs_cleanup(void) nf_unregister_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); unregister_pernet_device(&ipvs_core_dev_ops); unregister_pernet_subsys(&ipvs_core_ops); /* free ip_vs struct */ - ip_vs_sync_cleanup(); ip_vs_conn_cleanup(); - ip_vs_app_cleanup(); ip_vs_protocol_cleanup(); ip_vs_control_cleanup(); - ip_vs_estimator_cleanup(); pr_info("ipvs unloaded.\n"); } diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index f5d2a01d69d4..0fac6017b6fb 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -207,12 +207,3 @@ void __net_exit ip_vs_estimator_net_cleanup(struct net *net) { del_timer_sync(&net_ipvs(net)->est_timer); } - -int __init ip_vs_estimator_init(void) -{ - return 0; -} - -void ip_vs_estimator_cleanup(void) -{ -} diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 58bfabbe7446..7ee7215b8ba0 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -1689,12 +1689,3 @@ void ip_vs_sync_net_cleanup(struct net *net) if (retc && retc != -ESRCH) pr_err("Failed to stop Backup Daemon\n"); } - -int __init ip_vs_sync_init(void) -{ - return 0; -} - -void ip_vs_sync_cleanup(void) -{ -} -- cgit v1.2.3 From 61a6976bf19a6cf5dfcf37c3536665b316f22d49 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 14 Jun 2011 12:40:19 +0900 Subject: serial: sh-sci: Abstract register maps. This takes a bit of a sledgehammer to the horribly CPU subtype ifdef-ridden header and abstracts all of the different register layouts in to distinct types which in turn can be overriden on a per-port basis, or permitted to default to the map matching the port type at probe time. In the process this ultimately fixes up inumerable bugs with mismatches on various CPU types (particularly the legacy ones that were obviously broken years ago and no one noticed) and provides a more tightly coupled and consolidated platform for extending and implementing generic features. Signed-off-by: Paul Mundt --- arch/sh/Makefile | 1 + arch/sh/include/cpu-sh3/cpu/serial.h | 10 + arch/sh/include/cpu-sh4a/cpu/serial.h | 7 + arch/sh/kernel/cpu/sh3/Makefile | 18 +- arch/sh/kernel/cpu/sh3/serial-sh770x.c | 33 +++ arch/sh/kernel/cpu/sh3/serial-sh7710.c | 20 ++ arch/sh/kernel/cpu/sh3/serial-sh7720.c | 36 ++++ arch/sh/kernel/cpu/sh3/setup-sh7705.c | 5 + arch/sh/kernel/cpu/sh3/setup-sh770x.c | 8 + arch/sh/kernel/cpu/sh3/setup-sh7720.c | 5 + arch/sh/kernel/cpu/sh4/setup-sh7750.c | 3 +- arch/sh/kernel/cpu/sh4/setup-sh7760.c | 4 + arch/sh/kernel/cpu/sh4a/Makefile | 2 +- arch/sh/kernel/cpu/sh4a/serial-sh7722.c | 23 ++ arch/sh/kernel/cpu/sh4a/setup-sh7366.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7722.c | 6 + arch/sh/kernel/cpu/sh4a/setup-sh7723.c | 9 + arch/sh/kernel/cpu/sh4a/setup-sh7724.c | 9 + arch/sh/kernel/cpu/sh4a/setup-sh7763.c | 3 + arch/sh/kernel/cpu/sh4a/setup-sh7780.c | 3 +- arch/sh/kernel/cpu/sh4a/setup-sh7785.c | 8 +- arch/sh/kernel/cpu/sh4a/setup-sh7786.c | 8 +- drivers/tty/serial/sh-sci.c | 364 ++++++++++++++++++++++++-------- drivers/tty/serial/sh-sci.h | 222 ------------------- include/linux/serial_sci.h | 36 ++++ 25 files changed, 516 insertions(+), 328 deletions(-) create mode 100644 arch/sh/include/cpu-sh3/cpu/serial.h create mode 100644 arch/sh/include/cpu-sh4a/cpu/serial.h create mode 100644 arch/sh/kernel/cpu/sh3/serial-sh770x.c create mode 100644 arch/sh/kernel/cpu/sh3/serial-sh7710.c create mode 100644 arch/sh/kernel/cpu/sh3/serial-sh7720.c create mode 100644 arch/sh/kernel/cpu/sh4a/serial-sh7722.c (limited to 'include') diff --git a/arch/sh/Makefile b/arch/sh/Makefile index e3d8170ad00b..99385d0b3f3b 100644 --- a/arch/sh/Makefile +++ b/arch/sh/Makefile @@ -173,6 +173,7 @@ core-$(CONFIG_HD6446X_SERIES) += arch/sh/cchips/hd6446x/ cpuincdir-$(CONFIG_CPU_SH2A) += cpu-sh2a cpuincdir-$(CONFIG_CPU_SH2) += cpu-sh2 cpuincdir-$(CONFIG_CPU_SH3) += cpu-sh3 +cpuincdir-$(CONFIG_CPU_SH4A) += cpu-sh4a cpuincdir-$(CONFIG_CPU_SH4) += cpu-sh4 cpuincdir-$(CONFIG_CPU_SH5) += cpu-sh5 cpuincdir-y += cpu-common # Must be last diff --git a/arch/sh/include/cpu-sh3/cpu/serial.h b/arch/sh/include/cpu-sh3/cpu/serial.h new file mode 100644 index 000000000000..7766329bc103 --- /dev/null +++ b/arch/sh/include/cpu-sh3/cpu/serial.h @@ -0,0 +1,10 @@ +#ifndef __CPU_SH3_SERIAL_H +#define __CPU_SH3_SERIAL_H + +#include + +extern struct plat_sci_port_ops sh770x_sci_port_ops; +extern struct plat_sci_port_ops sh7710_sci_port_ops; +extern struct plat_sci_port_ops sh7720_sci_port_ops; + +#endif /* __CPU_SH3_SERIAL_H */ diff --git a/arch/sh/include/cpu-sh4a/cpu/serial.h b/arch/sh/include/cpu-sh4a/cpu/serial.h new file mode 100644 index 000000000000..ff1bc275d210 --- /dev/null +++ b/arch/sh/include/cpu-sh4a/cpu/serial.h @@ -0,0 +1,7 @@ +#ifndef __CPU_SH4A_SERIAL_H +#define __CPU_SH4A_SERIAL_H + +/* arch/sh/kernel/cpu/sh4a/serial-sh7722.c */ +extern struct plat_sci_port_ops sh7722_sci_port_ops; + +#endif /* __CPU_SH4A_SERIAL_H */ diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile index ecab274141a8..6f13f33a35ff 100644 --- a/arch/sh/kernel/cpu/sh3/Makefile +++ b/arch/sh/kernel/cpu/sh3/Makefile @@ -7,15 +7,15 @@ obj-y := ex.o probe.o entry.o setup-sh3.o obj-$(CONFIG_HIBERNATION) += swsusp.o # CPU subtype setup -obj-$(CONFIG_CPU_SUBTYPE_SH7705) += setup-sh7705.o -obj-$(CONFIG_CPU_SUBTYPE_SH7706) += setup-sh770x.o -obj-$(CONFIG_CPU_SUBTYPE_SH7707) += setup-sh770x.o -obj-$(CONFIG_CPU_SUBTYPE_SH7708) += setup-sh770x.o -obj-$(CONFIG_CPU_SUBTYPE_SH7709) += setup-sh770x.o -obj-$(CONFIG_CPU_SUBTYPE_SH7710) += setup-sh7710.o -obj-$(CONFIG_CPU_SUBTYPE_SH7712) += setup-sh7710.o -obj-$(CONFIG_CPU_SUBTYPE_SH7720) += setup-sh7720.o -obj-$(CONFIG_CPU_SUBTYPE_SH7721) += setup-sh7720.o +obj-$(CONFIG_CPU_SUBTYPE_SH7705) += setup-sh7705.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7706) += setup-sh770x.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7707) += setup-sh770x.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7708) += setup-sh770x.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7709) += setup-sh770x.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7710) += setup-sh7710.o serial-sh7710.o +obj-$(CONFIG_CPU_SUBTYPE_SH7712) += setup-sh7710.o serial-sh7710.o +obj-$(CONFIG_CPU_SUBTYPE_SH7720) += setup-sh7720.o serial-sh7720.o +obj-$(CONFIG_CPU_SUBTYPE_SH7721) += setup-sh7720.o serial-sh7720.o # Primary on-chip clocks (common) clock-$(CONFIG_CPU_SH3) := clock-sh3.o diff --git a/arch/sh/kernel/cpu/sh3/serial-sh770x.c b/arch/sh/kernel/cpu/sh3/serial-sh770x.c new file mode 100644 index 000000000000..4f7242c676b3 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/serial-sh770x.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +#define SCPCR 0xA4000116 +#define SCPDR 0xA4000136 + +static void sh770x_sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + unsigned short data; + + /* We need to set SCPCR to enable RTS/CTS */ + data = __raw_readw(SCPCR); + /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/ + __raw_writew(data & 0x0fcf, SCPCR); + + if (!(cflag & CRTSCTS)) { + /* We need to set SCPCR to enable RTS/CTS */ + data = __raw_readw(SCPCR); + /* Clear out SCP7MD1,0, SCP4MD1,0, + Set SCP6MD1,0 = {01} (output) */ + __raw_writew((data & 0x0fcf) | 0x1000, SCPCR); + + data = __raw_readb(SCPDR); + /* Set /RTS2 (bit6) = 0 */ + __raw_writeb(data & 0xbf, SCPDR); + } +} + +struct plat_sci_port_ops sh770x_sci_port_ops = { + .init_pins = sh770x_sci_init_pins, +}; diff --git a/arch/sh/kernel/cpu/sh3/serial-sh7710.c b/arch/sh/kernel/cpu/sh3/serial-sh7710.c new file mode 100644 index 000000000000..42190ef6aebf --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/serial-sh7710.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +#define PACR 0xa4050100 +#define PBCR 0xa4050102 + +static void sh7710_sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + if (port->mapbase == 0xA4400000) { + __raw_writew(__raw_readw(PACR) & 0xffc0, PACR); + __raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR); + } else if (port->mapbase == 0xA4410000) + __raw_writew(__raw_readw(PBCR) & 0xf003, PBCR); +} + +struct plat_sci_port_ops sh7710_sci_port_ops = { + .init_pins = sh7710_sci_init_pins, +}; diff --git a/arch/sh/kernel/cpu/sh3/serial-sh7720.c b/arch/sh/kernel/cpu/sh3/serial-sh7720.c new file mode 100644 index 000000000000..8234e1e7abd9 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/serial-sh7720.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +static void sh7720_sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + unsigned short data; + + if (cflag & CRTSCTS) { + /* enable RTS/CTS */ + if (port->mapbase == 0xa4430000) { /* SCIF0 */ + /* Clear PTCR bit 9-2; enable all scif pins but sck */ + data = __raw_readw(PORT_PTCR); + __raw_writew((data & 0xfc03), PORT_PTCR); + } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ + /* Clear PVCR bit 9-2 */ + data = __raw_readw(PORT_PVCR); + __raw_writew((data & 0xfc03), PORT_PVCR); + } + } else { + if (port->mapbase == 0xa4430000) { /* SCIF0 */ + /* Clear PTCR bit 5-2; enable only tx and rx */ + data = __raw_readw(PORT_PTCR); + __raw_writew((data & 0xffc3), PORT_PTCR); + } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ + /* Clear PVCR bit 5-2 */ + data = __raw_readw(PORT_PVCR); + __raw_writew((data & 0xffc3), PORT_PVCR); + } + } +} + +struct plat_sci_port_ops sh7720_sci_port_ops = { + .init_pins = sh7720_sci_init_pins, +}; diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7705.c b/arch/sh/kernel/cpu/sh3/setup-sh7705.c index cd2e702feb7e..2309618c015d 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh7705.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh7705.c @@ -15,6 +15,7 @@ #include #include #include +#include enum { UNUSED = 0, @@ -75,6 +76,8 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_4, .type = PORT_SCIF, .irqs = { 56, 56, 56 }, + .ops = &sh770x_sci_port_ops, + .regtype = SCIx_SH7705_SCIF_REGTYPE, }; static struct platform_device scif0_device = { @@ -92,6 +95,8 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_4, .type = PORT_SCIF, .irqs = { 52, 52, 52 }, + .ops = &sh770x_sci_port_ops, + .regtype = SCIx_SH7705_SCIF_REGTYPE, }; static struct platform_device scif1_device = { diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c index 6d549792f791..3f3d5fe5892d 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh770x.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c @@ -19,6 +19,7 @@ #include #include #include +#include enum { UNUSED = 0, @@ -114,6 +115,8 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCI, .irqs = { 23, 23, 23, 0 }, + .ops = &sh770x_sci_port_ops, + .regshift = 1, }; static struct platform_device scif0_device = { @@ -133,6 +136,8 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 56, 56, 56, 56 }, + .ops = &sh770x_sci_port_ops, + .regtype = SCIx_SH3_SCIF_REGTYPE, }; static struct platform_device scif1_device = { @@ -147,11 +152,14 @@ static struct platform_device scif1_device = { defined(CONFIG_CPU_SUBTYPE_SH7709) static struct plat_sci_port scif2_platform_data = { .mapbase = 0xa4000140, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_TE | SCSCR_RE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_IRDA, .irqs = { 52, 52, 52, 52 }, + .ops = &sh770x_sci_port_ops, + .regshift = 1, }; static struct platform_device scif2_device = { diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7720.c b/arch/sh/kernel/cpu/sh3/setup-sh7720.c index 365b94a6fcb7..94920345c14d 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh7720.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh7720.c @@ -20,6 +20,7 @@ #include #include #include +#include static struct resource rtc_resources[] = { [0] = { @@ -55,6 +56,8 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_4, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, + .ops = &sh7720_sci_port_ops, + .regtype = SCIx_SH7705_SCIF_REGTYPE, }; static struct platform_device scif0_device = { @@ -72,6 +75,8 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_4, .type = PORT_SCIF, .irqs = { 81, 81, 81, 81 }, + .ops = &sh7720_sci_port_ops, + .regtype = SCIx_SH7705_SCIF_REGTYPE, }; static struct platform_device scif1_device = { diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c index 8ea26e791187..c10db5b96e59 100644 --- a/arch/sh/kernel/cpu/sh4/setup-sh7750.c +++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c @@ -1,5 +1,5 @@ /* - * SH7750/SH7751 Setup + * SH7091/SH7750/SH7750S/SH7750R/SH7751/SH7751R Setup * * Copyright (C) 2006 Paul Mundt * Copyright (C) 2006 Jamie Lenehan @@ -44,6 +44,7 @@ static struct plat_sci_port sci_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCI, .irqs = { 23, 23, 23, 0 }, + .regshift = 2, }; static struct platform_device sci_device = { diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7760.c b/arch/sh/kernel/cpu/sh4/setup-sh7760.c index 78bbf232e391..c0b4c774700e 100644 --- a/arch/sh/kernel/cpu/sh4/setup-sh7760.c +++ b/arch/sh/kernel/cpu/sh4/setup-sh7760.c @@ -133,6 +133,7 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 52, 53, 55, 54 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif0_device = { @@ -150,6 +151,7 @@ static struct plat_sci_port scif1_platform_data = { .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .irqs = { 72, 73, 75, 74 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif1_device = { @@ -167,6 +169,7 @@ static struct plat_sci_port scif2_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 76, 77, 79, 78 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif2_device = { @@ -184,6 +187,7 @@ static struct plat_sci_port scif3_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCI, .irqs = { 80, 81, 82, 0 }, + .regshift = 2, }; static struct platform_device scif3_device = { diff --git a/arch/sh/kernel/cpu/sh4a/Makefile b/arch/sh/kernel/cpu/sh4a/Makefile index cc122b1d3035..c57fb287011e 100644 --- a/arch/sh/kernel/cpu/sh4a/Makefile +++ b/arch/sh/kernel/cpu/sh4a/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_CPU_SUBTYPE_SH7780) += setup-sh7780.o obj-$(CONFIG_CPU_SUBTYPE_SH7785) += setup-sh7785.o obj-$(CONFIG_CPU_SUBTYPE_SH7786) += setup-sh7786.o intc-shx3.o obj-$(CONFIG_CPU_SUBTYPE_SH7343) += setup-sh7343.o -obj-$(CONFIG_CPU_SUBTYPE_SH7722) += setup-sh7722.o +obj-$(CONFIG_CPU_SUBTYPE_SH7722) += setup-sh7722.o serial-sh7722.o obj-$(CONFIG_CPU_SUBTYPE_SH7723) += setup-sh7723.o obj-$(CONFIG_CPU_SUBTYPE_SH7724) += setup-sh7724.o obj-$(CONFIG_CPU_SUBTYPE_SH7366) += setup-sh7366.o diff --git a/arch/sh/kernel/cpu/sh4a/serial-sh7722.c b/arch/sh/kernel/cpu/sh4a/serial-sh7722.c new file mode 100644 index 000000000000..59bc3a72702e --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/serial-sh7722.c @@ -0,0 +1,23 @@ +#include +#include +#include + +#define PSCR 0xA405011E + +static void sh7722_sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + unsigned short data; + + if (port->mapbase == 0xffe00000) { + data = __raw_readw(PSCR); + data &= ~0x03cf; + if (!(cflag & CRTSCTS)) + data |= 0x0340; + + __raw_writew(data, PSCR); + } +} + +struct plat_sci_port_ops sh7722_sci_port_ops = { + .init_pins = sh7722_sci_init_pins, +}; diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c index 82616af64d62..87773869a2f3 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c @@ -20,6 +20,7 @@ static struct plat_sci_port scif0_platform_data = { .mapbase = 0xffe00000, + .port_reg = 0xa405013e, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c index 5813d8023619..863249dbf05b 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c @@ -185,6 +185,8 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, + .ops = &sh7722_sci_port_ops, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif0_device = { @@ -202,6 +204,8 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 81, 81, 81, 81 }, + .ops = &sh7722_sci_port_ops, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif1_device = { @@ -219,6 +223,8 @@ static struct plat_sci_port scif2_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 82, 82, 82, 82 }, + .ops = &sh7722_sci_port_ops, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif2_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c index 072382280f96..3c2810d8f72e 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c @@ -23,11 +23,13 @@ /* Serial */ static struct plat_sci_port scif0_platform_data = { .mapbase = 0xffe00000, + .port_reg = 0xa4050160, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif0_device = { @@ -40,11 +42,13 @@ static struct platform_device scif0_device = { static struct plat_sci_port scif1_platform_data = { .mapbase = 0xffe10000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 81, 81, 81, 81 }, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif1_device = { @@ -57,11 +61,13 @@ static struct platform_device scif1_device = { static struct plat_sci_port scif2_platform_data = { .mapbase = 0xffe20000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 82, 82, 82, 82 }, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif2_device = { @@ -75,6 +81,7 @@ static struct platform_device scif2_device = { static struct plat_sci_port scif3_platform_data = { .mapbase = 0xa4e30000, .flags = UPF_BOOT_AUTOCONF, + .port_reg = SCIx_NOT_SUPPORTED, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_3, .type = PORT_SCIFA, @@ -91,6 +98,7 @@ static struct platform_device scif3_device = { static struct plat_sci_port scif4_platform_data = { .mapbase = 0xa4e40000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_3, @@ -108,6 +116,7 @@ static struct platform_device scif4_device = { static struct plat_sci_port scif5_platform_data = { .mapbase = 0xa4e50000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_3, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c index 0333fe9e3881..8c892887ebd7 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c @@ -256,11 +256,13 @@ static struct platform_device dma1_device = { /* Serial */ static struct plat_sci_port scif0_platform_data = { .mapbase = 0xffe00000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif0_device = { @@ -273,11 +275,13 @@ static struct platform_device scif0_device = { static struct plat_sci_port scif1_platform_data = { .mapbase = 0xffe10000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 81, 81, 81, 81 }, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif1_device = { @@ -290,11 +294,13 @@ static struct platform_device scif1_device = { static struct plat_sci_port scif2_platform_data = { .mapbase = 0xffe20000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE, .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 82, 82, 82, 82 }, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, }; static struct platform_device scif2_device = { @@ -307,6 +313,7 @@ static struct platform_device scif2_device = { static struct plat_sci_port scif3_platform_data = { .mapbase = 0xa4e30000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE, .scbrr_algo_id = SCBRR_ALGO_3, @@ -324,6 +331,7 @@ static struct platform_device scif3_device = { static struct plat_sci_port scif4_platform_data = { .mapbase = 0xa4e40000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE, .scbrr_algo_id = SCBRR_ALGO_3, @@ -341,6 +349,7 @@ static struct platform_device scif4_device = { static struct plat_sci_port scif5_platform_data = { .mapbase = 0xa4e50000, + .port_reg = SCIx_NOT_SUPPORTED, .flags = UPF_BOOT_AUTOCONF, .scscr = SCSCR_RE | SCSCR_TE, .scbrr_algo_id = SCBRR_ALGO_3, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c index 593eca6509b5..00113515f233 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c @@ -23,6 +23,7 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 40, 40, 40, 40 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif0_device = { @@ -40,6 +41,7 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 76, 76, 76, 76 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif1_device = { @@ -57,6 +59,7 @@ static struct plat_sci_port scif2_platform_data = { .scbrr_algo_id = SCBRR_ALGO_2, .type = PORT_SCIF, .irqs = { 104, 104, 104, 104 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif2_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c index 08add7fa6849..3d4d2075c19a 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c @@ -14,7 +14,6 @@ #include #include #include - #include static struct plat_sci_port scif0_platform_data = { @@ -24,6 +23,7 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 40, 40, 40, 40 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif0_device = { @@ -41,6 +41,7 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 76, 76, 76, 76 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif1_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c index 18d8fc136fb2..b29e6340414a 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c @@ -15,9 +15,7 @@ #include #include #include - #include - #include static struct plat_sci_port scif0_platform_data = { @@ -27,6 +25,7 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 40, 40, 40, 40 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif0_device = { @@ -44,6 +43,7 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 44, 44, 44, 44 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif1_device = { @@ -61,6 +61,7 @@ static struct plat_sci_port scif2_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 60, 60, 60, 60 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif2_device = { @@ -78,6 +79,7 @@ static struct plat_sci_port scif3_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 61, 61, 61, 61 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif3_device = { @@ -95,6 +97,7 @@ static struct plat_sci_port scif4_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 62, 62, 62, 62 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif4_device = { @@ -112,6 +115,7 @@ static struct plat_sci_port scif5_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 63, 63, 63, 63 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif5_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c index beba32beb6d9..dd5e709f9821 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c @@ -1,7 +1,7 @@ /* * SH7786 Setup * - * Copyright (C) 2009 - 2010 Renesas Solutions Corp. + * Copyright (C) 2009 - 2011 Renesas Solutions Corp. * Kuninori Morimoto * Paul Mundt * @@ -33,6 +33,7 @@ static struct plat_sci_port scif0_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 40, 41, 43, 42 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif0_device = { @@ -53,6 +54,7 @@ static struct plat_sci_port scif1_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 44, 44, 44, 44 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif1_device = { @@ -70,6 +72,7 @@ static struct plat_sci_port scif2_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 50, 50, 50, 50 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif2_device = { @@ -87,6 +90,7 @@ static struct plat_sci_port scif3_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 51, 51, 51, 51 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif3_device = { @@ -104,6 +108,7 @@ static struct plat_sci_port scif4_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 52, 52, 52, 52 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif4_device = { @@ -121,6 +126,7 @@ static struct plat_sci_port scif5_platform_data = { .scbrr_algo_id = SCBRR_ALGO_1, .type = PORT_SCIF, .irqs = { 53, 53, 53, 53 }, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, }; static struct platform_device scif5_device = { diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 3248ddaa889d..14e1bae50392 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -117,6 +117,255 @@ to_sci_port(struct uart_port *uart) return container_of(uart, struct sci_port, port); } +struct plat_sci_reg { + u8 offset, size; +}; + +/* Helper for invalidating specific entries of an inherited map. */ +#define sci_reg_invalid { .offset = 0, .size = 0 } + +static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { + [SCIx_PROBE_REGTYPE] = { + [0 ... SCIx_NR_REGS - 1] = sci_reg_invalid, + }, + + /* + * Common SCI definitions, dependent on the port's regshift + * value. + */ + [SCIx_SCI_REGTYPE] = { + [SCSMR] = { 0x00, 8 }, + [SCBRR] = { 0x01, 8 }, + [SCSCR] = { 0x02, 8 }, + [SCxTDR] = { 0x03, 8 }, + [SCxSR] = { 0x04, 8 }, + [SCxRDR] = { 0x05, 8 }, + [SCFCR] = sci_reg_invalid, + [SCFDR] = sci_reg_invalid, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = sci_reg_invalid, + }, + + /* + * Common definitions for legacy IrDA ports, dependent on + * regshift value. + */ + [SCIx_IRDA_REGTYPE] = { + [SCSMR] = { 0x00, 8 }, + [SCBRR] = { 0x01, 8 }, + [SCSCR] = { 0x02, 8 }, + [SCxTDR] = { 0x03, 8 }, + [SCxSR] = { 0x04, 8 }, + [SCxRDR] = { 0x05, 8 }, + [SCFCR] = { 0x06, 8 }, + [SCFDR] = { 0x07, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = sci_reg_invalid, + }, + + /* + * Common SCIFA definitions. + */ + [SCIx_SCIFA_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x20, 8 }, + [SCxSR] = { 0x14, 16 }, + [SCxRDR] = { 0x24, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = sci_reg_invalid, + }, + + /* + * Common SCIFB definitions. + */ + [SCIx_SCIFB_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x40, 8 }, + [SCxSR] = { 0x14, 16 }, + [SCxRDR] = { 0x60, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = sci_reg_invalid, + }, + + /* + * Common SH-3 SCIF definitions. + */ + [SCIx_SH3_SCIF_REGTYPE] = { + [SCSMR] = { 0x00, 8 }, + [SCBRR] = { 0x02, 8 }, + [SCSCR] = { 0x04, 8 }, + [SCxTDR] = { 0x06, 8 }, + [SCxSR] = { 0x08, 16 }, + [SCxRDR] = { 0x0a, 8 }, + [SCFCR] = { 0x0c, 8 }, + [SCFDR] = { 0x0e, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = sci_reg_invalid, + }, + + /* + * Common SH-4(A) SCIF(B) definitions. + */ + [SCIx_SH4_SCIF_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = { 0x20, 16 }, + [SCLSR] = { 0x24, 16 }, + }, + + /* + * Common SH-4(A) SCIF(B) definitions for ports without an SCSPTR + * register. + */ + [SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = { 0x24, 16 }, + }, + + /* + * Common SH-4(A) SCIF(B) definitions for ports with FIFO data + * count registers. + */ + [SCIx_SH4_SCIF_FIFODATA_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = { 0x1c, 16 }, /* aliased to SCFDR */ + [SCRFDR] = { 0x20, 16 }, + [SCSPTR] = { 0x24, 16 }, + [SCLSR] = { 0x28, 16 }, + }, + + /* + * SH7705-style SCIF(B) ports, lacking both SCSPTR and SCLSR + * registers. + */ + [SCIx_SH7705_SCIF_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x20, 8 }, + [SCxSR] = { 0x14, 16 }, + [SCxRDR] = { 0x24, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = sci_reg_invalid, + [SCLSR] = sci_reg_invalid, + }, +}; + +/* + * The "offset" here is rather misleading, in that it refers to an enum + * value relative to the port mapping rather than the fixed offset + * itself, which needs to be manually retrieved from the platform's + * register map for the given port. + */ +static unsigned int sci_serial_in(struct uart_port *p, int offset) +{ + struct sci_port *s = to_sci_port(p); + struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + offset; + + if (reg->size == 8) + return ioread8(p->membase + (reg->offset << p->regshift)); + else if (reg->size == 16) + return ioread16(p->membase + (reg->offset << p->regshift)); + else + WARN(1, "Invalid register access\n"); + + return 0; +} + +static void sci_serial_out(struct uart_port *p, int offset, int value) +{ + struct sci_port *s = to_sci_port(p); + struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + offset; + + if (reg->size == 8) + iowrite8(value, p->membase + (reg->offset << p->regshift)); + else if (reg->size == 16) + iowrite16(value, p->membase + (reg->offset << p->regshift)); + else + WARN(1, "Invalid register access\n"); +} + +#define sci_in(up, offset) (up->serial_in(up, offset)) +#define sci_out(up, offset, value) (up->serial_out(up, offset, value)) + +static int sci_probe_regmap(struct plat_sci_port *cfg) +{ + switch (cfg->type) { + case PORT_SCI: + cfg->regtype = SCIx_SCI_REGTYPE; + break; + case PORT_IRDA: + cfg->regtype = SCIx_IRDA_REGTYPE; + break; + case PORT_SCIFA: + cfg->regtype = SCIx_SCIFA_REGTYPE; + break; + case PORT_SCIFB: + cfg->regtype = SCIx_SCIFB_REGTYPE; + break; + case PORT_SCIF: + /* + * The SH-4 is a bit of a misnomer here, although that's + * where this particular port layout originated. This + * configuration (or some slight variation thereof) + * remains the dominant model for all SCIFs. + */ + cfg->regtype = SCIx_SH4_SCIF_REGTYPE; + break; + default: + printk(KERN_ERR "Can't probe register map for given port\n"); + return -EINVAL; + } + + return 0; +} + #if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) #ifdef CONFIG_CONSOLE_POLL @@ -160,103 +409,29 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c) } #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ -#if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - if (port->mapbase == 0xA4400000) { - __raw_writew(__raw_readw(PACR) & 0xffc0, PACR); - __raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR); - } else if (port->mapbase == 0xA4410000) - __raw_writew(__raw_readw(PBCR) & 0xf003, PBCR); -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || defined(CONFIG_CPU_SUBTYPE_SH7721) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - unsigned short data; - - if (cflag & CRTSCTS) { - /* enable RTS/CTS */ - if (port->mapbase == 0xa4430000) { /* SCIF0 */ - /* Clear PTCR bit 9-2; enable all scif pins but sck */ - data = __raw_readw(PORT_PTCR); - __raw_writew((data & 0xfc03), PORT_PTCR); - } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ - /* Clear PVCR bit 9-2 */ - data = __raw_readw(PORT_PVCR); - __raw_writew((data & 0xfc03), PORT_PVCR); - } - } else { - if (port->mapbase == 0xa4430000) { /* SCIF0 */ - /* Clear PTCR bit 5-2; enable only tx and rx */ - data = __raw_readw(PORT_PTCR); - __raw_writew((data & 0xffc3), PORT_PTCR); - } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ - /* Clear PVCR bit 5-2 */ - data = __raw_readw(PORT_PVCR); - __raw_writew((data & 0xffc3), PORT_PVCR); - } - } -} -#elif defined(CONFIG_CPU_SH3) -/* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */ -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) +static void sci_init_pins(struct uart_port *port, unsigned int cflag) { - unsigned short data; - - /* We need to set SCPCR to enable RTS/CTS */ - data = __raw_readw(SCPCR); - /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/ - __raw_writew(data & 0x0fcf, SCPCR); - - if (!(cflag & CRTSCTS)) { - /* We need to set SCPCR to enable RTS/CTS */ - data = __raw_readw(SCPCR); - /* Clear out SCP7MD1,0, SCP4MD1,0, - Set SCP6MD1,0 = {01} (output) */ - __raw_writew((data & 0x0fcf) | 0x1000, SCPCR); + struct sci_port *s = to_sci_port(port); + struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR; - data = __raw_readb(SCPDR); - /* Set /RTS2 (bit6) = 0 */ - __raw_writeb(data & 0xbf, SCPDR); + /* + * Use port-specific handler if provided. + */ + if (s->cfg->ops && s->cfg->ops->init_pins) { + s->cfg->ops->init_pins(port, cflag); + return; } -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7722) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - unsigned short data; - if (port->mapbase == 0xffe00000) { - data = __raw_readw(PSCR); - data &= ~0x03cf; - if (!(cflag & CRTSCTS)) - data |= 0x0340; + /* + * For the generic path SCSPTR is necessary. Bail out if that's + * unavailable, too. + */ + if (!reg->size) + return; - __raw_writew(data, PSCR); - } -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7757) || \ - defined(CONFIG_CPU_SUBTYPE_SH7763) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) || \ - defined(CONFIG_CPU_SUBTYPE_SHX3) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - if (!(cflag & CRTSCTS)) - __raw_writew(0x0080, SCSPTR0); /* Set RTS = 1 */ -} -#elif defined(CONFIG_CPU_SH4) && !defined(CONFIG_CPU_SH4A) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ if (!(cflag & CRTSCTS)) - __raw_writew(0x0080, SCSPTR2); /* Set RTS = 1 */ + sci_out(port, SCSPTR, 0x0080); /* Set RTS = 1 */ } -#else -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - /* Nothing to do */ -} -#endif #if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ defined(CONFIG_CPU_SUBTYPE_SH7780) || \ @@ -1752,6 +1927,9 @@ static int __devinit sci_init_single(struct platform_device *dev, break; } + if (p->regtype == SCIx_PROBE_REGTYPE) + BUG_ON(sci_probe_regmap(p) != 0); + if (dev) { sci_port->iclk = clk_get(&dev->dev, "sci_ick"); if (IS_ERR(sci_port->iclk)) { @@ -1812,9 +1990,10 @@ static int __devinit sci_init_single(struct platform_device *dev, port->mapbase = p->mapbase; port->type = p->type; port->flags = p->flags; + port->regshift = p->regshift; /* - * The UART port needs an IRQ value, so we peg this to the TX IRQ + * The UART port needs an IRQ value, so we peg this to the RX IRQ * for the multi-IRQ ports, which is where we are primarily * concerned with the shutdown path synchronization. * @@ -1822,6 +2001,9 @@ static int __devinit sci_init_single(struct platform_device *dev, */ port->irq = p->irqs[SCIx_RXI_IRQ]; + port->serial_in = sci_serial_in; + port->serial_out = sci_serial_out; + if (p->dma_dev) dev_dbg(port->dev, "DMA device %p, tx %d, rx %d\n", p->dma_dev, p->dma_slave_tx, p->dma_slave_rx); diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 5834f33d20ff..26de640a9d01 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -2,69 +2,6 @@ #include #include -#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7706) || \ - defined(CONFIG_CPU_SUBTYPE_SH7707) || \ - defined(CONFIG_CPU_SUBTYPE_SH7708) || \ - defined(CONFIG_CPU_SUBTYPE_SH7709) -# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */ -# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) -# define PORT_PTCR 0xA405011EUL -# define PORT_PVCR 0xA4050122UL -#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ - defined(CONFIG_CPU_SUBTYPE_SH7091) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751R) || \ - defined(CONFIG_CPU_SUBTYPE_SH4_202) -# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7760) -# define SCSPTR0 0xfe600024 /* 16 bit SCIF */ -# define SCSPTR2 0xfe620024 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -# define SCSPTR0 0xA4400000 /* 16 bit SCIF */ -# define PACR 0xa4050100 -# define PBCR 0xa4050102 -#elif defined(CONFIG_CPU_SUBTYPE_SH7343) -# define SCSPTR0 0xffe00010 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7722) -# define PWDR 0xA4050166 -# define PSCR 0xA405011E -#elif defined(CONFIG_CPU_SUBTYPE_SH7366) -# define SCPDR0 0xA405013E /* 16 bit SCIF0 PSDR */ -# define SCSPTR0 SCPDR0 -#elif defined(CONFIG_CPU_SUBTYPE_SH7723) -# define SCSPTR0 0xa4050160 -#elif defined(CONFIG_CPU_SUBTYPE_SH7757) -# define SCSPTR0 0xfe4b0020 -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) -# define SCSPTR0 0xffe00024 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7770) -# define SCSPTR0 0xff923020 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) -# define SCSPTR0 0xffea0024 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \ - defined(CONFIG_CPU_SUBTYPE_SH7203) || \ - defined(CONFIG_CPU_SUBTYPE_SH7206) || \ - defined(CONFIG_CPU_SUBTYPE_SH7263) -# define SCSPTR0 0xfffe8020 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7619) -# define SCSPTR0 0xf8400020 /* 16 bit SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SHX3) -# define SCSPTR0 0xffc30020 /* 16 bit SCIF */ -#else -# error CPU subtype not defined -#endif - #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ @@ -119,162 +56,3 @@ #define SCI_MAJOR 204 #define SCI_MINOR_START 8 - -#define SCI_IN(size, offset) \ - ioread##size(port->membase + (offset)) - -#define SCI_OUT(size, offset, value) \ - iowrite##size(value, port->membase + (offset)) - -#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ - static inline unsigned int sci_##name##_in(struct uart_port *port) \ - { \ - if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ - return SCI_IN(scif_size, scif_offset); \ - } else { /* PORT_SCI or PORT_SCIFA */ \ - return SCI_IN(sci_size, sci_offset); \ - } \ - } \ - static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ - { \ - if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ - SCI_OUT(scif_size, scif_offset, value); \ - } else { /* PORT_SCI or PORT_SCIFA */ \ - SCI_OUT(sci_size, sci_offset, value); \ - } \ - } - -#define CPU_SCIF_FNS(name, scif_offset, scif_size) \ - static inline unsigned int sci_##name##_in(struct uart_port *port) \ - { \ - return SCI_IN(scif_size, scif_offset); \ - } \ - static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ - { \ - SCI_OUT(scif_size, scif_offset, value); \ - } - -#if defined(CONFIG_CPU_SH3) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) -#if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) -#elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH7367) -#define SCIF_FNS(name, scif_offset, scif_size) \ - CPU_SCIF_FNS(name, scif_offset, scif_size) -#elif defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) || \ - defined(CONFIG_ARCH_SH73A0) -#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \ - CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) -#define SCIF_FNS(name, scif_offset, scif_size) \ - CPU_SCIF_FNS(name, scif_offset, scif_size) -#else -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh3_scif_offset, sh3_scif_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh3_scif_offset, sh3_scif_size) -#endif -#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ - defined(CONFIG_CPU_SUBTYPE_SH7724) - #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) - #define SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) -#else -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) -#endif - -#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH7367) - -SCIF_FNS(SCSMR, 0x00, 16) -SCIF_FNS(SCBRR, 0x04, 8) -SCIF_FNS(SCSCR, 0x08, 16) -SCIF_FNS(SCxSR, 0x14, 16) -SCIF_FNS(SCFCR, 0x18, 16) -SCIF_FNS(SCFDR, 0x1c, 16) -SCIF_FNS(SCxTDR, 0x20, 8) -SCIF_FNS(SCxRDR, 0x24, 8) -SCIF_FNS(SCLSR, 0x00, 0) -#elif defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) || \ - defined(CONFIG_ARCH_SH73A0) -SCIF_FNS(SCSMR, 0x00, 16) -SCIF_FNS(SCBRR, 0x04, 8) -SCIF_FNS(SCSCR, 0x08, 16) -SCIF_FNS(SCTDSR, 0x0c, 16) -SCIF_FNS(SCFER, 0x10, 16) -SCIF_FNS(SCxSR, 0x14, 16) -SCIF_FNS(SCFCR, 0x18, 16) -SCIF_FNS(SCFDR, 0x1c, 16) -SCIF_FNS(SCTFDR, 0x38, 16) -SCIF_FNS(SCRFDR, 0x3c, 16) -SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8) -SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8) -SCIF_FNS(SCLSR, 0x00, 0) -#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ - defined(CONFIG_CPU_SUBTYPE_SH7724) -SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16) -SCIx_FNS(SCBRR, 0x04, 8, 0x04, 8) -SCIx_FNS(SCSCR, 0x08, 16, 0x08, 16) -SCIx_FNS(SCxTDR, 0x20, 8, 0x0c, 8) -SCIx_FNS(SCxSR, 0x14, 16, 0x10, 16) -SCIx_FNS(SCxRDR, 0x24, 8, 0x14, 8) -SCIx_FNS(SCSPTR, 0, 0, 0, 0) -SCIF_FNS(SCFCR, 0x18, 16) -SCIF_FNS(SCFDR, 0x1c, 16) -SCIF_FNS(SCLSR, 0x24, 16) -#else -/* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 */ -/* name off sz off sz off sz off sz */ -SCIx_FNS(SCSMR, 0x00, 8, 0x00, 8, 0x00, 8, 0x00, 16) -SCIx_FNS(SCBRR, 0x02, 8, 0x04, 8, 0x02, 8, 0x04, 8) -SCIx_FNS(SCSCR, 0x04, 8, 0x08, 8, 0x04, 8, 0x08, 16) -SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8) -SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16) -SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8) -SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16) -#if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) -SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) -SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) -SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) -SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) -SCIF_FNS(SCLSR, 0, 0, 0x28, 16) -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) -SCIF_FNS(SCFDR, 0, 0, 0x1C, 16) -SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) -SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) -SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) -SCIF_FNS(SCLSR, 0, 0, 0x28, 16) -#else -SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) -#if defined(CONFIG_CPU_SUBTYPE_SH7722) -SCIF_FNS(SCSPTR, 0, 0, 0, 0) -#else -SCIF_FNS(SCSPTR, 0, 0, 0x20, 16) -#endif -SCIF_FNS(SCLSR, 0, 0, 0x24, 16) -#endif -#endif -#define sci_in(port, reg) sci_##reg##_in(port) -#define sci_out(port, reg, value) sci_##reg##_out(port, value) diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index ecefec7c0b67..4ca130a90ea5 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -58,6 +58,22 @@ enum { SCIx_NR_IRQS, }; +enum { + SCIx_PROBE_REGTYPE, + + SCIx_SCI_REGTYPE, + SCIx_IRDA_REGTYPE, + SCIx_SCIFA_REGTYPE, + SCIx_SCIFB_REGTYPE, + SCIx_SH3_SCIF_REGTYPE, + SCIx_SH4_SCIF_REGTYPE, + SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, + SCIx_SH4_SCIF_FIFODATA_REGTYPE, + SCIx_SH7705_SCIF_REGTYPE, + + SCIx_NR_REGTYPES, +}; + #define SCIx_IRQ_MUXED(irq) \ { \ [SCIx_ERI_IRQ] = (irq), \ @@ -66,8 +82,24 @@ enum { [SCIx_BRI_IRQ] = (irq), \ } +/* + * SCI register subset common for all port types. + * Not all registers will exist on all parts. + */ +enum { + SCSMR, SCBRR, SCSCR, SCxSR, + SCFCR, SCFDR, SCxTDR, SCxRDR, + SCLSR, SCTFDR, SCRFDR, SCSPTR, + + SCIx_NR_REGS, +}; + struct device; +struct plat_sci_port_ops { + void (*init_pins)(struct uart_port *, unsigned int cflag); +}; + /* * Platform device specific platform_data struct */ @@ -87,6 +119,10 @@ struct plat_sci_port { unsigned int error_mask; int port_reg; + unsigned char regshift; + unsigned char regtype; + + struct plat_sci_port_ops *ops; struct device *dma_dev; -- cgit v1.2.3 From 55309216baeb9d7f951520cf8e8bf2337cd17bad Mon Sep 17 00:00:00 2001 From: Harry Butterworth Date: Sat, 11 Jun 2011 16:02:06 +0800 Subject: ALSA: ctxfi: Add support for Creative Titanium HD Initialise model-specific DAC and ADC parts. Add controls for output and mic source selection. Rename some mixer controls according to ControlNames.txt. Remove Playback switches for Line-in and IEC958-in - these were controlling the input mute/unmute which affected capture too. Use the capture switches to control the input mute/unmute instead - it's less confusing. Initialise the WM8775 to invert the left-right clock to swap the left and right channels of the mic and aux input. Signed-off-by: Harry Butterworth Signed-off-by: Takashi Iwai --- include/linux/pci_ids.h | 1 + sound/pci/ctxfi/ct20k2reg.h | 1 + sound/pci/ctxfi/ctatc.c | 124 ++++++++++++----- sound/pci/ctxfi/ctatc.h | 8 ++ sound/pci/ctxfi/ctdaio.c | 23 +-- sound/pci/ctxfi/ctdaio.h | 1 + sound/pci/ctxfi/cthardware.h | 8 ++ sound/pci/ctxfi/cthw20k1.c | 18 +++ sound/pci/ctxfi/cthw20k2.c | 323 ++++++++++++++++++++++++++++++++----------- sound/pci/ctxfi/ctmixer.c | 141 ++++++++++++++----- sound/pci/ctxfi/xfi.c | 4 +- 11 files changed, 488 insertions(+), 164 deletions(-) (limited to 'include') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index a311008af5e1..f23f8bf02b04 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1308,6 +1308,7 @@ #define PCI_SUBDEVICE_ID_CREATIVE_SB08801 0x0041 #define PCI_SUBDEVICE_ID_CREATIVE_SB08802 0x0042 #define PCI_SUBDEVICE_ID_CREATIVE_SB08803 0x0043 +#define PCI_SUBDEVICE_ID_CREATIVE_SB1270 0x0062 #define PCI_SUBDEVICE_ID_CREATIVE_HENDRIX 0x6000 #define PCI_VENDOR_ID_ECTIVA 0x1102 /* duplicate: CREATIVE */ diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h index e0394e3996e8..ca501ba03d64 100644 --- a/sound/pci/ctxfi/ct20k2reg.h +++ b/sound/pci/ctxfi/ct20k2reg.h @@ -55,6 +55,7 @@ /* GPIO Registers */ #define GPIO_DATA 0x1B7020 #define GPIO_CTRL 0x1B7024 +#define GPIO_EXT_DATA 0x1B70A0 /* Virtual memory registers */ #define VMEM_PTPAL 0x1C6300 /* 0x1C6300 + (16 * Chn) */ diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 13f33c0719d3..952fd94c2666 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -30,7 +30,6 @@ #include #define MONO_SUM_SCALE 0x19a8 /* 2^(-0.5) in 14-bit floating format */ -#define DAIONUM 7 #define MAX_MULTI_CHN 8 #define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \ @@ -53,6 +52,8 @@ static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = { static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760, "SB0760", CTSB0760), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB1270, + "SB1270", CTSB1270), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801, "SB0880", CTSB0880), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802, @@ -75,6 +76,7 @@ static const char *ct_subsys_name[NUM_CTCARDS] = { [CTSB0760] = "SB076x", [CTHENDRIX] = "Hendrix", [CTSB0880] = "SB0880", + [CTSB1270] = "SB1270", [CT20K2_UNKNOWN] = "Unknown", }; @@ -459,12 +461,12 @@ static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm, apcm->substream->runtime->rate); *n_srcc = 0; - if (1 == atc->msr) { + if (1 == atc->msr) { /* FIXME: do we really need SRC here if pitch==1 */ *n_srcc = apcm->substream->runtime->channels; conf[0].pitch = pitch; conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1; conf[0].vo = 1; - } else if (2 == atc->msr) { + } else if (2 <= atc->msr) { if (0x8000000 < pitch) { /* Need two-stage SRCs, SRCIMPs and * AMIXERs for converting format */ @@ -977,6 +979,55 @@ static int atc_have_digit_io_switch(struct ct_atc *atc) return hw->have_digit_io_switch(hw); } +static int atc_have_dedicated_mic(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + + return hw->have_dedicated_mic(hw); +} + +static int atc_have_output_switch(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + + return hw->have_output_switch(hw); +} + +static int atc_output_switch_get(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + + return hw->output_switch_get(hw); +} + +static int atc_output_switch_put(struct ct_atc *atc, int position) +{ + struct hw *hw = atc->hw; + + return hw->output_switch_put(hw, position); +} + +static int atc_have_mic_source_switch(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + + return hw->have_mic_source_switch(hw); +} + +static int atc_mic_source_switch_get(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + + return hw->mic_source_switch_get(hw); +} + +static int atc_mic_source_switch_put(struct ct_atc *atc, int position) +{ + struct hw *hw = atc->hw; + + return hw->mic_source_switch_put(hw, position); +} + static int atc_select_digit_io(struct ct_atc *atc) { struct hw *hw = atc->hw; @@ -1045,6 +1096,11 @@ static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state) return atc_daio_unmute(atc, state, LINEIM); } +static int atc_mic_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, MIC); +} + static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state) { return atc_daio_unmute(atc, state, SPDIFOO); @@ -1331,17 +1387,20 @@ static int atc_get_resources(struct ct_atc *atc) struct srcimp_mgr *srcimp_mgr; struct sum_desc sum_dsc = {0}; struct sum_mgr *sum_mgr; - int err, i; + int err, i, num_srcs, num_daios; + + num_daios = ((atc->model == CTSB1270) ? 8 : 7); + num_srcs = ((atc->model == CTSB1270) ? 6 : 4); - atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); + atc->daios = kzalloc(sizeof(void *)*num_daios, GFP_KERNEL); if (!atc->daios) return -ENOMEM; - atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); + atc->srcs = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL); if (!atc->srcs) return -ENOMEM; - atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); + atc->srcimps = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL); if (!atc->srcimps) return -ENOMEM; @@ -1351,8 +1410,9 @@ static int atc_get_resources(struct ct_atc *atc) daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; da_desc.msr = atc->msr; - for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) { - da_desc.type = i; + for (i = 0, atc->n_daio = 0; i < num_daios; i++) { + da_desc.type = (atc->model != CTSB073X) ? i : + ((i == SPDIFIO) ? SPDIFI1 : i); err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); if (err) { @@ -1362,23 +1422,12 @@ static int atc_get_resources(struct ct_atc *atc) } atc->n_daio++; } - if (atc->model == CTSB073X) - da_desc.type = SPDIFI1; - else - da_desc.type = SPDIFIO; - err = daio_mgr->get_daio(daio_mgr, &da_desc, - (struct daio **)&atc->daios[i]); - if (err) { - printk(KERN_ERR "ctxfi: Failed to get S/PDIF-in resource!!!\n"); - return err; - } - atc->n_daio++; src_mgr = atc->rsc_mgrs[SRC]; src_dsc.multi = 1; src_dsc.msr = atc->msr; src_dsc.mode = ARCRW; - for (i = 0, atc->n_src = 0; i < (2*2); i++) { + for (i = 0, atc->n_src = 0; i < num_srcs; i++) { err = src_mgr->get_src(src_mgr, &src_dsc, (struct src **)&atc->srcs[i]); if (err) @@ -1388,8 +1437,8 @@ static int atc_get_resources(struct ct_atc *atc) } srcimp_mgr = atc->rsc_mgrs[SRCIMP]; - srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */ - for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) { + srcimp_dsc.msr = 8; + for (i = 0, atc->n_srcimp = 0; i < num_srcs; i++) { err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, (struct srcimp **)&atc->srcimps[i]); if (err) @@ -1397,15 +1446,6 @@ static int atc_get_resources(struct ct_atc *atc) atc->n_srcimp++; } - srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */ - for (i = 0; i < (2*1); i++) { - err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, - (struct srcimp **)&atc->srcimps[2*1+i]); - if (err) - return err; - - atc->n_srcimp++; - } sum_mgr = atc->rsc_mgrs[SUM]; sum_dsc.msr = atc->msr; @@ -1488,6 +1528,18 @@ static void atc_connect_resources(struct ct_atc *atc) src = atc->srcs[3]; mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc); + if (atc->model == CTSB1270) { + /* Titanium HD has a dedicated ADC for the Mic. */ + dai = container_of(atc->daios[MIC], struct dai, daio); + atc_connect_dai(atc->rsc_mgrs[SRC], dai, + (struct src **)&atc->srcs[4], + (struct srcimp **)&atc->srcimps[4]); + src = atc->srcs[4]; + mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc); + src = atc->srcs[5]; + mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc); + } + dai = container_of(atc->daios[SPDIFIO], struct dai, daio); atc_connect_dai(atc->rsc_mgrs[SRC], dai, (struct src **)&atc->srcs[0], @@ -1606,12 +1658,20 @@ static struct ct_atc atc_preset __devinitdata = { .line_clfe_unmute = atc_line_clfe_unmute, .line_rear_unmute = atc_line_rear_unmute, .line_in_unmute = atc_line_in_unmute, + .mic_unmute = atc_mic_unmute, .spdif_out_unmute = atc_spdif_out_unmute, .spdif_in_unmute = atc_spdif_in_unmute, .spdif_out_get_status = atc_spdif_out_get_status, .spdif_out_set_status = atc_spdif_out_set_status, .spdif_out_passthru = atc_spdif_out_passthru, .have_digit_io_switch = atc_have_digit_io_switch, + .have_dedicated_mic = atc_have_dedicated_mic, + .have_output_switch = atc_have_output_switch, + .output_switch_get = atc_output_switch_get, + .output_switch_put = atc_output_switch_put, + .have_mic_source_switch = atc_have_mic_source_switch, + .mic_source_switch_get = atc_mic_source_switch_get, + .mic_source_switch_put = atc_mic_source_switch_put, #ifdef CONFIG_PM .suspend = atc_suspend, .resume = atc_resume, diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index 7167c0185d52..6bad27e06f4d 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -115,12 +115,20 @@ struct ct_atc { int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state); int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state); int (*line_in_unmute)(struct ct_atc *atc, unsigned char state); + int (*mic_unmute)(struct ct_atc *atc, unsigned char state); int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state); int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state); int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status); int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status); int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state); int (*have_digit_io_switch)(struct ct_atc *atc); + int (*have_dedicated_mic)(struct ct_atc *atc); + int (*have_output_switch)(struct ct_atc *atc); + int (*output_switch_get)(struct ct_atc *atc); + int (*output_switch_put)(struct ct_atc *atc, int position); + int (*have_mic_source_switch)(struct ct_atc *atc); + int (*mic_source_switch_get)(struct ct_atc *atc); + int (*mic_source_switch_put)(struct ct_atc *atc, int position); /* Don't touch! Used for internal object. */ void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */ diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 47d9ea97de02..0c00eb4088ef 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -22,20 +22,9 @@ #include #include -#define DAIO_RESOURCE_NUM NUM_DAIOTYP #define DAIO_OUT_MAX SPDIFOO -union daio_usage { - struct { - unsigned short lineo1:1; - unsigned short lineo2:1; - unsigned short lineo3:1; - unsigned short lineo4:1; - unsigned short spdifoo:1; - unsigned short lineim:1; - unsigned short spdifio:1; - unsigned short spdifi1:1; - } bf; +struct daio_usage { unsigned short data; }; @@ -61,6 +50,7 @@ struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { [LINEO3] = {.left = 0x50, .right = 0x51}, [LINEO4] = {.left = 0x70, .right = 0x71}, [LINEIM] = {.left = 0x45, .right = 0xc5}, + [MIC] = {.left = 0x55, .right = 0xd5}, [SPDIFOO] = {.left = 0x00, .right = 0x01}, [SPDIFIO] = {.left = 0x05, .right = 0x85}, }; @@ -138,6 +128,7 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) case LINEO3: return 5; case LINEO4: return 6; case LINEIM: return 4; + case MIC: return 5; default: return -EINVAL; } default: @@ -519,17 +510,17 @@ static int dai_rsc_uninit(struct dai *dai) static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type) { - if (((union daio_usage *)mgr->rscs)->data & (0x1 << type)) + if (((struct daio_usage *)mgr->rscs)->data & (0x1 << type)) return -ENOENT; - ((union daio_usage *)mgr->rscs)->data |= (0x1 << type); + ((struct daio_usage *)mgr->rscs)->data |= (0x1 << type); return 0; } static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type) { - ((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type); + ((struct daio_usage *)mgr->rscs)->data &= ~(0x1 << type); return 0; } @@ -712,7 +703,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) if (!daio_mgr) return -ENOMEM; - err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw); + err = rsc_mgr_init(&daio_mgr->mgr, DAIO, NUM_DAIOTYP, hw); if (err) goto error1; diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h index 0f52ce571ee8..85ccb6ee1ab4 100644 --- a/sound/pci/ctxfi/ctdaio.h +++ b/sound/pci/ctxfi/ctdaio.h @@ -33,6 +33,7 @@ enum DAIOTYP { SPDIFOO, /* S/PDIF Out (Flexijack/Optical) */ LINEIM, SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */ + MIC, /* Dedicated mic on Titanium HD */ SPDIFI1, /* S/PDIF In on internal Drive Bay */ NUM_DAIOTYP }; diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index af55405f5dec..de59bbf2702f 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -39,6 +39,7 @@ enum CTCARDS { CT20K2_MODEL_FIRST = CTSB0760, CTHENDRIX, CTSB0880, + CTSB1270, CT20K2_UNKNOWN, NUM_CTCARDS /* This should always be the last */ }; @@ -71,6 +72,13 @@ struct hw { int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source); int (*select_adc_source)(struct hw *hw, enum ADCSRC source); int (*have_digit_io_switch)(struct hw *hw); + int (*have_dedicated_mic)(struct hw *hw); + int (*have_output_switch)(struct hw *hw); + int (*output_switch_get)(struct hw *hw); + int (*output_switch_put)(struct hw *hw, int position); + int (*have_mic_source_switch)(struct hw *hw); + int (*mic_source_switch_get)(struct hw *hw); + int (*mic_source_switch_put)(struct hw *hw, int position); /* SRC operations */ int (*src_rsc_get_ctrl_blk)(void **rblk); diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index a5c957db5cea..9a85a84b23ab 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1783,6 +1783,21 @@ static int hw_have_digit_io_switch(struct hw *hw) return !(hw->model == CTSB073X || hw->model == CTUAA); } +static int hw_have_dedicated_mic(struct hw *hw) +{ + return 0; +} + +static int hw_have_output_switch(struct hw *hw) +{ + return 0; +} + +static int hw_have_mic_source_switch(struct hw *hw) +{ + return 0; +} + #define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) #define UAA_CFG_PWRSTATUS 0x44 @@ -2173,6 +2188,9 @@ static struct hw ct20k1_preset __devinitdata = { .is_adc_source_selected = hw_is_adc_input_selected, .select_adc_source = hw_adc_input_select, .have_digit_io_switch = hw_have_digit_io_switch, + .have_dedicated_mic = hw_have_dedicated_mic, + .have_output_switch = hw_have_output_switch, + .have_mic_source_switch = hw_have_mic_source_switch, #ifdef CONFIG_PM .suspend = hw_suspend, .resume = hw_resume, diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 5364164674e4..8bc6e41ce64b 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -8,7 +8,7 @@ * @File cthw20k2.c * * @Brief - * This file contains the implementation of hardware access methord for 20k2. + * This file contains the implementation of hardware access method for 20k2. * * @Author Liu Chun * @Date May 14 2008 @@ -38,6 +38,8 @@ struct hw20k2 { unsigned char dev_id; unsigned char addr_size; unsigned char data_size; + + int mic_source; }; static u32 hw_read_20kx(struct hw *hw, u32 reg); @@ -1163,7 +1165,12 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101); hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); } else if (2 == info->msr) { - hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111); + if (hw->model != CTSB1270) { + hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111); + } else { + /* PCM4220 on Titanium HD is different. */ + hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11011111); + } /* Specify all playing 96khz * EA [0] - Enabled * RTA [4:5] - 96kHz @@ -1175,6 +1182,10 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) * RTD [28:29] - 96kHz */ hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111); hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); + } else if ((4 == info->msr) && (hw->model == CTSB1270)) { + hw_write_20kx(hw, AUDIO_IO_MCLK, 0x21011111); + hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121); + hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); } else { printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n"); return -EINVAL; @@ -1182,6 +1193,8 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) for (i = 0; i < 8; i++) { if (i <= 3) { + /* This comment looks wrong since loop is over 4 */ + /* channels and emu20k2 supports 4 spdif IOs. */ /* 1st 3 channels are SPDIFs (SB0960) */ if (i == 3) data = 0x1001001; @@ -1206,12 +1219,16 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B); } else { + /* Again, loop is over 4 channels not 5. */ /* Next 5 channels are I2S (SB0960) */ data = 0x11; hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data); if (2 == info->msr) { /* Four channels per sample period */ data |= 0x1000; + } else if (4 == info->msr) { + /* FIXME: check this against the chip spec */ + data |= 0x2000; } hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), data); } @@ -1557,7 +1574,7 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data) hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); hw20k2_i2c_wait_data_ready(hw); - /* Dummy write to trigger the write oprtation */ + /* Dummy write to trigger the write operation */ hw_write_20kx(hw, I2C_IF_WDATA, 0); hw20k2_i2c_wait_data_ready(hw); @@ -1568,6 +1585,30 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data) return 0; } +static void hw_dac_stop(struct hw *hw) +{ + u32 data; + data = hw_read_20kx(hw, GPIO_DATA); + data &= 0xFFFFFFFD; + hw_write_20kx(hw, GPIO_DATA, data); + mdelay(10); +} + +static void hw_dac_start(struct hw *hw) +{ + u32 data; + data = hw_read_20kx(hw, GPIO_DATA); + data |= 0x2; + hw_write_20kx(hw, GPIO_DATA, data); + mdelay(50); +} + +static void hw_dac_reset(struct hw *hw) +{ + hw_dac_stop(hw); + hw_dac_start(hw); +} + static int hw_dac_init(struct hw *hw, const struct dac_conf *info) { int err; @@ -1594,6 +1635,21 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) 0x00000000 /* Vol Control B4 */ }; + if (hw->model == CTSB1270) { + hw_dac_stop(hw); + data = hw_read_20kx(hw, GPIO_DATA); + data &= ~0x0600; + if (1 == info->msr) + data |= 0x0000; /* Single Speed Mode 0-50kHz */ + else if (2 == info->msr) + data |= 0x0200; /* Double Speed Mode 50-100kHz */ + else + data |= 0x0600; /* Quad Speed Mode 100-200kHz */ + hw_write_20kx(hw, GPIO_DATA, data); + hw_dac_start(hw); + return 0; + } + /* Set DAC reset bit as output */ data = hw_read_20kx(hw, GPIO_CTRL); data |= 0x02; @@ -1606,22 +1662,8 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) for (i = 0; i < 2; i++) { /* Reset DAC twice just in-case the chip * didn't initialized properly */ - data = hw_read_20kx(hw, GPIO_DATA); - /* GPIO data bit 1 */ - data &= 0xFFFFFFFD; - hw_write_20kx(hw, GPIO_DATA, data); - mdelay(10); - data |= 0x2; - hw_write_20kx(hw, GPIO_DATA, data); - mdelay(50); - - /* Reset the 2nd time */ - data &= 0xFFFFFFFD; - hw_write_20kx(hw, GPIO_DATA, data); - mdelay(10); - data |= 0x2; - hw_write_20kx(hw, GPIO_DATA, data); - mdelay(50); + hw_dac_reset(hw); + hw_dac_reset(hw); if (hw20k2_i2c_read(hw, CS4382_MC1, &cs_read.mode_control_1)) continue; @@ -1725,7 +1767,11 @@ End: static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) { u32 data; - + if (hw->model == CTSB1270) { + /* Titanium HD has two ADC chips, one for line in and one */ + /* for MIC. We don't need to switch the ADC input. */ + return 1; + } data = hw_read_20kx(hw, GPIO_DATA); switch (type) { case ADC_MICIN: @@ -1742,35 +1788,47 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) #define MIC_BOOST_0DB 0xCF #define MIC_BOOST_STEPS_PER_DB 2 -#define MIC_BOOST_20DB (MIC_BOOST_0DB + 20 * MIC_BOOST_STEPS_PER_DB) + +static void hw_wm8775_input_select(struct hw *hw, u8 input, s8 gain_in_db) +{ + u32 adcmc, gain; + + if (input > 3) + input = 3; + + adcmc = ((u32)1 << input) | 0x100; /* Link L+R gain... */ + + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, adcmc), + MAKE_WM8775_DATA(adcmc)); + + if (gain_in_db < -103) + gain_in_db = -103; + if (gain_in_db > 24) + gain_in_db = 24; + + gain = gain_in_db * MIC_BOOST_STEPS_PER_DB + MIC_BOOST_0DB; + + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, gain), + MAKE_WM8775_DATA(gain)); + /* ...so there should be no need for the following. */ + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, gain), + MAKE_WM8775_DATA(gain)); +} static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) { u32 data; - data = hw_read_20kx(hw, GPIO_DATA); switch (type) { case ADC_MICIN: data |= (0x1 << 14); hw_write_20kx(hw, GPIO_DATA, data); - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), - MAKE_WM8775_DATA(0x101)); /* Mic-in */ - hw20k2_i2c_write(hw, - MAKE_WM8775_ADDR(WM8775_AADCL, MIC_BOOST_20DB), - MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */ - hw20k2_i2c_write(hw, - MAKE_WM8775_ADDR(WM8775_AADCR, MIC_BOOST_20DB), - MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */ + hw_wm8775_input_select(hw, 0, 20); /* Mic, 20dB */ break; case ADC_LINEIN: data &= ~(0x1 << 14); hw_write_20kx(hw, GPIO_DATA, data); - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), - MAKE_WM8775_DATA(0x102)); /* Line-in */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), - MAKE_WM8775_DATA(0xCF)); /* No boost */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), - MAKE_WM8775_DATA(0xCF)); /* No boost */ + hw_wm8775_input_select(hw, 1, 0); /* Line-in, 0dB */ break; default: break; @@ -1782,7 +1840,7 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) static int hw_adc_init(struct hw *hw, const struct adc_conf *info) { int err; - u32 mux = 2, data, ctl; + u32 data, ctl; /* Set ADC reset bit as output */ data = hw_read_20kx(hw, GPIO_CTRL); @@ -1796,19 +1854,42 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) goto error; } - /* Make ADC in normal operation */ + /* Reset the ADC (reset is active low). */ data = hw_read_20kx(hw, GPIO_DATA); data &= ~(0x1 << 15); + hw_write_20kx(hw, GPIO_DATA, data); + + if (hw->model == CTSB1270) { + /* Set up the PCM4220 ADC on Titanium HD */ + data &= ~0x0C; + if (1 == info->msr) + data |= 0x00; /* Single Speed Mode 32-50kHz */ + else if (2 == info->msr) + data |= 0x08; /* Double Speed Mode 50-108kHz */ + else + data |= 0x04; /* Quad Speed Mode 108kHz-216kHz */ + hw_write_20kx(hw, GPIO_DATA, data); + } + mdelay(10); + /* Return the ADC to normal operation. */ data |= (0x1 << 15); hw_write_20kx(hw, GPIO_DATA, data); mdelay(50); + /* I2C write to register offset 0x0B to set ADC LRCLK polarity */ + /* invert bit, interface format to I2S, word length to 24-bit, */ + /* enable ADC high pass filter. Fixes bug 5323? */ + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_IC, 0x26), + MAKE_WM8775_DATA(0x26)); + /* Set the master mode (256fs) */ if (1 == info->msr) { + /* slave mode, 128x oversampling 256fs */ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02), MAKE_WM8775_DATA(0x02)); - } else if (2 == info->msr) { + } else if ((2 == info->msr) || (4 == info->msr)) { + /* slave mode, 64x oversampling, 256fs */ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), MAKE_WM8775_DATA(0x0A)); } else { @@ -1818,47 +1899,17 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) goto error; } - /* Configure GPIO bit 14 change to line-in/mic-in */ - ctl = hw_read_20kx(hw, GPIO_CTRL); - ctl |= 0x1 << 14; - hw_write_20kx(hw, GPIO_CTRL, ctl); - - /* Check using Mic-in or Line-in */ - data = hw_read_20kx(hw, GPIO_DATA); - - if (mux == 1) { - /* Configures GPIO data to select Mic-in */ - data |= 0x1 << 14; - hw_write_20kx(hw, GPIO_DATA, data); - - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), - MAKE_WM8775_DATA(0x101)); /* Mic-in */ - hw20k2_i2c_write(hw, - MAKE_WM8775_ADDR(WM8775_AADCL, MIC_BOOST_20DB), - MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */ - hw20k2_i2c_write(hw, - MAKE_WM8775_ADDR(WM8775_AADCR, MIC_BOOST_20DB), - MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */ - } else if (mux == 2) { - /* Configures GPIO data to select Line-in */ - data &= ~(0x1 << 14); - hw_write_20kx(hw, GPIO_DATA, data); - - /* Setup ADC */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), - MAKE_WM8775_DATA(0x102)); /* Line-in */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), - MAKE_WM8775_DATA(0xCF)); /* No boost */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), - MAKE_WM8775_DATA(0xCF)); /* No boost */ + if (hw->model != CTSB1270) { + /* Configure GPIO bit 14 change to line-in/mic-in */ + ctl = hw_read_20kx(hw, GPIO_CTRL); + ctl |= 0x1 << 14; + hw_write_20kx(hw, GPIO_CTRL, ctl); + hw_adc_input_select(hw, ADC_LINEIN); } else { - printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n"); - err = -EINVAL; - goto error; + hw_wm8775_input_select(hw, 0, 0); } return 0; - error: hw20k2_i2c_uninit(hw); return err; @@ -1869,6 +1920,102 @@ static int hw_have_digit_io_switch(struct hw *hw) return 0; } +static int hw_have_dedicated_mic(struct hw *hw) +{ + return hw->model == CTSB1270; +} + +static int hw_have_output_switch(struct hw *hw) +{ + return hw->model == CTSB1270; +} + +static int hw_output_switch_get(struct hw *hw) +{ + u32 data = hw_read_20kx(hw, GPIO_EXT_DATA); + + switch (data & 0x30) { + case 0x00: + return 0; + case 0x10: + return 1; + case 0x20: + return 2; + default: + return 3; + } +} + +static int hw_output_switch_put(struct hw *hw, int position) +{ + u32 data; + + if (position == hw_output_switch_get(hw)) + return 0; + + /* Mute line and headphones (intended for anti-pop). */ + data = hw_read_20kx(hw, GPIO_DATA); + data |= (0x03 << 11); + hw_write_20kx(hw, GPIO_DATA, data); + + data = hw_read_20kx(hw, GPIO_EXT_DATA) & ~0x30; + switch (position) { + case 0: + break; + case 1: + data |= 0x10; + break; + default: + data |= 0x20; + } + hw_write_20kx(hw, GPIO_EXT_DATA, data); + + /* Unmute line and headphones. */ + data = hw_read_20kx(hw, GPIO_DATA); + data &= ~(0x03 << 11); + hw_write_20kx(hw, GPIO_DATA, data); + + return 1; +} + +static int hw_have_mic_source_switch(struct hw *hw) +{ + return hw->model == CTSB1270; +} + +static int hw_mic_source_switch_get(struct hw *hw) +{ + struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; + + return hw20k2->mic_source; +} + +static int hw_mic_source_switch_put(struct hw *hw, int position) +{ + struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; + + if (position == hw20k2->mic_source) + return 0; + + switch (position) { + case 0: + hw_wm8775_input_select(hw, 0, 0); /* Mic, 0dB */ + break; + case 1: + hw_wm8775_input_select(hw, 1, 0); /* FP Mic, 0dB */ + break; + case 2: + hw_wm8775_input_select(hw, 3, 0); /* Aux Ext, 0dB */ + break; + default: + return 0; + } + + hw20k2->mic_source = position; + + return 1; +} + static irqreturn_t ct_20k2_interrupt(int irq, void *dev_id) { struct hw *hw = dev_id; @@ -2023,13 +2170,16 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) /* Reset all SRC pending interrupts */ hw_write_20kx(hw, SRC_IP, 0); - /* TODO: detect the card ID and configure GPIO accordingly. */ - /* Configures GPIO (0xD802 0x98028) */ - /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/ - /* Configures GPIO (SB0880) */ - /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/ - hw_write_20kx(hw, GPIO_CTRL, 0xD802); - + if (hw->model != CTSB1270) { + /* TODO: detect the card ID and configure GPIO accordingly. */ + /* Configures GPIO (0xD802 0x98028) */ + /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/ + /* Configures GPIO (SB0880) */ + /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/ + hw_write_20kx(hw, GPIO_CTRL, 0xD802); + } else { + hw_write_20kx(hw, GPIO_CTRL, 0x9E5F); + } /* Enable audio ring */ hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01); @@ -2107,6 +2257,13 @@ static struct hw ct20k2_preset __devinitdata = { .is_adc_source_selected = hw_is_adc_input_selected, .select_adc_source = hw_adc_input_select, .have_digit_io_switch = hw_have_digit_io_switch, + .have_dedicated_mic = hw_have_dedicated_mic, + .have_output_switch = hw_have_output_switch, + .output_switch_get = hw_output_switch_get, + .output_switch_put = hw_output_switch_put, + .have_mic_source_switch = hw_have_mic_source_switch, + .mic_source_switch_get = hw_mic_source_switch_get, + .mic_source_switch_put = hw_mic_source_switch_put, #ifdef CONFIG_PM .suspend = hw_suspend, .resume = hw_resume, diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index c3519ff42fbb..388235c43789 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -86,9 +86,7 @@ enum CTALSA_MIXER_CTL { MIXER_LINEIN_C_S, MIXER_MIC_C_S, MIXER_SPDIFI_C_S, - MIXER_LINEIN_P_S, MIXER_SPDIFO_P_S, - MIXER_SPDIFI_P_S, MIXER_WAVEF_P_S, MIXER_WAVER_P_S, MIXER_WAVEC_P_S, @@ -137,11 +135,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_LINEIN_P] = { .ctl = 1, - .name = "Line-in Playback Volume", + .name = "Line Playback Volume", }, [MIXER_LINEIN_C] = { .ctl = 1, - .name = "Line-in Capture Volume", + .name = "Line Capture Volume", }, [MIXER_MIC_P] = { .ctl = 1, @@ -153,15 +151,15 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_SPDIFI_P] = { .ctl = 1, - .name = "S/PDIF-in Playback Volume", + .name = "IEC958 Playback Volume", }, [MIXER_SPDIFI_C] = { .ctl = 1, - .name = "S/PDIF-in Capture Volume", + .name = "IEC958 Capture Volume", }, [MIXER_SPDIFO_P] = { .ctl = 1, - .name = "S/PDIF-out Playback Volume", + .name = "Digital Playback Volume", }, [MIXER_WAVEF_P] = { .ctl = 1, @@ -179,14 +177,13 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { .ctl = 1, .name = "Surround Playback Volume", }, - [MIXER_PCM_C_S] = { .ctl = 1, .name = "PCM Capture Switch", }, [MIXER_LINEIN_C_S] = { .ctl = 1, - .name = "Line-in Capture Switch", + .name = "Line Capture Switch", }, [MIXER_MIC_C_S] = { .ctl = 1, @@ -194,19 +191,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_SPDIFI_C_S] = { .ctl = 1, - .name = "S/PDIF-in Capture Switch", - }, - [MIXER_LINEIN_P_S] = { - .ctl = 1, - .name = "Line-in Playback Switch", + .name = "IEC958 Capture Switch", }, [MIXER_SPDIFO_P_S] = { .ctl = 1, - .name = "S/PDIF-out Playback Switch", - }, - [MIXER_SPDIFI_P_S] = { - .ctl = 1, - .name = "S/PDIF-in Playback Switch", + .name = "Digital Playback Switch", }, [MIXER_WAVEF_P_S] = { .ctl = 1, @@ -236,6 +225,8 @@ ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); static void ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); +/* FIXME: this static looks like it would fail if more than one card was */ +/* installed. */ static struct snd_kcontrol *kctls[2] = {NULL}; static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index) @@ -420,6 +411,77 @@ static struct snd_kcontrol_new vol_ctl = { .tlv = { .p = ct_vol_db_scale }, }; +static int output_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "FP Headphones", "Headphones", "Speakers" + }; + + return snd_ctl_enum_info(info, 1, 3, names); +} + +static int output_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = atc->output_switch_get(atc); + return 0; +} + +static int output_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.enumerated.item[0] > 2) + return -EINVAL; + return atc->output_switch_put(atc, ucontrol->value.enumerated.item[0]); +} + +static struct snd_kcontrol_new output_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Output Playback Enum", + .info = output_switch_info, + .get = output_switch_get, + .put = output_switch_put, +}; + +static int mic_source_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "Mic", "FP Mic", "Aux" + }; + + return snd_ctl_enum_info(info, 1, 3, names); +} + +static int mic_source_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = atc->mic_source_switch_get(atc); + return 0; +} + +static int mic_source_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.enumerated.item[0] > 2) + return -EINVAL; + return atc->mic_source_switch_put(atc, + ucontrol->value.enumerated.item[0]); +} + +static struct snd_kcontrol_new mic_source_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Source Capture Enum", + .info = mic_source_switch_info, + .get = mic_source_switch_get, + .put = mic_source_switch_put, +}; + static void do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type) { @@ -465,6 +527,7 @@ do_digit_io_switch(struct ct_atc *atc, int state) static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state) { struct ct_mixer *mixer = atc->mixer; + int have_dedicated_mic = atc->have_dedicated_mic(atc); /* Do changes in mixer. */ if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) { @@ -477,8 +540,17 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state) } } /* Do changes out of mixer. */ - if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) - do_line_mic_switch(atc, type); + if (!have_dedicated_mic && + (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) { + if (state) + do_line_mic_switch(atc, type); + atc->line_in_unmute(atc, state); + } else if (have_dedicated_mic && (MIXER_LINEIN_C_S == type)) + atc->line_in_unmute(atc, state); + else if (have_dedicated_mic && (MIXER_MIC_C_S == type)) + atc->mic_unmute(atc, state); + else if (MIXER_SPDIFI_C_S == type) + atc->spdif_in_unmute(atc, state); else if (MIXER_WAVEF_P_S == type) atc->line_front_unmute(atc, state); else if (MIXER_WAVES_P_S == type) @@ -487,12 +559,8 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state) atc->line_clfe_unmute(atc, state); else if (MIXER_WAVER_P_S == type) atc->line_rear_unmute(atc, state); - else if (MIXER_LINEIN_P_S == type) - atc->line_in_unmute(atc, state); else if (MIXER_SPDIFO_P_S == type) atc->spdif_out_unmute(atc, state); - else if (MIXER_SPDIFI_P_S == type) - atc->spdif_in_unmute(atc, state); else if (MIXER_DIGITAL_IO_S == type) do_digit_io_switch(atc, state); @@ -686,6 +754,7 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl = atc->have_digit_io_switch(atc); + for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) { if (ct_kcontrol_init_table[type].ctl) { swh_ctl.name = ct_kcontrol_init_table[type].name; @@ -708,6 +777,17 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) if (err) return err; + if (atc->have_output_switch(atc)) { + err = ct_mixer_kcontrol_new(mixer, &output_ctl); + if (err) + return err; + } + + if (atc->have_mic_source_switch(atc)) { + err = ct_mixer_kcontrol_new(mixer, &mic_source_ctl); + if (err) + return err; + } atc->line_front_unmute(atc, 1); set_switch_state(mixer, MIXER_WAVEF_P_S, 1); atc->line_surround_unmute(atc, 0); @@ -719,13 +799,12 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) atc->spdif_out_unmute(atc, 0); set_switch_state(mixer, MIXER_SPDIFO_P_S, 0); atc->line_in_unmute(atc, 0); - set_switch_state(mixer, MIXER_LINEIN_P_S, 0); + if (atc->have_dedicated_mic(atc)) + atc->mic_unmute(atc, 0); atc->spdif_in_unmute(atc, 0); - set_switch_state(mixer, MIXER_SPDIFI_P_S, 0); - - set_switch_state(mixer, MIXER_PCM_C_S, 1); - set_switch_state(mixer, MIXER_LINEIN_C_S, 1); - set_switch_state(mixer, MIXER_SPDIFI_C_S, 1); + set_switch_state(mixer, MIXER_PCM_C_S, 0); + set_switch_state(mixer, MIXER_LINEIN_C_S, 0); + set_switch_state(mixer, MIXER_SPDIFI_C_S, 0); return 0; } diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index f42e7e1a1074..7afa765afa3c 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -80,11 +80,11 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) "are 48000 and 44100, Value 48000 is assumed.\n"); reference_rate = 48000; } - if ((multiple != 1) && (multiple != 2)) { + if ((multiple != 1) && (multiple != 2) && (multiple != 4)) { printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n", multiple); printk(KERN_ERR "ctxfi: The valid values for multiple are " - "1 and 2, Value 2 is assumed.\n"); + "1, 2 and 4, Value 2 is assumed.\n"); multiple = 2; } err = ct_atc_create(card, pci, reference_rate, multiple, -- cgit v1.2.3 From 74315cccd2104a953f493acca2c6b0519d6f5c6f Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Wed, 8 Jun 2011 17:29:11 -0400 Subject: iommu-api: Add missing header file If CONFIG_IOMMU_API is not defined some functions will just return -ENODEV. Add errno.h for the definition of ENODEV. Signed-off-by: Laura Abbott Signed-off-by: Joerg Roedel --- include/linux/iommu.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 0a2ba4098996..9940319d6f9d 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -19,6 +19,8 @@ #ifndef __LINUX_IOMMU_H #define __LINUX_IOMMU_H +#include + #define IOMMU_READ (1) #define IOMMU_WRITE (2) #define IOMMU_CACHE (4) /* DMA cache coherency */ -- cgit v1.2.3 From b3c705aa9e9147a30009bdf6ba853aa7fe138e58 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 14 Jun 2011 14:37:06 +0200 Subject: ALSA: rawmidi - Use workq for event handling Kill tasklet usage in rawmidi core code. Use workq for the event callback instead of tasklet (which is used only in core/seq/seq_midi.c). Signed-off-by: Takashi Iwai --- include/sound/rawmidi.h | 4 +++- sound/core/rawmidi.c | 37 +++++++++++-------------------------- 2 files changed, 14 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h index 2480e7d10dcf..6b14359d9fed 100644 --- a/include/sound/rawmidi.h +++ b/include/sound/rawmidi.h @@ -27,6 +27,7 @@ #include #include #include +#include #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) #include "seq_device.h" @@ -63,6 +64,7 @@ struct snd_rawmidi_global_ops { }; struct snd_rawmidi_runtime { + struct snd_rawmidi_substream *substream; unsigned int drain: 1, /* drain stage */ oss: 1; /* OSS compatible mode */ /* midi stream buffer */ @@ -79,7 +81,7 @@ struct snd_rawmidi_runtime { /* event handler (new bytes, input only) */ void (*event)(struct snd_rawmidi_substream *substream); /* defers calls to event [input] or ops->trigger [output] */ - struct tasklet_struct tasklet; + struct work_struct event_work; /* private data */ void *private_data; void (*private_free)(struct snd_rawmidi_substream *substream); diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index cbbed0db9e56..0757f542999d 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -92,16 +92,12 @@ static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substre (!substream->append || runtime->avail >= count); } -static void snd_rawmidi_input_event_tasklet(unsigned long data) +static void snd_rawmidi_input_event_work(struct work_struct *work) { - struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data; - substream->runtime->event(substream); -} - -static void snd_rawmidi_output_trigger_tasklet(unsigned long data) -{ - struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data; - substream->ops->trigger(substream, 1); + struct snd_rawmidi_runtime *runtime = + container_of(work, struct snd_rawmidi_runtime, event_work); + if (runtime->event) + runtime->event(runtime->substream); } static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) @@ -110,16 +106,10 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) if ((runtime = kzalloc(sizeof(*runtime), GFP_KERNEL)) == NULL) return -ENOMEM; + runtime->substream = substream; spin_lock_init(&runtime->lock); init_waitqueue_head(&runtime->sleep); - if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT) - tasklet_init(&runtime->tasklet, - snd_rawmidi_input_event_tasklet, - (unsigned long)substream); - else - tasklet_init(&runtime->tasklet, - snd_rawmidi_output_trigger_tasklet, - (unsigned long)substream); + INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work); runtime->event = NULL; runtime->buffer_size = PAGE_SIZE; runtime->avail_min = 1; @@ -150,12 +140,7 @@ static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *subs { if (!substream->opened) return; - if (up) { - tasklet_schedule(&substream->runtime->tasklet); - } else { - tasklet_kill(&substream->runtime->tasklet); - substream->ops->trigger(substream, 0); - } + substream->ops->trigger(substream, up); } static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, int up) @@ -163,8 +148,8 @@ static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, i if (!substream->opened) return; substream->ops->trigger(substream, up); - if (!up && substream->runtime->event) - tasklet_kill(&substream->runtime->tasklet); + if (!up) + cancel_work_sync(&substream->runtime->event_work); } int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream) @@ -926,7 +911,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, } if (result > 0) { if (runtime->event) - tasklet_schedule(&runtime->tasklet); + schedule_work(&runtime->event_work); else if (snd_rawmidi_ready(substream)) wake_up(&runtime->sleep); } -- cgit v1.2.3 From 5d3de7df18077a0f508ae2c3e3f1866da65fdffd Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Tue, 14 Jun 2011 13:37:41 -0300 Subject: Bluetooth: Add support for SMP timeout This patch adds support for disconnecting the link when SMP procedure takes more than 30 seconds. SMP begins when either the Pairing Request command is sent or the Pairing Response is received, and it ends when the link is encrypted (or terminated). Vol 3, Part H Section 3.4. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 ++ net/bluetooth/l2cap_core.c | 74 +++++++++++++++++++++++++------------------ net/bluetooth/smp.c | 14 ++++++++ 3 files changed, 60 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 01c993b6b263..157419afe532 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -401,6 +401,8 @@ struct l2cap_conn { __u8 pcnf[16]; /* SMP Pairing Confirm */ __u8 tk[16]; /* SMP Temporary Key */ + struct timer_list security_timer; + struct list_head chan_l; rwlock_t chan_lock; }; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 584a4237eb3f..bbbae2e0aa84 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -970,6 +970,45 @@ static void l2cap_info_timeout(unsigned long arg) l2cap_conn_start(conn); } +static void l2cap_conn_del(struct hci_conn *hcon, int err) +{ + struct l2cap_conn *conn = hcon->l2cap_data; + struct l2cap_chan *chan, *l; + struct sock *sk; + + if (!conn) + return; + + BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); + + kfree_skb(conn->rx_skb); + + /* Kill channels */ + list_for_each_entry_safe(chan, l, &conn->chan_l, list) { + sk = chan->sk; + bh_lock_sock(sk); + l2cap_chan_del(chan, err); + bh_unlock_sock(sk); + chan->ops->close(chan->data); + } + + if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) + del_timer_sync(&conn->info_timer); + + if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) + del_timer(&conn->security_timer); + + hcon->l2cap_data = NULL; + kfree(conn); +} + +static void security_timeout(unsigned long arg) +{ + struct l2cap_conn *conn = (void *) arg; + + l2cap_conn_del(conn->hcon, ETIMEDOUT); +} + static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) { struct l2cap_conn *conn = hcon->l2cap_data; @@ -1001,7 +1040,10 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) INIT_LIST_HEAD(&conn->chan_l); - if (hcon->type != LE_LINK) + if (hcon->type == LE_LINK) + setup_timer(&conn->security_timer, security_timeout, + (unsigned long) conn); + else setup_timer(&conn->info_timer, l2cap_info_timeout, (unsigned long) conn); @@ -1010,35 +1052,6 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) return conn; } -static void l2cap_conn_del(struct hci_conn *hcon, int err) -{ - struct l2cap_conn *conn = hcon->l2cap_data; - struct l2cap_chan *chan, *l; - struct sock *sk; - - if (!conn) - return; - - BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); - - kfree_skb(conn->rx_skb); - - /* Kill channels */ - list_for_each_entry_safe(chan, l, &conn->chan_l, list) { - sk = chan->sk; - bh_lock_sock(sk); - l2cap_chan_del(chan, err); - bh_unlock_sock(sk); - chan->ops->close(chan->data); - } - - if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) - del_timer_sync(&conn->info_timer); - - hcon->l2cap_data = NULL; - kfree(conn); -} - static inline void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { write_lock_bh(&conn->chan_lock); @@ -4182,6 +4195,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (chan->scid == L2CAP_CID_LE_DATA) { if (!status && encrypt) { chan->sec_level = hcon->sec_level; + del_timer(&conn->security_timer); l2cap_chan_ready(sk); } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index dfd6891b9c86..39886786eb7f 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -27,6 +27,8 @@ #include #include +#define SMP_TIMEOUT 30000 /* 30 seconds */ + static inline void swap128(u8 src[16], u8 dst[16]) { int i; @@ -228,6 +230,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp); + mod_timer(&conn->security_timer, jiffies + + msecs_to_jiffies(SMP_TIMEOUT)); + return 0; } @@ -303,6 +308,9 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); } + mod_timer(&conn->security_timer, jiffies + + msecs_to_jiffies(SMP_TIMEOUT)); + return 0; } @@ -382,6 +390,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); + mod_timer(&conn->security_timer, jiffies + + msecs_to_jiffies(SMP_TIMEOUT)); + set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); return 0; @@ -415,6 +426,9 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) conn->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&conn->preq[1], &cp, sizeof(cp)); + mod_timer(&conn->security_timer, jiffies + + msecs_to_jiffies(SMP_TIMEOUT)); + smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); } else { struct smp_cmd_security_req cp; -- cgit v1.2.3 From 3158c50c33c1acddcfa3c57fab812435aa459750 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Tue, 14 Jun 2011 13:37:42 -0300 Subject: Bluetooth: Add key size checks for SMP This patch implements a check in smp cmd pairing request and pairing response to verify if encryption key maximum size is compatible in both slave and master when SMP Pairing is requested. Keys are also masked to the correct negotiated size. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Anderson Briglia Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 1 + include/net/bluetooth/smp.h | 3 +++ net/bluetooth/smp.c | 54 ++++++++++++++++++++++++++++++++----------- 3 files changed, 45 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 157419afe532..bf1c7f681932 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -400,6 +400,7 @@ struct l2cap_conn { __u8 prnd[16]; /* SMP Pairing Random */ __u8 pcnf[16]; /* SMP Pairing Confirm */ __u8 tk[16]; /* SMP Temporary Key */ + __u8 smp_key_size; struct timer_list security_timer; diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index 111853ab239a..4fb7d198a876 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -112,6 +112,9 @@ struct smp_cmd_security_req { #define SMP_UNSPECIFIED 0x08 #define SMP_REPEATED_ATTEMPTS 0x09 +#define SMP_MIN_ENC_KEY_SIZE 7 +#define SMP_MAX_ENC_KEY_SIZE 16 + /* SMP Commands */ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level); int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 39886786eb7f..52e9ec2644c1 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -200,35 +200,51 @@ static void build_pairing_cmd(struct l2cap_conn *conn, { cmd->io_capability = conn->hcon->io_capability; cmd->oob_flag = SMP_OOB_NOT_PRESENT; - cmd->max_key_size = 16; + cmd->max_key_size = SMP_MAX_ENC_KEY_SIZE; cmd->init_key_dist = 0x00; cmd->resp_key_dist = 0x00; cmd->auth_req = authreq; } +static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) +{ + if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) || + (max_key_size < SMP_MIN_ENC_KEY_SIZE)) + return SMP_ENC_KEY_SIZE; + + conn->smp_key_size = max_key_size; + + return 0; +} + static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { - struct smp_cmd_pairing *rp = (void *) skb->data; + struct smp_cmd_pairing rsp, *req = (void *) skb->data; + u8 key_size; BT_DBG("conn %p", conn); conn->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&conn->preq[1], rp, sizeof(*rp)); - skb_pull(skb, sizeof(*rp)); + memcpy(&conn->preq[1], req, sizeof(*req)); + skb_pull(skb, sizeof(*req)); - if (rp->oob_flag) + if (req->oob_flag) return SMP_OOB_NOT_AVAIL; /* We didn't start the pairing, so no requirements */ - build_pairing_cmd(conn, rp, SMP_AUTH_NONE); + build_pairing_cmd(conn, &rsp, SMP_AUTH_NONE); + + key_size = min(req->max_key_size, rsp.max_key_size); + if (check_enc_key_size(conn, key_size)) + return SMP_ENC_KEY_SIZE; /* Just works */ memset(conn->tk, 0, sizeof(conn->tk)); conn->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&conn->prsp[1], rp, sizeof(*rp)); + memcpy(&conn->prsp[1], &rsp, sizeof(rsp)); - smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp); + smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp); mod_timer(&conn->security_timer, jiffies + msecs_to_jiffies(SMP_TIMEOUT)); @@ -238,24 +254,30 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { - struct smp_cmd_pairing *rp = (void *) skb->data; + struct smp_cmd_pairing *req, *rsp = (void *) skb->data; struct smp_cmd_pairing_confirm cp; struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; int ret; - u8 res[16]; + u8 res[16], key_size; BT_DBG("conn %p", conn); - skb_pull(skb, sizeof(*rp)); + skb_pull(skb, sizeof(*rsp)); + + req = (void *) &conn->preq[1]; - if (rp->oob_flag) + key_size = min(req->max_key_size, rsp->max_key_size); + if (check_enc_key_size(conn, key_size)) + return SMP_ENC_KEY_SIZE; + + if (rsp->oob_flag) return SMP_OOB_NOT_AVAIL; /* Just works */ memset(conn->tk, 0, sizeof(conn->tk)); conn->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&conn->prsp[1], rp, sizeof(*rp)); + memcpy(&conn->prsp[1], rsp, sizeof(*rsp)); ret = smp_rand(conn->prnd); if (ret) @@ -353,6 +375,9 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) smp_s1(tfm, conn->tk, random, conn->prnd, key); swap128(key, hcon->ltk); + memset(hcon->ltk + conn->smp_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); + memset(rand, 0, sizeof(rand)); ediv = 0; hci_le_start_enc(hcon, ediv, rand, hcon->ltk); @@ -364,6 +389,9 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) smp_s1(tfm, conn->tk, conn->prnd, random, key); swap128(key, hcon->ltk); + + memset(hcon->ltk + conn->smp_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); } return 0; -- cgit v1.2.3 From 09223371deac67d08ca0b70bd18787920284c967 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Tue, 14 Jun 2011 13:26:25 +0800 Subject: rcu: Use softirq to address performance regression Commit a26ac2455ffcf3(rcu: move TREE_RCU from softirq to kthread) introduced performance regression. In an AIM7 test, this commit degraded performance by about 40%. The commit runs rcu callbacks in a kthread instead of softirq. We observed high rate of context switch which is caused by this. Out test system has 64 CPUs and HZ is 1000, so we saw more than 64k context switch per second which is caused by RCU's per-CPU kthread. A trace showed that most of the time the RCU per-CPU kthread doesn't actually handle any callbacks, but instead just does a very small amount of work handling grace periods. This means that RCU's per-CPU kthreads are making the scheduler do quite a bit of work in order to allow a very small amount of RCU-related processing to be done. Alex Shi's analysis determined that this slowdown is due to lock contention within the scheduler. Unfortunately, as Peter Zijlstra points out, the scheduler's real-time semantics require global action, which means that this contention is inherent in real-time scheduling. (Yes, perhaps someone will come up with a workaround -- otherwise, -rt is not going to do well on large SMP systems -- but this patch will work around this issue in the meantime. And "the meantime" might well be forever.) This patch therefore re-introduces softirq processing to RCU, but only for core RCU work. RCU callbacks are still executed in kthread context, so that only a small amount of RCU work runs in softirq context in the common case. This should minimize ksoftirqd execution, allowing us to skip boosting of ksoftirqd for CONFIG_RCU_BOOST=y kernels. Signed-off-by: Shaohua Li Tested-by: "Alex,Shi" Signed-off-by: Paul E. McKenney --- Documentation/filesystems/proc.txt | 1 + include/linux/interrupt.h | 1 + include/trace/events/irq.h | 3 ++- kernel/rcutree.c | 23 +++++++++++++++++++---- kernel/rcutree.h | 1 + kernel/rcutree_plugin.h | 9 +++++++++ kernel/softirq.c | 2 +- tools/perf/util/trace-event-parse.c | 1 + 8 files changed, 35 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index f48178024067..db3b1aba32a3 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -843,6 +843,7 @@ Provides counts of softirq handlers serviced since boot time, for each cpu. TASKLET: 0 0 0 290 SCHED: 27035 26983 26971 26746 HRTIMER: 0 0 0 0 + RCU: 1678 1769 2178 2250 1.3 IDE devices in /proc/ide diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 6c12989839d9..f6efed0039ed 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -414,6 +414,7 @@ enum TASKLET_SOFTIRQ, SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, + RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS }; diff --git a/include/trace/events/irq.h b/include/trace/events/irq.h index ae045ca7d356..1c09820df585 100644 --- a/include/trace/events/irq.h +++ b/include/trace/events/irq.h @@ -20,7 +20,8 @@ struct softirq_action; softirq_name(BLOCK_IOPOLL), \ softirq_name(TASKLET), \ softirq_name(SCHED), \ - softirq_name(HRTIMER)) + softirq_name(HRTIMER), \ + softirq_name(RCU)) /** * irq_handler_entry - called immediately before the irq action handler diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 0a8ec5b2e208..ae5c9ea68662 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -100,6 +100,7 @@ static char rcu_kthreads_spawnable; static void rcu_node_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu); static void invoke_rcu_cpu_kthread(void); +static void __invoke_rcu_cpu_kthread(void); #define RCU_KTHREAD_PRIO 1 /* RT priority for per-CPU kthreads. */ @@ -1442,13 +1443,21 @@ __rcu_process_callbacks(struct rcu_state *rsp, struct rcu_data *rdp) } /* If there are callbacks ready, invoke them. */ - rcu_do_batch(rsp, rdp); + if (cpu_has_callbacks_ready_to_invoke(rdp)) + __invoke_rcu_cpu_kthread(); +} + +static void rcu_kthread_do_work(void) +{ + rcu_do_batch(&rcu_sched_state, &__get_cpu_var(rcu_sched_data)); + rcu_do_batch(&rcu_bh_state, &__get_cpu_var(rcu_bh_data)); + rcu_preempt_do_callbacks(); } /* * Do softirq processing for the current CPU. */ -static void rcu_process_callbacks(void) +static void rcu_process_callbacks(struct softirq_action *unused) { __rcu_process_callbacks(&rcu_sched_state, &__get_cpu_var(rcu_sched_data)); @@ -1465,7 +1474,7 @@ static void rcu_process_callbacks(void) * the current CPU with interrupts disabled, the rcu_cpu_kthread_task * cannot disappear out from under us. */ -static void invoke_rcu_cpu_kthread(void) +static void __invoke_rcu_cpu_kthread(void) { unsigned long flags; @@ -1479,6 +1488,11 @@ static void invoke_rcu_cpu_kthread(void) local_irq_restore(flags); } +static void invoke_rcu_cpu_kthread(void) +{ + raise_softirq(RCU_SOFTIRQ); +} + /* * Wake up the specified per-rcu_node-structure kthread. * Because the per-rcu_node kthreads are immortal, we don't need @@ -1613,7 +1627,7 @@ static int rcu_cpu_kthread(void *arg) *workp = 0; local_irq_restore(flags); if (work) - rcu_process_callbacks(); + rcu_kthread_do_work(); local_bh_enable(); if (*workp != 0) spincnt++; @@ -2387,6 +2401,7 @@ void __init rcu_init(void) rcu_init_one(&rcu_sched_state, &rcu_sched_data); rcu_init_one(&rcu_bh_state, &rcu_bh_data); __rcu_init_preempt(); + open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); /* * We don't need protection against CPU-hotplug here because diff --git a/kernel/rcutree.h b/kernel/rcutree.h index 7b9a08b4aaea..0fed6b934d2a 100644 --- a/kernel/rcutree.h +++ b/kernel/rcutree.h @@ -439,6 +439,7 @@ static void rcu_preempt_offline_cpu(int cpu); #endif /* #ifdef CONFIG_HOTPLUG_CPU */ static void rcu_preempt_check_callbacks(int cpu); static void rcu_preempt_process_callbacks(void); +static void rcu_preempt_do_callbacks(void); void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu)); #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_TREE_PREEMPT_RCU) static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp); diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index ea2e2fb79e81..38d09c5f2b41 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -602,6 +602,11 @@ static void rcu_preempt_process_callbacks(void) &__get_cpu_var(rcu_preempt_data)); } +static void rcu_preempt_do_callbacks(void) +{ + rcu_do_batch(&rcu_preempt_state, &__get_cpu_var(rcu_preempt_data)); +} + /* * Queue a preemptible-RCU callback for invocation after a grace period. */ @@ -997,6 +1002,10 @@ static void rcu_preempt_process_callbacks(void) { } +static void rcu_preempt_do_callbacks(void) +{ +} + /* * Wait for an rcu-preempt grace period, but make it happen quickly. * But because preemptible RCU does not exist, map to rcu-sched. diff --git a/kernel/softirq.c b/kernel/softirq.c index 13960170cad4..40cf63ddd4b3 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -58,7 +58,7 @@ DEFINE_PER_CPU(struct task_struct *, ksoftirqd); char *softirq_to_name[NR_SOFTIRQS] = { "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL", - "TASKLET", "SCHED", "HRTIMER" + "TASKLET", "SCHED", "HRTIMER", "RCU" }; /* diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 1e88485c16a0..0a7ed5b5e281 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -2187,6 +2187,7 @@ static const struct flag flags[] = { { "TASKLET_SOFTIRQ", 6 }, { "SCHED_SOFTIRQ", 7 }, { "HRTIMER_SOFTIRQ", 8 }, + { "RCU_SOFTIRQ", 9 }, { "HRTIMER_NORESTART", 0 }, { "HRTIMER_RESTART", 1 }, -- cgit v1.2.3 From 7ea5906405a1f3fc1c0033dfd7e02f2cfd1de5e5 Mon Sep 17 00:00:00 2001 From: Vaibhav Nagarnaik Date: Tue, 3 May 2011 17:56:42 -0700 Subject: tracing: Use NUMA allocation for per-cpu ring buffer pages The tracing ring buffer is a group of per-cpu ring buffers where allocation and logging is done on a per-cpu basis. The events that are generated on a particular CPU are logged in the corresponding buffer. This is to provide wait-free writes between CPUs and good NUMA node locality while accessing the ring buffer. However, the allocation routines consider NUMA locality only for buffer page metadata and not for the actual buffer page. This causes the pages to be allocated on the NUMA node local to the CPU where the allocation routine is running at the time. This patch fixes the problem by using a NUMA node specific allocation routine so that the pages are allocated from a NUMA node local to the logging CPU. I tested with the getuid_microbench from autotest. It is a simple binary that calls getuid() in a loop and measures the average time for the syscall to complete. The following command was used to test: $ getuid_microbench 1000000 Compared the numbers found on kernel with and without this patch and found that logging latency decreases by 30-50 ns/call. tracing with non-NUMA allocation - 569 ns/call tracing with NUMA allocation - 512 ns/call Signed-off-by: Vaibhav Nagarnaik Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Michael Rubin Cc: David Sharp Link: http://lkml.kernel.org/r/1304470602-20366-1-git-send-email-vnagarnaik@google.com Signed-off-by: Steven Rostedt --- include/linux/ring_buffer.h | 2 +- kernel/trace/ring_buffer.c | 36 +++++++++++++++++++----------------- kernel/trace/ring_buffer_benchmark.c | 2 +- kernel/trace/trace.c | 7 +++---- 4 files changed, 24 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index ab38ac80b0f9..b891de96000f 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -169,7 +169,7 @@ void ring_buffer_set_clock(struct ring_buffer *buffer, size_t ring_buffer_page_len(void *page); -void *ring_buffer_alloc_read_page(struct ring_buffer *buffer); +void *ring_buffer_alloc_read_page(struct ring_buffer *buffer, int cpu); void ring_buffer_free_read_page(struct ring_buffer *buffer, void *data); int ring_buffer_read_page(struct ring_buffer *buffer, void **data_page, size_t len, int cpu, int full); diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index b0c7aa407943..725153d6cf73 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -997,13 +997,14 @@ static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, unsigned nr_pages) { struct buffer_page *bpage, *tmp; - unsigned long addr; LIST_HEAD(pages); unsigned i; WARN_ON(!nr_pages); for (i = 0; i < nr_pages; i++) { + struct page *page; + bpage = kzalloc_node(ALIGN(sizeof(*bpage), cache_line_size()), GFP_KERNEL, cpu_to_node(cpu_buffer->cpu)); if (!bpage) @@ -1013,10 +1014,11 @@ static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, list_add(&bpage->list, &pages); - addr = __get_free_page(GFP_KERNEL); - if (!addr) + page = alloc_pages_node(cpu_to_node(cpu_buffer->cpu), + GFP_KERNEL, 0); + if (!page) goto free_pages; - bpage->page = (void *)addr; + bpage->page = page_address(page); rb_init_page(bpage->page); } @@ -1045,7 +1047,7 @@ rb_allocate_cpu_buffer(struct ring_buffer *buffer, int cpu) { struct ring_buffer_per_cpu *cpu_buffer; struct buffer_page *bpage; - unsigned long addr; + struct page *page; int ret; cpu_buffer = kzalloc_node(ALIGN(sizeof(*cpu_buffer), cache_line_size()), @@ -1067,10 +1069,10 @@ rb_allocate_cpu_buffer(struct ring_buffer *buffer, int cpu) rb_check_bpage(cpu_buffer, bpage); cpu_buffer->reader_page = bpage; - addr = __get_free_page(GFP_KERNEL); - if (!addr) + page = alloc_pages_node(cpu_to_node(cpu), GFP_KERNEL, 0); + if (!page) goto fail_free_reader; - bpage->page = (void *)addr; + bpage->page = page_address(page); rb_init_page(bpage->page); INIT_LIST_HEAD(&cpu_buffer->reader_page->list); @@ -1314,7 +1316,6 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size) unsigned nr_pages, rm_pages, new_pages; struct buffer_page *bpage, *tmp; unsigned long buffer_size; - unsigned long addr; LIST_HEAD(pages); int i, cpu; @@ -1375,16 +1376,17 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size) for_each_buffer_cpu(buffer, cpu) { for (i = 0; i < new_pages; i++) { + struct page *page; bpage = kzalloc_node(ALIGN(sizeof(*bpage), cache_line_size()), GFP_KERNEL, cpu_to_node(cpu)); if (!bpage) goto free_pages; list_add(&bpage->list, &pages); - addr = __get_free_page(GFP_KERNEL); - if (!addr) + page = alloc_pages_node(cpu_to_node(cpu), GFP_KERNEL, 0); + if (!page) goto free_pages; - bpage->page = (void *)addr; + bpage->page = page_address(page); rb_init_page(bpage->page); } } @@ -3730,16 +3732,16 @@ EXPORT_SYMBOL_GPL(ring_buffer_swap_cpu); * Returns: * The page allocated, or NULL on error. */ -void *ring_buffer_alloc_read_page(struct ring_buffer *buffer) +void *ring_buffer_alloc_read_page(struct ring_buffer *buffer, int cpu) { struct buffer_data_page *bpage; - unsigned long addr; + struct page *page; - addr = __get_free_page(GFP_KERNEL); - if (!addr) + page = alloc_pages_node(cpu_to_node(cpu), GFP_KERNEL, 0); + if (!page) return NULL; - bpage = (void *)addr; + bpage = page_address(page); rb_init_page(bpage); diff --git a/kernel/trace/ring_buffer_benchmark.c b/kernel/trace/ring_buffer_benchmark.c index 302f8a614635..a5457d577b98 100644 --- a/kernel/trace/ring_buffer_benchmark.c +++ b/kernel/trace/ring_buffer_benchmark.c @@ -106,7 +106,7 @@ static enum event_status read_page(int cpu) int inc; int i; - bpage = ring_buffer_alloc_read_page(buffer); + bpage = ring_buffer_alloc_read_page(buffer, cpu); if (!bpage) return EVENT_DROPPED; diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 71777c8fe36b..61fda6b6f1ab 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -3697,7 +3697,7 @@ tracing_buffers_read(struct file *filp, char __user *ubuf, return 0; if (!info->spare) - info->spare = ring_buffer_alloc_read_page(info->tr->buffer); + info->spare = ring_buffer_alloc_read_page(info->tr->buffer, info->cpu); if (!info->spare) return -ENOMEM; @@ -3854,7 +3854,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, ref->ref = 1; ref->buffer = info->tr->buffer; - ref->page = ring_buffer_alloc_read_page(ref->buffer); + ref->page = ring_buffer_alloc_read_page(ref->buffer, info->cpu); if (!ref->page) { kfree(ref); break; @@ -3863,8 +3863,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, r = ring_buffer_read_page(ref->buffer, &ref->page, len, info->cpu, 1); if (r < 0) { - ring_buffer_free_read_page(ref->buffer, - ref->page); + ring_buffer_free_read_page(ref->buffer, ref->page); kfree(ref); break; } -- cgit v1.2.3 From 395810627b6a43c8d0ec948884043946fa162308 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 8 Jun 2011 16:09:21 +0900 Subject: x86: Swap save_stack_trace_regs parameters Swap the 1st and 2nd parameters of save_stack_trace_regs() as same as the parameters of save_stack_trace_tsk(). Signed-off-by: Masami Hiramatsu Cc: yrl.pp-manager.tt@hitachi.com Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20110608070921.17777.31103.stgit@fedora15 Signed-off-by: Steven Rostedt --- arch/x86/kernel/stacktrace.c | 2 +- arch/x86/mm/kmemcheck/error.c | 2 +- include/linux/stacktrace.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/stacktrace.c b/arch/x86/kernel/stacktrace.c index 55d9bc03f696..fdd0c6430e5a 100644 --- a/arch/x86/kernel/stacktrace.c +++ b/arch/x86/kernel/stacktrace.c @@ -66,7 +66,7 @@ void save_stack_trace(struct stack_trace *trace) } EXPORT_SYMBOL_GPL(save_stack_trace); -void save_stack_trace_regs(struct stack_trace *trace, struct pt_regs *regs) +void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) { dump_trace(current, regs, NULL, 0, &save_stack_ops, trace); if (trace->nr_entries < trace->max_entries) diff --git a/arch/x86/mm/kmemcheck/error.c b/arch/x86/mm/kmemcheck/error.c index 704a37cedddb..dab41876cdd5 100644 --- a/arch/x86/mm/kmemcheck/error.c +++ b/arch/x86/mm/kmemcheck/error.c @@ -185,7 +185,7 @@ void kmemcheck_error_save(enum kmemcheck_shadow state, e->trace.entries = e->trace_entries; e->trace.max_entries = ARRAY_SIZE(e->trace_entries); e->trace.skip = 0; - save_stack_trace_regs(&e->trace, regs); + save_stack_trace_regs(regs, &e->trace); /* Round address down to nearest 16 bytes */ shadow_copy = kmemcheck_shadow_lookup(address diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h index 25310f1d7f37..115b570e3bff 100644 --- a/include/linux/stacktrace.h +++ b/include/linux/stacktrace.h @@ -14,8 +14,8 @@ struct stack_trace { }; extern void save_stack_trace(struct stack_trace *trace); -extern void save_stack_trace_regs(struct stack_trace *trace, - struct pt_regs *regs); +extern void save_stack_trace_regs(struct pt_regs *regs, + struct stack_trace *trace); extern void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace); -- cgit v1.2.3 From 1fd8df2c3970c9e7e4e262354154ee39e58bdd7c Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 8 Jun 2011 16:09:34 +0900 Subject: tracing/kprobes: Fix kprobe-tracer to support stack trace Fix to support kernel stack trace correctly on kprobe-tracer. Since the execution path of kprobe-based dynamic events is different from other tracepoint-based events, normal ftrace_trace_stack() doesn't work correctly. To fix that, this introduces ftrace_trace_stack_regs() which traces stack via pt_regs instead of current stack register. e.g. # echo p schedule+4 > /sys/kernel/debug/tracing/kprobe_events # echo 1 > /sys/kernel/debug/tracing/options/stacktrace # echo 1 > /sys/kernel/debug/tracing/events/kprobes/enable # head -n 20 /sys/kernel/debug/tracing/trace bash-2968 [000] 10297.050245: p_schedule_4: (schedule+0x4/0x4ca) bash-2968 [000] 10297.050247: => schedule_timeout => n_tty_read => tty_read => vfs_read => sys_read => system_call_fastpath kworker/0:1-2940 [000] 10297.050265: p_schedule_4: (schedule+0x4/0x4ca) kworker/0:1-2940 [000] 10297.050266: => worker_thread => kthread => kernel_thread_helper sshd-1132 [000] 10297.050365: p_schedule_4: (schedule+0x4/0x4ca) sshd-1132 [000] 10297.050365: => sysret_careful Note: Even with this fix, the first entry will be skipped if the probe is put on the function entry area before the frame pointer is set up (usually, that is 4 bytes (push %bp; mov %sp %bp) on x86), because stack unwinder depends on the frame pointer. Signed-off-by: Masami Hiramatsu Cc: Frederic Weisbecker Cc: yrl.pp-manager.tt@hitachi.com Cc: Peter Zijlstra Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20110608070934.17777.17116.stgit@fedora15 Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 4 ++++ kernel/trace/trace.c | 34 +++++++++++++++++++++++++++++----- kernel/trace/trace.h | 9 +++++++++ kernel/trace/trace_kprobe.c | 6 ++++-- 4 files changed, 46 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 59d3ef100eb9..b1e69eefc203 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -129,6 +129,10 @@ void trace_current_buffer_unlock_commit(struct ring_buffer *buffer, void trace_nowake_buffer_unlock_commit(struct ring_buffer *buffer, struct ring_buffer_event *event, unsigned long flags, int pc); +void trace_nowake_buffer_unlock_commit_regs(struct ring_buffer *buffer, + struct ring_buffer_event *event, + unsigned long flags, int pc, + struct pt_regs *regs); void trace_current_buffer_discard_commit(struct ring_buffer *buffer, struct ring_buffer_event *event); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index c977018e87c2..d9c16123f6e2 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1193,6 +1193,18 @@ void trace_nowake_buffer_unlock_commit(struct ring_buffer *buffer, } EXPORT_SYMBOL_GPL(trace_nowake_buffer_unlock_commit); +void trace_nowake_buffer_unlock_commit_regs(struct ring_buffer *buffer, + struct ring_buffer_event *event, + unsigned long flags, int pc, + struct pt_regs *regs) +{ + ring_buffer_unlock_commit(buffer, event); + + ftrace_trace_stack_regs(buffer, flags, 0, pc, regs); + ftrace_trace_userstack(buffer, flags, pc); +} +EXPORT_SYMBOL_GPL(trace_nowake_buffer_unlock_commit_regs); + void trace_current_buffer_discard_commit(struct ring_buffer *buffer, struct ring_buffer_event *event) { @@ -1238,7 +1250,7 @@ ftrace(struct trace_array *tr, struct trace_array_cpu *data, #ifdef CONFIG_STACKTRACE static void __ftrace_trace_stack(struct ring_buffer *buffer, unsigned long flags, - int skip, int pc) + int skip, int pc, struct pt_regs *regs) { struct ftrace_event_call *call = &event_kernel_stack; struct ring_buffer_event *event; @@ -1257,24 +1269,36 @@ static void __ftrace_trace_stack(struct ring_buffer *buffer, trace.skip = skip; trace.entries = entry->caller; - save_stack_trace(&trace); + if (regs) + save_stack_trace_regs(regs, &trace); + else + save_stack_trace(&trace); if (!filter_check_discard(call, entry, buffer, event)) ring_buffer_unlock_commit(buffer, event); } +void ftrace_trace_stack_regs(struct ring_buffer *buffer, unsigned long flags, + int skip, int pc, struct pt_regs *regs) +{ + if (!(trace_flags & TRACE_ITER_STACKTRACE)) + return; + + __ftrace_trace_stack(buffer, flags, skip, pc, regs); +} + void ftrace_trace_stack(struct ring_buffer *buffer, unsigned long flags, int skip, int pc) { if (!(trace_flags & TRACE_ITER_STACKTRACE)) return; - __ftrace_trace_stack(buffer, flags, skip, pc); + __ftrace_trace_stack(buffer, flags, skip, pc, NULL); } void __trace_stack(struct trace_array *tr, unsigned long flags, int skip, int pc) { - __ftrace_trace_stack(tr->buffer, flags, skip, pc); + __ftrace_trace_stack(tr->buffer, flags, skip, pc, NULL); } /** @@ -1290,7 +1314,7 @@ void trace_dump_stack(void) local_save_flags(flags); /* skipping 3 traces, seems to get us at the caller of this function */ - __ftrace_trace_stack(global_trace.buffer, flags, 3, preempt_count()); + __ftrace_trace_stack(global_trace.buffer, flags, 3, preempt_count(), NULL); } static DEFINE_PER_CPU(int, user_stack_count); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 742f545ae185..a3e2db708072 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -389,6 +389,9 @@ void update_max_tr_single(struct trace_array *tr, void ftrace_trace_stack(struct ring_buffer *buffer, unsigned long flags, int skip, int pc); +void ftrace_trace_stack_regs(struct ring_buffer *buffer, unsigned long flags, + int skip, int pc, struct pt_regs *regs); + void ftrace_trace_userstack(struct ring_buffer *buffer, unsigned long flags, int pc); @@ -400,6 +403,12 @@ static inline void ftrace_trace_stack(struct ring_buffer *buffer, { } +static inline void ftrace_trace_stack_regs(struct ring_buffer *buffer, + unsigned long flags, int skip, + int pc, struct pt_regs *regs) +{ +} + static inline void ftrace_trace_userstack(struct ring_buffer *buffer, unsigned long flags, int pc) { diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index f925c45f0afa..7053ef3d73d2 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -1397,7 +1397,8 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs) store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); if (!filter_current_check_discard(buffer, call, entry, event)) - trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc); + trace_nowake_buffer_unlock_commit_regs(buffer, event, + irq_flags, pc, regs); } /* Kretprobe handler */ @@ -1429,7 +1430,8 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri, store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); if (!filter_current_check_discard(buffer, call, entry, event)) - trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc); + trace_nowake_buffer_unlock_commit_regs(buffer, event, + irq_flags, pc, regs); } /* Event entry printers */ -- cgit v1.2.3 From 221c56373ee7088dd3015b928782d5e70dc5074e Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Mon, 13 Jun 2011 13:39:01 +0000 Subject: tg3: Migrate phy preprocessor defs to system defs This patch changes to code to use some of the preprocessor definitions from mii.h over its homegrown equivalents. Signed-off-by: Matt Carlson Reviewed-by: Michael Chan Reviewed-by: Benjamin Li Signed-off-by: David S. Miller --- drivers/net/tg3.c | 26 ++++++++++++-------------- drivers/net/tg3.h | 8 -------- include/linux/mii.h | 2 ++ 3 files changed, 14 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 7b1e2abf0cea..da5e9105b9f4 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -861,7 +861,7 @@ static int tg3_writephy(struct tg3 *tp, int reg, u32 val) int ret; if ((tp->phy_flags & TG3_PHYFLG_IS_FET) && - (reg == MII_TG3_CTRL || reg == MII_TG3_AUX_CTRL)) + (reg == MII_CTRL1000 || reg == MII_TG3_AUX_CTRL)) return 0; if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { @@ -1981,15 +1981,14 @@ static int tg3_phy_reset_5703_4_5(struct tg3 *tp) /* Set full-duplex, 1000 mbps. */ tg3_writephy(tp, MII_BMCR, - BMCR_FULLDPLX | TG3_BMCR_SPEED1000); + BMCR_FULLDPLX | BMCR_SPEED1000); /* Set to master mode. */ - if (tg3_readphy(tp, MII_TG3_CTRL, &phy9_orig)) + if (tg3_readphy(tp, MII_CTRL1000, &phy9_orig)) continue; - tg3_writephy(tp, MII_TG3_CTRL, - (MII_TG3_CTRL_AS_MASTER | - MII_TG3_CTRL_ENABLE_AS_MASTER)); + tg3_writephy(tp, MII_CTRL1000, + CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER); err = TG3_PHY_AUXCTL_SMDSP_ENABLE(tp); if (err) @@ -2014,7 +2013,7 @@ static int tg3_phy_reset_5703_4_5(struct tg3 *tp) TG3_PHY_AUXCTL_SMDSP_DISABLE(tp); - tg3_writephy(tp, MII_TG3_CTRL, phy9_orig); + tg3_writephy(tp, MII_CTRL1000, phy9_orig); if (!tg3_readphy(tp, MII_TG3_EXT_CTRL, ®32)) { reg32 &= ~0x3000; @@ -2958,16 +2957,15 @@ static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl) new_adv = 0; if (advertise & ADVERTISED_1000baseT_Half) - new_adv |= MII_TG3_CTRL_ADV_1000_HALF; + new_adv |= ADVERTISE_1000HALF; if (advertise & ADVERTISED_1000baseT_Full) - new_adv |= MII_TG3_CTRL_ADV_1000_FULL; + new_adv |= ADVERTISE_1000FULL; if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) - new_adv |= (MII_TG3_CTRL_AS_MASTER | - MII_TG3_CTRL_ENABLE_AS_MASTER); + new_adv |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER; - err = tg3_writephy(tp, MII_TG3_CTRL, new_adv); + err = tg3_writephy(tp, MII_CTRL1000, new_adv); if (err) goto done; @@ -3076,7 +3074,7 @@ static void tg3_phy_copper_begin(struct tg3 *tp) break; case SPEED_1000: - bmcr |= TG3_BMCR_SPEED1000; + bmcr |= BMCR_SPEED1000; break; } @@ -3153,7 +3151,7 @@ static int tg3_copper_is_advertising_all(struct tg3 *tp, u32 mask) if (mask & ADVERTISED_1000baseT_Full) all_mask |= ADVERTISE_1000FULL; - if (tg3_readphy(tp, MII_TG3_CTRL, &tg3_ctrl)) + if (tg3_readphy(tp, MII_CTRL1000, &tg3_ctrl)) return 0; if ((tg3_ctrl & all_mask) != all_mask) diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 54441a346cb3..bedc3b4557b5 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -2152,14 +2152,6 @@ /*** Tigon3 specific PHY MII registers. ***/ -#define TG3_BMCR_SPEED1000 0x0040 - -#define MII_TG3_CTRL 0x09 /* 1000-baseT control register */ -#define MII_TG3_CTRL_ADV_1000_HALF 0x0100 -#define MII_TG3_CTRL_ADV_1000_FULL 0x0200 -#define MII_TG3_CTRL_AS_MASTER 0x0800 -#define MII_TG3_CTRL_ENABLE_AS_MASTER 0x1000 - #define MII_TG3_MMD_CTRL 0x0d /* MMD Access Control register */ #define MII_TG3_MMD_CTRL_DATA_NOINC 0x4000 #define MII_TG3_MMD_ADDRESS 0x0e /* MMD Address Data register */ diff --git a/include/linux/mii.h b/include/linux/mii.h index 359fba880274..103113a2fd18 100644 --- a/include/linux/mii.h +++ b/include/linux/mii.h @@ -128,6 +128,8 @@ /* 1000BASE-T Control register */ #define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ #define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ +#define CTL1000_AS_MASTER 0x0800 +#define CTL1000_ENABLE_MASTER 0x1000 /* 1000BASE-T Status register */ #define LPA_1000LOCALRXOK 0x2000 /* Link partner local receiver status */ -- cgit v1.2.3 From 0b760113a3a155269a3fba93a409c640031dd68f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 31 May 2011 15:15:34 -0400 Subject: NLM: Don't hang forever on NLM unlock requests If the NLM daemon is killed on the NFS server, we can currently end up hanging forever on an 'unlock' request, instead of aborting. Basically, if the rpcbind request fails, or the server keeps returning garbage, we really want to quit instead of retrying. Tested-by: Vasily Averin Signed-off-by: Trond Myklebust Cc: stable@kernel.org --- fs/lockd/clntproc.c | 8 +++++++- include/linux/sunrpc/sched.h | 3 ++- net/sunrpc/clnt.c | 3 +++ net/sunrpc/sched.c | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index adb45ec9038c..e374050a911c 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -708,7 +708,13 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data) if (task->tk_status < 0) { dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status); - goto retry_rebind; + switch (task->tk_status) { + case -EACCES: + case -EIO: + goto die; + default: + goto retry_rebind; + } } if (status == NLM_LCK_DENIED_GRACE_PERIOD) { rpc_delay(task, NLMCLNT_GRACE_WAIT); diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index f73c482ec9c6..fe2d8e6b923b 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -84,7 +84,8 @@ struct rpc_task { #endif unsigned char tk_priority : 2,/* Task priority */ tk_garb_retry : 2, - tk_cred_retry : 2; + tk_cred_retry : 2, + tk_rebind_retry : 2; }; #define tk_xprt tk_client->cl_xprt diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index b84d7395535e..566bcfd067f6 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1175,6 +1175,9 @@ call_bind_status(struct rpc_task *task) status = -EOPNOTSUPP; break; } + if (task->tk_rebind_retry == 0) + break; + task->tk_rebind_retry--; rpc_delay(task, 3*HZ); goto retry_timeout; case -ETIMEDOUT: diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 6b43ee7221d5..a27406b1654f 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -792,6 +792,7 @@ static void rpc_init_task(struct rpc_task *task, const struct rpc_task_setup *ta /* Initialize retry counters */ task->tk_garb_retry = 2; task->tk_cred_retry = 2; + task->tk_rebind_retry = 2; task->tk_priority = task_setup_data->priority - RPC_PRIORITY_LOW; task->tk_owner = current->tgid; -- cgit v1.2.3 From c9c30dd5f73dccaa326a54dfcf490316946aea87 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Sat, 11 Jun 2011 17:08:39 -0400 Subject: NFSv4.1: deprecate headerpadsz in CREATE_SESSION We don't support header padding yet so better off ditching it Reported-by: Sid Moore Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 4 ---- fs/nfs/nfs4xdr.c | 10 ++++++---- include/linux/nfs_xdr.h | 1 - 3 files changed, 6 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d2c4b59c896d..3ca449789545 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5098,7 +5098,6 @@ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args) if (mxresp_sz == 0) mxresp_sz = NFS_MAX_FILE_IO_SIZE; /* Fore channel attributes */ - args->fc_attrs.headerpadsz = 0; args->fc_attrs.max_rqst_sz = mxrqst_sz; args->fc_attrs.max_resp_sz = mxresp_sz; args->fc_attrs.max_ops = NFS4_MAX_OPS; @@ -5111,7 +5110,6 @@ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args) args->fc_attrs.max_ops, args->fc_attrs.max_reqs); /* Back channel attributes */ - args->bc_attrs.headerpadsz = 0; args->bc_attrs.max_rqst_sz = PAGE_SIZE; args->bc_attrs.max_resp_sz = PAGE_SIZE; args->bc_attrs.max_resp_sz_cached = 0; @@ -5131,8 +5129,6 @@ static int nfs4_verify_fore_channel_attrs(struct nfs41_create_session_args *args struct nfs4_channel_attrs *sent = &args->fc_attrs; struct nfs4_channel_attrs *rcvd = &session->fc_attrs; - if (rcvd->headerpadsz > sent->headerpadsz) - return -EINVAL; if (rcvd->max_resp_sz > sent->max_resp_sz) return -EINVAL; /* diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index d869a5e5464b..c4b7d6c04948 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1725,7 +1725,7 @@ static void encode_create_session(struct xdr_stream *xdr, *p++ = cpu_to_be32(args->flags); /*flags */ /* Fore Channel */ - *p++ = cpu_to_be32(args->fc_attrs.headerpadsz); /* header padding size */ + *p++ = cpu_to_be32(0); /* header padding size */ *p++ = cpu_to_be32(args->fc_attrs.max_rqst_sz); /* max req size */ *p++ = cpu_to_be32(args->fc_attrs.max_resp_sz); /* max resp size */ *p++ = cpu_to_be32(max_resp_sz_cached); /* Max resp sz cached */ @@ -1734,7 +1734,7 @@ static void encode_create_session(struct xdr_stream *xdr, *p++ = cpu_to_be32(0); /* rdmachannel_attrs */ /* Back Channel */ - *p++ = cpu_to_be32(args->fc_attrs.headerpadsz); /* header padding size */ + *p++ = cpu_to_be32(0); /* header padding size */ *p++ = cpu_to_be32(args->bc_attrs.max_rqst_sz); /* max req size */ *p++ = cpu_to_be32(args->bc_attrs.max_resp_sz); /* max resp size */ *p++ = cpu_to_be32(args->bc_attrs.max_resp_sz_cached); /* Max resp sz cached */ @@ -4997,12 +4997,14 @@ static int decode_chan_attrs(struct xdr_stream *xdr, struct nfs4_channel_attrs *attrs) { __be32 *p; - u32 nr_attrs; + u32 nr_attrs, val; p = xdr_inline_decode(xdr, 28); if (unlikely(!p)) goto out_overflow; - attrs->headerpadsz = be32_to_cpup(p++); + val = be32_to_cpup(p++); /* headerpadsz */ + if (val) + return -EINVAL; /* no support for header padding yet */ attrs->max_rqst_sz = be32_to_cpup(p++); attrs->max_resp_sz = be32_to_cpup(p++); attrs->max_resp_sz_cached = be32_to_cpup(p++); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 5e8444a11adf..00848d86ffb2 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -158,7 +158,6 @@ struct nfs_seqid; /* nfs41 sessions channel attributes */ struct nfs4_channel_attrs { - u32 headerpadsz; u32 max_rqst_sz; u32 max_resp_sz; u32 max_resp_sz_cached; -- cgit v1.2.3 From a59ec1e7ff98cc4365d5b1bff4e7102e86b5716b Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Wed, 15 Jun 2011 15:08:11 -0700 Subject: backlight: new driver for the ADP8870 backlight devices Signed-off-by: Michael Hennerich Signed-off-by: Mike Frysinger Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- .../testing/sysfs-class-backlight-driver-adp8870 | 56 ++ drivers/video/backlight/Kconfig | 12 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/adp8870_bl.c | 1011 ++++++++++++++++++++ include/linux/i2c/adp8870.h | 153 +++ 5 files changed, 1233 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-backlight-driver-adp8870 create mode 100644 drivers/video/backlight/adp8870_bl.c create mode 100644 include/linux/i2c/adp8870.h (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-backlight-driver-adp8870 b/Documentation/ABI/testing/sysfs-class-backlight-driver-adp8870 new file mode 100644 index 000000000000..aa11dbdd794b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-backlight-driver-adp8870 @@ -0,0 +1,56 @@ +What: /sys/class/backlight//_max +What: /sys/class/backlight//l1_daylight_max +What: /sys/class/backlight//l2_bright_max +What: /sys/class/backlight//l3_office_max +What: /sys/class/backlight//l4_indoor_max +What: /sys/class/backlight//l5_dark_max +Date: Mai 2011 +KernelVersion: 2.6.40 +Contact: device-drivers-devel@blackfin.uclinux.org +Description: + Control the maximum brightness for + on this . Values are between 0 and 127. This file + will also show the brightness level stored for this + . + +What: /sys/class/backlight//_dim +What: /sys/class/backlight//l2_bright_dim +What: /sys/class/backlight//l3_office_dim +What: /sys/class/backlight//l4_indoor_dim +What: /sys/class/backlight//l5_dark_dim +Date: Mai 2011 +KernelVersion: 2.6.40 +Contact: device-drivers-devel@blackfin.uclinux.org +Description: + Control the dim brightness for + on this . Values are between 0 and 127, typically + set to 0. Full off when the backlight is disabled. + This file will also show the dim brightness level stored for + this . + +What: /sys/class/backlight//ambient_light_level +Date: Mai 2011 +KernelVersion: 2.6.40 +Contact: device-drivers-devel@blackfin.uclinux.org +Description: + Get conversion value of the light sensor. + This value is updated every 80 ms (when the light sensor + is enabled). Returns integer between 0 (dark) and + 8000 (max ambient brightness) + +What: /sys/class/backlight//ambient_light_zone +Date: Mai 2011 +KernelVersion: 2.6.40 +Contact: device-drivers-devel@blackfin.uclinux.org +Description: + Get/Set current ambient light zone. Reading returns + integer between 1..5 (1 = daylight, 2 = bright, ..., 5 = dark). + Writing a value between 1..5 forces the backlight controller + to enter the corresponding ambient light zone. + Writing 0 returns to normal/automatic ambient light level + operation. The ambient light sensing feature on these devices + is an extension to the API documented in + Documentation/ABI/stable/sysfs-class-backlight. + It can be enabled by writing the value stored in + /sys/class/backlight//max_brightness to + /sys/class/backlight//brightness. \ No newline at end of file diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 0c9373bedd1f..2d93c8d61ad5 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -302,6 +302,18 @@ config BACKLIGHT_ADP8860 To compile this driver as a module, choose M here: the module will be called adp8860_bl. +config BACKLIGHT_ADP8870 + tristate "Backlight Driver for ADP8870 using WLED" + depends on BACKLIGHT_CLASS_DEVICE && I2C + select NEW_LEDS + select LEDS_CLASS + help + If you have a LCD backlight connected to the ADP8870, + say Y here to enable this driver. + + To compile this driver as a module, choose M here: the module will + be called adp8870_bl. + config BACKLIGHT_88PM860X tristate "Backlight Driver for 88PM8606 using WLED" depends on MFD_88PM860X diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index b9ca8490df87..ee72adb8786e 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o obj-$(CONFIG_BACKLIGHT_ADX) += adx_bl.o obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o +obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c new file mode 100644 index 000000000000..e320b62576e2 --- /dev/null +++ b/drivers/video/backlight/adp8870_bl.c @@ -0,0 +1,1011 @@ +/* + * Backlight driver for Analog Devices ADP8870 Backlight Devices + * + * Copyright 2009-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#define ADP8870_EXT_FEATURES +#define ADP8870_USE_LEDS + + +#define ADP8870_MFDVID 0x00 /* Manufacturer and device ID */ +#define ADP8870_MDCR 0x01 /* Device mode and status */ +#define ADP8870_INT_STAT 0x02 /* Interrupts status */ +#define ADP8870_INT_EN 0x03 /* Interrupts enable */ +#define ADP8870_CFGR 0x04 /* Configuration register */ +#define ADP8870_BLSEL 0x05 /* Sink enable backlight or independent */ +#define ADP8870_PWMLED 0x06 /* PWM Enable Selection Register */ +#define ADP8870_BLOFF 0x07 /* Backlight off timeout */ +#define ADP8870_BLDIM 0x08 /* Backlight dim timeout */ +#define ADP8870_BLFR 0x09 /* Backlight fade in and out rates */ +#define ADP8870_BLMX1 0x0A /* Backlight (Brightness Level 1-daylight) maximum current */ +#define ADP8870_BLDM1 0x0B /* Backlight (Brightness Level 1-daylight) dim current */ +#define ADP8870_BLMX2 0x0C /* Backlight (Brightness Level 2-bright) maximum current */ +#define ADP8870_BLDM2 0x0D /* Backlight (Brightness Level 2-bright) dim current */ +#define ADP8870_BLMX3 0x0E /* Backlight (Brightness Level 3-office) maximum current */ +#define ADP8870_BLDM3 0x0F /* Backlight (Brightness Level 3-office) dim current */ +#define ADP8870_BLMX4 0x10 /* Backlight (Brightness Level 4-indoor) maximum current */ +#define ADP8870_BLDM4 0x11 /* Backlight (Brightness Level 4-indoor) dim current */ +#define ADP8870_BLMX5 0x12 /* Backlight (Brightness Level 5-dark) maximum current */ +#define ADP8870_BLDM5 0x13 /* Backlight (Brightness Level 5-dark) dim current */ +#define ADP8870_ISCLAW 0x1A /* Independent sink current fade law register */ +#define ADP8870_ISCC 0x1B /* Independent sink current control register */ +#define ADP8870_ISCT1 0x1C /* Independent Sink Current Timer Register LED[7:5] */ +#define ADP8870_ISCT2 0x1D /* Independent Sink Current Timer Register LED[4:1] */ +#define ADP8870_ISCF 0x1E /* Independent sink current fade register */ +#define ADP8870_ISC1 0x1F /* Independent Sink Current LED1 */ +#define ADP8870_ISC2 0x20 /* Independent Sink Current LED2 */ +#define ADP8870_ISC3 0x21 /* Independent Sink Current LED3 */ +#define ADP8870_ISC4 0x22 /* Independent Sink Current LED4 */ +#define ADP8870_ISC5 0x23 /* Independent Sink Current LED5 */ +#define ADP8870_ISC6 0x24 /* Independent Sink Current LED6 */ +#define ADP8870_ISC7 0x25 /* Independent Sink Current LED7 (Brightness Level 1-daylight) */ +#define ADP8870_ISC7_L2 0x26 /* Independent Sink Current LED7 (Brightness Level 2-bright) */ +#define ADP8870_ISC7_L3 0x27 /* Independent Sink Current LED7 (Brightness Level 3-office) */ +#define ADP8870_ISC7_L4 0x28 /* Independent Sink Current LED7 (Brightness Level 4-indoor) */ +#define ADP8870_ISC7_L5 0x29 /* Independent Sink Current LED7 (Brightness Level 5-dark) */ +#define ADP8870_CMP_CTL 0x2D /* ALS Comparator Control Register */ +#define ADP8870_ALS1_EN 0x2E /* Main ALS comparator level enable */ +#define ADP8870_ALS2_EN 0x2F /* Second ALS comparator level enable */ +#define ADP8870_ALS1_STAT 0x30 /* Main ALS Comparator Status Register */ +#define ADP8870_ALS2_STAT 0x31 /* Second ALS Comparator Status Register */ +#define ADP8870_L2TRP 0x32 /* L2 comparator reference */ +#define ADP8870_L2HYS 0x33 /* L2 hysteresis */ +#define ADP8870_L3TRP 0x34 /* L3 comparator reference */ +#define ADP8870_L3HYS 0x35 /* L3 hysteresis */ +#define ADP8870_L4TRP 0x36 /* L4 comparator reference */ +#define ADP8870_L4HYS 0x37 /* L4 hysteresis */ +#define ADP8870_L5TRP 0x38 /* L5 comparator reference */ +#define ADP8870_L5HYS 0x39 /* L5 hysteresis */ +#define ADP8870_PH1LEVL 0x40 /* First phototransistor ambient light level-low byte register */ +#define ADP8870_PH1LEVH 0x41 /* First phototransistor ambient light level-high byte register */ +#define ADP8870_PH2LEVL 0x42 /* Second phototransistor ambient light level-low byte register */ +#define ADP8870_PH2LEVH 0x43 /* Second phototransistor ambient light level-high byte register */ + +#define ADP8870_MANUFID 0x3 /* Analog Devices AD8870 Manufacturer and device ID */ +#define ADP8870_DEVID(x) ((x) & 0xF) +#define ADP8870_MANID(x) ((x) >> 4) + +/* MDCR Device mode and status */ +#define D7ALSEN (1 << 7) +#define INT_CFG (1 << 6) +#define NSTBY (1 << 5) +#define DIM_EN (1 << 4) +#define GDWN_DIS (1 << 3) +#define SIS_EN (1 << 2) +#define CMP_AUTOEN (1 << 1) +#define BLEN (1 << 0) + +/* ADP8870_ALS1_EN Main ALS comparator level enable */ +#define L5_EN (1 << 3) +#define L4_EN (1 << 2) +#define L3_EN (1 << 1) +#define L2_EN (1 << 0) + +#define CFGR_BLV_SHIFT 3 +#define CFGR_BLV_MASK 0x7 +#define ADP8870_FLAG_LED_MASK 0xFF + +#define FADE_VAL(in, out) ((0xF & (in)) | ((0xF & (out)) << 4)) +#define BL_CFGR_VAL(law, blv) ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1)) +#define ALS_CMPR_CFG_VAL(filt) ((0x7 & (filt)) << 1) + +struct adp8870_bl { + struct i2c_client *client; + struct backlight_device *bl; + struct adp8870_led *led; + struct adp8870_backlight_platform_data *pdata; + struct mutex lock; + unsigned long cached_daylight_max; + int id; + int revid; + int current_brightness; +}; + +struct adp8870_led { + struct led_classdev cdev; + struct work_struct work; + struct i2c_client *client; + enum led_brightness new_brightness; + int id; + int flags; +}; + +static int adp8870_read(struct i2c_client *client, int reg, uint8_t *val) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) { + dev_err(&client->dev, "failed reading at 0x%02x\n", reg); + return ret; + } + + *val = ret; + return 0; +} + + +static int adp8870_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret) + dev_err(&client->dev, "failed to write\n"); + + return ret; +} + +static int adp8870_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask) +{ + struct adp8870_bl *data = i2c_get_clientdata(client); + uint8_t reg_val; + int ret; + + mutex_lock(&data->lock); + + ret = adp8870_read(client, reg, ®_val); + + if (!ret && ((reg_val & bit_mask) == 0)) { + reg_val |= bit_mask; + ret = adp8870_write(client, reg, reg_val); + } + + mutex_unlock(&data->lock); + return ret; +} + +static int adp8870_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask) +{ + struct adp8870_bl *data = i2c_get_clientdata(client); + uint8_t reg_val; + int ret; + + mutex_lock(&data->lock); + + ret = adp8870_read(client, reg, ®_val); + + if (!ret && (reg_val & bit_mask)) { + reg_val &= ~bit_mask; + ret = adp8870_write(client, reg, reg_val); + } + + mutex_unlock(&data->lock); + return ret; +} + +/* + * Independent sink / LED + */ +#if defined(ADP8870_USE_LEDS) +static void adp8870_led_work(struct work_struct *work) +{ + struct adp8870_led *led = container_of(work, struct adp8870_led, work); + adp8870_write(led->client, ADP8870_ISC1 + led->id - 1, + led->new_brightness >> 1); +} + +static void adp8870_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct adp8870_led *led; + + led = container_of(led_cdev, struct adp8870_led, cdev); + led->new_brightness = value; + /* + * Use workqueue for IO since I2C operations can sleep. + */ + schedule_work(&led->work); +} + +static int adp8870_led_setup(struct adp8870_led *led) +{ + struct i2c_client *client = led->client; + int ret = 0; + + ret = adp8870_write(client, ADP8870_ISC1 + led->id - 1, 0); + if (ret) + return ret; + + ret = adp8870_set_bits(client, ADP8870_ISCC, 1 << (led->id - 1)); + if (ret) + return ret; + + if (led->id > 4) + ret = adp8870_set_bits(client, ADP8870_ISCT1, + (led->flags & 0x3) << ((led->id - 5) * 2)); + else + ret = adp8870_set_bits(client, ADP8870_ISCT2, + (led->flags & 0x3) << ((led->id - 1) * 2)); + + return ret; +} + +static int __devinit adp8870_led_probe(struct i2c_client *client) +{ + struct adp8870_backlight_platform_data *pdata = + client->dev.platform_data; + struct adp8870_bl *data = i2c_get_clientdata(client); + struct adp8870_led *led, *led_dat; + struct led_info *cur_led; + int ret, i; + + + led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL); + if (led == NULL) { + dev_err(&client->dev, "failed to alloc memory\n"); + return -ENOMEM; + } + + ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law); + if (ret) + goto err_free; + + ret = adp8870_write(client, ADP8870_ISCT1, + (pdata->led_on_time & 0x3) << 6); + if (ret) + goto err_free; + + ret = adp8870_write(client, ADP8870_ISCF, + FADE_VAL(pdata->led_fade_in, pdata->led_fade_out)); + if (ret) + goto err_free; + + for (i = 0; i < pdata->num_leds; ++i) { + cur_led = &pdata->leds[i]; + led_dat = &led[i]; + + led_dat->id = cur_led->flags & ADP8870_FLAG_LED_MASK; + + if (led_dat->id > 7 || led_dat->id < 1) { + dev_err(&client->dev, "Invalid LED ID %d\n", + led_dat->id); + goto err; + } + + if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) { + dev_err(&client->dev, "LED %d used by Backlight\n", + led_dat->id); + goto err; + } + + led_dat->cdev.name = cur_led->name; + led_dat->cdev.default_trigger = cur_led->default_trigger; + led_dat->cdev.brightness_set = adp8870_led_set; + led_dat->cdev.brightness = LED_OFF; + led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT; + led_dat->client = client; + led_dat->new_brightness = LED_OFF; + INIT_WORK(&led_dat->work, adp8870_led_work); + + ret = led_classdev_register(&client->dev, &led_dat->cdev); + if (ret) { + dev_err(&client->dev, "failed to register LED %d\n", + led_dat->id); + goto err; + } + + ret = adp8870_led_setup(led_dat); + if (ret) { + dev_err(&client->dev, "failed to write\n"); + i++; + goto err; + } + } + + data->led = led; + + return 0; + + err: + for (i = i - 1; i >= 0; --i) { + led_classdev_unregister(&led[i].cdev); + cancel_work_sync(&led[i].work); + } + + err_free: + kfree(led); + + return ret; +} + +static int __devexit adp8870_led_remove(struct i2c_client *client) +{ + struct adp8870_backlight_platform_data *pdata = + client->dev.platform_data; + struct adp8870_bl *data = i2c_get_clientdata(client); + int i; + + for (i = 0; i < pdata->num_leds; i++) { + led_classdev_unregister(&data->led[i].cdev); + cancel_work_sync(&data->led[i].work); + } + + kfree(data->led); + return 0; +} +#else +static int __devinit adp8870_led_probe(struct i2c_client *client) +{ + return 0; +} + +static int __devexit adp8870_led_remove(struct i2c_client *client) +{ + return 0; +} +#endif + +static int adp8870_bl_set(struct backlight_device *bl, int brightness) +{ + struct adp8870_bl *data = bl_get_data(bl); + struct i2c_client *client = data->client; + int ret = 0; + + if (data->pdata->en_ambl_sens) { + if ((brightness > 0) && (brightness < ADP8870_MAX_BRIGHTNESS)) { + /* Disable Ambient Light auto adjust */ + ret = adp8870_clr_bits(client, ADP8870_MDCR, + CMP_AUTOEN); + if (ret) + return ret; + ret = adp8870_write(client, ADP8870_BLMX1, brightness); + if (ret) + return ret; + } else { + /* + * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust + * restore daylight l1 sysfs brightness + */ + ret = adp8870_write(client, ADP8870_BLMX1, + data->cached_daylight_max); + if (ret) + return ret; + + ret = adp8870_set_bits(client, ADP8870_MDCR, + CMP_AUTOEN); + if (ret) + return ret; + } + } else { + ret = adp8870_write(client, ADP8870_BLMX1, brightness); + if (ret) + return ret; + } + + if (data->current_brightness && brightness == 0) + ret = adp8870_set_bits(client, + ADP8870_MDCR, DIM_EN); + else if (data->current_brightness == 0 && brightness) + ret = adp8870_clr_bits(client, + ADP8870_MDCR, DIM_EN); + + if (!ret) + data->current_brightness = brightness; + + return ret; +} + +static int adp8870_bl_update_status(struct backlight_device *bl) +{ + int brightness = bl->props.brightness; + if (bl->props.power != FB_BLANK_UNBLANK) + brightness = 0; + + if (bl->props.fb_blank != FB_BLANK_UNBLANK) + brightness = 0; + + return adp8870_bl_set(bl, brightness); +} + +static int adp8870_bl_get_brightness(struct backlight_device *bl) +{ + struct adp8870_bl *data = bl_get_data(bl); + + return data->current_brightness; +} + +static const struct backlight_ops adp8870_bl_ops = { + .update_status = adp8870_bl_update_status, + .get_brightness = adp8870_bl_get_brightness, +}; + +static int adp8870_bl_setup(struct backlight_device *bl) +{ + struct adp8870_bl *data = bl_get_data(bl); + struct i2c_client *client = data->client; + struct adp8870_backlight_platform_data *pdata = data->pdata; + int ret = 0; + + ret = adp8870_write(client, ADP8870_BLSEL, ~pdata->bl_led_assign); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_PWMLED, pdata->pwm_assign); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLMX1, pdata->l1_daylight_max); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLDM1, pdata->l1_daylight_dim); + if (ret) + return ret; + + if (pdata->en_ambl_sens) { + data->cached_daylight_max = pdata->l1_daylight_max; + ret = adp8870_write(client, ADP8870_BLMX2, + pdata->l2_bright_max); + if (ret) + return ret; + ret = adp8870_write(client, ADP8870_BLDM2, + pdata->l2_bright_dim); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLMX3, + pdata->l3_office_max); + if (ret) + return ret; + ret = adp8870_write(client, ADP8870_BLDM3, + pdata->l3_office_dim); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLMX4, + pdata->l4_indoor_max); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLDM4, + pdata->l4_indor_dim); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLMX5, + pdata->l5_dark_max); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLDM5, + pdata->l5_dark_dim); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L2TRP, pdata->l2_trip); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L2HYS, pdata->l2_hyst); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L3TRP, pdata->l3_trip); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L3HYS, pdata->l3_hyst); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L4TRP, pdata->l4_trip); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L4HYS, pdata->l4_hyst); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L5TRP, pdata->l5_trip); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L5HYS, pdata->l5_hyst); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_ALS1_EN, L5_EN | L4_EN | + L3_EN | L2_EN); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_CMP_CTL, + ALS_CMPR_CFG_VAL(pdata->abml_filt)); + if (ret) + return ret; + } + + ret = adp8870_write(client, ADP8870_CFGR, + BL_CFGR_VAL(pdata->bl_fade_law, 0)); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLFR, FADE_VAL(pdata->bl_fade_in, + pdata->bl_fade_out)); + if (ret) + return ret; + /* + * ADP8870 Rev0 requires GDWN_DIS bit set + */ + + ret = adp8870_set_bits(client, ADP8870_MDCR, BLEN | DIM_EN | NSTBY | + (data->revid == 0 ? GDWN_DIS : 0)); + + return ret; +} + +static ssize_t adp8870_show(struct device *dev, char *buf, int reg) +{ + struct adp8870_bl *data = dev_get_drvdata(dev); + int error; + uint8_t reg_val; + + mutex_lock(&data->lock); + error = adp8870_read(data->client, reg, ®_val); + mutex_unlock(&data->lock); + + if (error < 0) + return error; + + return sprintf(buf, "%u\n", reg_val); +} + +static ssize_t adp8870_store(struct device *dev, const char *buf, + size_t count, int reg) +{ + struct adp8870_bl *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&data->lock); + adp8870_write(data->client, reg, val); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t adp8870_bl_l5_dark_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLMX5); +} + +static ssize_t adp8870_bl_l5_dark_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLMX5); +} +static DEVICE_ATTR(l5_dark_max, 0664, adp8870_bl_l5_dark_max_show, + adp8870_bl_l5_dark_max_store); + + +static ssize_t adp8870_bl_l4_indoor_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLMX4); +} + +static ssize_t adp8870_bl_l4_indoor_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLMX4); +} +static DEVICE_ATTR(l4_indoor_max, 0664, adp8870_bl_l4_indoor_max_show, + adp8870_bl_l4_indoor_max_store); + + +static ssize_t adp8870_bl_l3_office_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLMX3); +} + +static ssize_t adp8870_bl_l3_office_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLMX3); +} + +static DEVICE_ATTR(l3_office_max, 0664, adp8870_bl_l3_office_max_show, + adp8870_bl_l3_office_max_store); + +static ssize_t adp8870_bl_l2_bright_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLMX2); +} + +static ssize_t adp8870_bl_l2_bright_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLMX2); +} +static DEVICE_ATTR(l2_bright_max, 0664, adp8870_bl_l2_bright_max_show, + adp8870_bl_l2_bright_max_store); + +static ssize_t adp8870_bl_l1_daylight_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLMX1); +} + +static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adp8870_bl *data = dev_get_drvdata(dev); + int ret = strict_strtoul(buf, 10, &data->cached_daylight_max); + if (ret) + return ret; + + return adp8870_store(dev, buf, count, ADP8870_BLMX1); +} +static DEVICE_ATTR(l1_daylight_max, 0664, adp8870_bl_l1_daylight_max_show, + adp8870_bl_l1_daylight_max_store); + +static ssize_t adp8870_bl_l5_dark_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLDM5); +} + +static ssize_t adp8870_bl_l5_dark_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLDM5); +} +static DEVICE_ATTR(l5_dark_dim, 0664, adp8870_bl_l5_dark_dim_show, + adp8870_bl_l5_dark_dim_store); + +static ssize_t adp8870_bl_l4_indoor_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLDM4); +} + +static ssize_t adp8870_bl_l4_indoor_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLDM4); +} +static DEVICE_ATTR(l4_indoor_dim, 0664, adp8870_bl_l4_indoor_dim_show, + adp8870_bl_l4_indoor_dim_store); + + +static ssize_t adp8870_bl_l3_office_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLDM3); +} + +static ssize_t adp8870_bl_l3_office_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLDM3); +} +static DEVICE_ATTR(l3_office_dim, 0664, adp8870_bl_l3_office_dim_show, + adp8870_bl_l3_office_dim_store); + +static ssize_t adp8870_bl_l2_bright_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLDM2); +} + +static ssize_t adp8870_bl_l2_bright_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLDM2); +} +static DEVICE_ATTR(l2_bright_dim, 0664, adp8870_bl_l2_bright_dim_show, + adp8870_bl_l2_bright_dim_store); + +static ssize_t adp8870_bl_l1_daylight_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLDM1); +} + +static ssize_t adp8870_bl_l1_daylight_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLDM1); +} +static DEVICE_ATTR(l1_daylight_dim, 0664, adp8870_bl_l1_daylight_dim_show, + adp8870_bl_l1_daylight_dim_store); + +#ifdef ADP8870_EXT_FEATURES +static ssize_t adp8870_bl_ambient_light_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adp8870_bl *data = dev_get_drvdata(dev); + int error; + uint8_t reg_val; + uint16_t ret_val; + + mutex_lock(&data->lock); + error = adp8870_read(data->client, ADP8870_PH1LEVL, ®_val); + if (error < 0) { + mutex_unlock(&data->lock); + return error; + } + ret_val = reg_val; + error = adp8870_read(data->client, ADP8870_PH1LEVH, ®_val); + mutex_unlock(&data->lock); + + if (error < 0) + return error; + + /* Return 13-bit conversion value for the first light sensor */ + ret_val += (reg_val & 0x1F) << 8; + + return sprintf(buf, "%u\n", ret_val); +} +static DEVICE_ATTR(ambient_light_level, 0444, + adp8870_bl_ambient_light_level_show, NULL); + +static ssize_t adp8870_bl_ambient_light_zone_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adp8870_bl *data = dev_get_drvdata(dev); + int error; + uint8_t reg_val; + + mutex_lock(&data->lock); + error = adp8870_read(data->client, ADP8870_CFGR, ®_val); + mutex_unlock(&data->lock); + + if (error < 0) + return error; + + return sprintf(buf, "%u\n", + ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1); +} + +static ssize_t adp8870_bl_ambient_light_zone_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adp8870_bl *data = dev_get_drvdata(dev); + unsigned long val; + uint8_t reg_val; + int ret; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return ret; + + if (val == 0) { + /* Enable automatic ambient light sensing */ + adp8870_set_bits(data->client, ADP8870_MDCR, CMP_AUTOEN); + } else if ((val > 0) && (val < 6)) { + /* Disable automatic ambient light sensing */ + adp8870_clr_bits(data->client, ADP8870_MDCR, CMP_AUTOEN); + + /* Set user supplied ambient light zone */ + mutex_lock(&data->lock); + adp8870_read(data->client, ADP8870_CFGR, ®_val); + reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); + reg_val |= (val - 1) << CFGR_BLV_SHIFT; + adp8870_write(data->client, ADP8870_CFGR, reg_val); + mutex_unlock(&data->lock); + } + + return count; +} +static DEVICE_ATTR(ambient_light_zone, 0664, + adp8870_bl_ambient_light_zone_show, + adp8870_bl_ambient_light_zone_store); +#endif + +static struct attribute *adp8870_bl_attributes[] = { + &dev_attr_l5_dark_max.attr, + &dev_attr_l5_dark_dim.attr, + &dev_attr_l4_indoor_max.attr, + &dev_attr_l4_indoor_dim.attr, + &dev_attr_l3_office_max.attr, + &dev_attr_l3_office_dim.attr, + &dev_attr_l2_bright_max.attr, + &dev_attr_l2_bright_dim.attr, + &dev_attr_l1_daylight_max.attr, + &dev_attr_l1_daylight_dim.attr, +#ifdef ADP8870_EXT_FEATURES + &dev_attr_ambient_light_level.attr, + &dev_attr_ambient_light_zone.attr, +#endif + NULL +}; + +static const struct attribute_group adp8870_bl_attr_group = { + .attrs = adp8870_bl_attributes, +}; + +static int __devinit adp8870_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct backlight_properties props; + struct backlight_device *bl; + struct adp8870_bl *data; + struct adp8870_backlight_platform_data *pdata = + client->dev.platform_data; + uint8_t reg_val; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + ret = adp8870_read(client, ADP8870_MFDVID, ®_val); + if (ret < 0) + return -EIO; + + if (ADP8870_MANID(reg_val) != ADP8870_MANUFID) { + dev_err(&client->dev, "failed to probe\n"); + return -ENODEV; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + data->revid = ADP8870_DEVID(reg_val); + data->client = client; + data->pdata = pdata; + data->id = id->driver_data; + data->current_brightness = 0; + i2c_set_clientdata(client, data); + + mutex_init(&data->lock); + + memset(&props, 0, sizeof(props)); + props.max_brightness = props.brightness = ADP8870_MAX_BRIGHTNESS; + bl = backlight_device_register(dev_driver_string(&client->dev), + &client->dev, data, &adp8870_bl_ops, &props); + if (IS_ERR(bl)) { + dev_err(&client->dev, "failed to register backlight\n"); + ret = PTR_ERR(bl); + goto out2; + } + + data->bl = bl; + + if (pdata->en_ambl_sens) + ret = sysfs_create_group(&bl->dev.kobj, + &adp8870_bl_attr_group); + + if (ret) { + dev_err(&client->dev, "failed to register sysfs\n"); + goto out1; + } + + ret = adp8870_bl_setup(bl); + if (ret) { + ret = -EIO; + goto out; + } + + backlight_update_status(bl); + + dev_info(&client->dev, "Rev.%d Backlight\n", data->revid); + + if (pdata->num_leds) + adp8870_led_probe(client); + + return 0; + +out: + if (data->pdata->en_ambl_sens) + sysfs_remove_group(&data->bl->dev.kobj, + &adp8870_bl_attr_group); +out1: + backlight_device_unregister(bl); +out2: + i2c_set_clientdata(client, NULL); + kfree(data); + + return ret; +} + +static int __devexit adp8870_remove(struct i2c_client *client) +{ + struct adp8870_bl *data = i2c_get_clientdata(client); + + adp8870_clr_bits(client, ADP8870_MDCR, NSTBY); + + if (data->led) + adp8870_led_remove(client); + + if (data->pdata->en_ambl_sens) + sysfs_remove_group(&data->bl->dev.kobj, + &adp8870_bl_attr_group); + + backlight_device_unregister(data->bl); + i2c_set_clientdata(client, NULL); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int adp8870_i2c_suspend(struct i2c_client *client, pm_message_t message) +{ + adp8870_clr_bits(client, ADP8870_MDCR, NSTBY); + + return 0; +} + +static int adp8870_i2c_resume(struct i2c_client *client) +{ + adp8870_set_bits(client, ADP8870_MDCR, NSTBY); + + return 0; +} +#else +#define adp8870_i2c_suspend NULL +#define adp8870_i2c_resume NULL +#endif + +static const struct i2c_device_id adp8870_id[] = { + { "adp8870", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adp8870_id); + +static struct i2c_driver adp8870_driver = { + .driver = { + .name = KBUILD_MODNAME, + }, + .probe = adp8870_probe, + .remove = __devexit_p(adp8870_remove), + .suspend = adp8870_i2c_suspend, + .resume = adp8870_i2c_resume, + .id_table = adp8870_id, +}; + +static int __init adp8870_init(void) +{ + return i2c_add_driver(&adp8870_driver); +} +module_init(adp8870_init); + +static void __exit adp8870_exit(void) +{ + i2c_del_driver(&adp8870_driver); +} +module_exit(adp8870_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("ADP8870 Backlight driver"); +MODULE_ALIAS("platform:adp8870-backlight"); diff --git a/include/linux/i2c/adp8870.h b/include/linux/i2c/adp8870.h new file mode 100644 index 000000000000..624dceccbd5b --- /dev/null +++ b/include/linux/i2c/adp8870.h @@ -0,0 +1,153 @@ +/* + * Definitions and platform data for Analog Devices + * Backlight drivers ADP8870 + * + * Copyright 2009-2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __LINUX_I2C_ADP8870_H +#define __LINUX_I2C_ADP8870_H + +#define ID_ADP8870 8870 + +#define ADP8870_MAX_BRIGHTNESS 0x7F +#define FLAG_OFFT_SHIFT 8 + +/* + * LEDs subdevice platform data + */ + +#define ADP8870_LED_DIS_BLINK (0 << FLAG_OFFT_SHIFT) +#define ADP8870_LED_OFFT_600ms (1 << FLAG_OFFT_SHIFT) +#define ADP8870_LED_OFFT_1200ms (2 << FLAG_OFFT_SHIFT) +#define ADP8870_LED_OFFT_1800ms (3 << FLAG_OFFT_SHIFT) + +#define ADP8870_LED_ONT_200ms 0 +#define ADP8870_LED_ONT_600ms 1 +#define ADP8870_LED_ONT_800ms 2 +#define ADP8870_LED_ONT_1200ms 3 + +#define ADP8870_LED_D7 (7) +#define ADP8870_LED_D6 (6) +#define ADP8870_LED_D5 (5) +#define ADP8870_LED_D4 (4) +#define ADP8870_LED_D3 (3) +#define ADP8870_LED_D2 (2) +#define ADP8870_LED_D1 (1) + +/* + * Backlight subdevice platform data + */ + +#define ADP8870_BL_D7 (1 << 6) +#define ADP8870_BL_D6 (1 << 5) +#define ADP8870_BL_D5 (1 << 4) +#define ADP8870_BL_D4 (1 << 3) +#define ADP8870_BL_D3 (1 << 2) +#define ADP8870_BL_D2 (1 << 1) +#define ADP8870_BL_D1 (1 << 0) + +#define ADP8870_FADE_T_DIS 0 /* Fade Timer Disabled */ +#define ADP8870_FADE_T_300ms 1 /* 0.3 Sec */ +#define ADP8870_FADE_T_600ms 2 +#define ADP8870_FADE_T_900ms 3 +#define ADP8870_FADE_T_1200ms 4 +#define ADP8870_FADE_T_1500ms 5 +#define ADP8870_FADE_T_1800ms 6 +#define ADP8870_FADE_T_2100ms 7 +#define ADP8870_FADE_T_2400ms 8 +#define ADP8870_FADE_T_2700ms 9 +#define ADP8870_FADE_T_3000ms 10 +#define ADP8870_FADE_T_3500ms 11 +#define ADP8870_FADE_T_4000ms 12 +#define ADP8870_FADE_T_4500ms 13 +#define ADP8870_FADE_T_5000ms 14 +#define ADP8870_FADE_T_5500ms 15 /* 5.5 Sec */ + +#define ADP8870_FADE_LAW_LINEAR 0 +#define ADP8870_FADE_LAW_SQUARE 1 +#define ADP8870_FADE_LAW_CUBIC1 2 +#define ADP8870_FADE_LAW_CUBIC2 3 + +#define ADP8870_BL_AMBL_FILT_80ms 0 /* Light sensor filter time */ +#define ADP8870_BL_AMBL_FILT_160ms 1 +#define ADP8870_BL_AMBL_FILT_320ms 2 +#define ADP8870_BL_AMBL_FILT_640ms 3 +#define ADP8870_BL_AMBL_FILT_1280ms 4 +#define ADP8870_BL_AMBL_FILT_2560ms 5 +#define ADP8870_BL_AMBL_FILT_5120ms 6 +#define ADP8870_BL_AMBL_FILT_10240ms 7 /* 10.24 sec */ + +/* + * Blacklight current 0..30mA + */ +#define ADP8870_BL_CUR_mA(I) ((I * 127) / 30) + +/* + * L2 comparator current 0..1106uA + */ +#define ADP8870_L2_COMP_CURR_uA(I) ((I * 255) / 1106) + +/* + * L3 comparator current 0..551uA + */ +#define ADP8870_L3_COMP_CURR_uA(I) ((I * 255) / 551) + +/* + * L4 comparator current 0..275uA + */ +#define ADP8870_L4_COMP_CURR_uA(I) ((I * 255) / 275) + +/* + * L5 comparator current 0..138uA + */ +#define ADP8870_L5_COMP_CURR_uA(I) ((I * 255) / 138) + +struct adp8870_backlight_platform_data { + u8 bl_led_assign; /* 1 = Backlight 0 = Individual LED */ + u8 pwm_assign; /* 1 = Enables PWM mode */ + + u8 bl_fade_in; /* Backlight Fade-In Timer */ + u8 bl_fade_out; /* Backlight Fade-Out Timer */ + u8 bl_fade_law; /* fade-on/fade-off transfer characteristic */ + + u8 en_ambl_sens; /* 1 = enable ambient light sensor */ + u8 abml_filt; /* Light sensor filter time */ + + u8 l1_daylight_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */ + u8 l1_daylight_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */ + u8 l2_bright_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */ + u8 l2_bright_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */ + u8 l3_office_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */ + u8 l3_office_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */ + u8 l4_indoor_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */ + u8 l4_indor_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */ + u8 l5_dark_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */ + u8 l5_dark_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */ + + u8 l2_trip; /* use L2_COMP_CURR_uA(I) 0 <= I <= 1106 uA */ + u8 l2_hyst; /* use L2_COMP_CURR_uA(I) 0 <= I <= 1106 uA */ + u8 l3_trip; /* use L3_COMP_CURR_uA(I) 0 <= I <= 551 uA */ + u8 l3_hyst; /* use L3_COMP_CURR_uA(I) 0 <= I <= 551 uA */ + u8 l4_trip; /* use L4_COMP_CURR_uA(I) 0 <= I <= 275 uA */ + u8 l4_hyst; /* use L4_COMP_CURR_uA(I) 0 <= I <= 275 uA */ + u8 l5_trip; /* use L5_COMP_CURR_uA(I) 0 <= I <= 138 uA */ + u8 l5_hyst; /* use L6_COMP_CURR_uA(I) 0 <= I <= 138 uA */ + + /** + * Independent Current Sinks / LEDS + * Sinks not assigned to the Backlight can be exposed to + * user space using the LEDS CLASS interface + */ + + int num_leds; + struct led_info *leds; + u8 led_fade_in; /* LED Fade-In Timer */ + u8 led_fade_out; /* LED Fade-Out Timer */ + u8 led_fade_law; /* fade-on/fade-off transfer characteristic */ + u8 led_on_time; +}; + +#endif /* __LINUX_I2C_ADP8870_H */ -- cgit v1.2.3 From a433658c30974fc87ba3ff52d7e4e6299762aa3d Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Wed, 15 Jun 2011 15:08:13 -0700 Subject: vmscan,memcg: memcg aware swap token Currently, memcg reclaim can disable swap token even if the swap token mm doesn't belong in its memory cgroup. It's slightly risky. If an admin creates very small mem-cgroup and silly guy runs contentious heavy memory pressure workload, every tasks are going to lose swap token and then system may become unresponsive. That's bad. This patch adds 'memcg' parameter into disable_swap_token(). and if the parameter doesn't match swap token, VM doesn't disable it. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: KOSAKI Motohiro Reviewed-by: KAMEZAWA Hiroyuki Reviewed-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 6 ++++ include/linux/swap.h | 8 ++--- mm/memcontrol.c | 16 ++++----- mm/thrash.c | 87 ++++++++++++++++++++++++++++++++++++---------- mm/vmscan.c | 4 +-- 5 files changed, 85 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 9724a38ee69d..50940da6adf3 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -84,6 +84,7 @@ int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem); extern struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page); extern struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p); +extern struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm); static inline int mm_match_cgroup(const struct mm_struct *mm, const struct mem_cgroup *cgroup) @@ -246,6 +247,11 @@ static inline struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page) return NULL; } +static inline struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm) +{ + return NULL; +} + static inline int mm_match_cgroup(struct mm_struct *mm, struct mem_cgroup *mem) { return 1; diff --git a/include/linux/swap.h b/include/linux/swap.h index 384eb5fe530b..e70564647039 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -358,6 +358,7 @@ struct backing_dev_info; extern struct mm_struct *swap_token_mm; extern void grab_swap_token(struct mm_struct *); extern void __put_swap_token(struct mm_struct *); +extern void disable_swap_token(struct mem_cgroup *memcg); static inline int has_swap_token(struct mm_struct *mm) { @@ -370,11 +371,6 @@ static inline void put_swap_token(struct mm_struct *mm) __put_swap_token(mm); } -static inline void disable_swap_token(void) -{ - put_swap_token(swap_token_mm); -} - #ifdef CONFIG_CGROUP_MEM_RES_CTLR extern void mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout); @@ -500,7 +496,7 @@ static inline int has_swap_token(struct mm_struct *mm) return 0; } -static inline void disable_swap_token(void) +static inline void disable_swap_token(struct mem_cgroup *memcg) { } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index bd9052a5d3ad..e37c44d88d46 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -735,7 +735,7 @@ struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p) struct mem_cgroup, css); } -static struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm) +struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm) { struct mem_cgroup *mem = NULL; @@ -5414,18 +5414,16 @@ static void mem_cgroup_move_task(struct cgroup_subsys *ss, struct cgroup *old_cont, struct task_struct *p) { - struct mm_struct *mm; + struct mm_struct *mm = get_task_mm(p); - if (!mc.to) - /* no need to move charge */ - return; - - mm = get_task_mm(p); if (mm) { - mem_cgroup_move_charge(mm); + if (mc.to) + mem_cgroup_move_charge(mm); + put_swap_token(mm); mmput(mm); } - mem_cgroup_clear_mc(); + if (mc.to) + mem_cgroup_clear_mc(); } #else /* !CONFIG_MMU */ static int mem_cgroup_can_attach(struct cgroup_subsys *ss, diff --git a/mm/thrash.c b/mm/thrash.c index 2372d4ed5dd8..6cdf86511385 100644 --- a/mm/thrash.c +++ b/mm/thrash.c @@ -21,11 +21,31 @@ #include #include #include +#include static DEFINE_SPINLOCK(swap_token_lock); struct mm_struct *swap_token_mm; +struct mem_cgroup *swap_token_memcg; static unsigned int global_faults; +#ifdef CONFIG_CGROUP_MEM_RES_CTLR +static struct mem_cgroup *swap_token_memcg_from_mm(struct mm_struct *mm) +{ + struct mem_cgroup *memcg; + + memcg = try_get_mem_cgroup_from_mm(mm); + if (memcg) + css_put(mem_cgroup_css(memcg)); + + return memcg; +} +#else +static struct mem_cgroup *swap_token_memcg_from_mm(struct mm_struct *mm) +{ + return NULL; +} +#endif + void grab_swap_token(struct mm_struct *mm) { int current_interval; @@ -38,40 +58,69 @@ void grab_swap_token(struct mm_struct *mm) return; /* First come first served */ - if (swap_token_mm == NULL) { - mm->token_priority = mm->token_priority + 2; - swap_token_mm = mm; + if (!swap_token_mm) + goto replace_token; + + if (mm == swap_token_mm) { + mm->token_priority += 2; goto out; } - if (mm != swap_token_mm) { - if (current_interval < mm->last_interval) - mm->token_priority++; - else { - if (likely(mm->token_priority > 0)) - mm->token_priority--; - } - /* Check if we deserve the token */ - if (mm->token_priority > swap_token_mm->token_priority) { - mm->token_priority += 2; - swap_token_mm = mm; - } - } else { - /* Token holder came in again! */ - mm->token_priority += 2; + if (current_interval < mm->last_interval) + mm->token_priority++; + else { + if (likely(mm->token_priority > 0)) + mm->token_priority--; } + /* Check if we deserve the token */ + if (mm->token_priority > swap_token_mm->token_priority) + goto replace_token; + out: mm->faultstamp = global_faults; mm->last_interval = current_interval; spin_unlock(&swap_token_lock); + return; + +replace_token: + mm->token_priority += 2; + swap_token_mm = mm; + swap_token_memcg = swap_token_memcg_from_mm(mm); + goto out; } /* Called on process exit. */ void __put_swap_token(struct mm_struct *mm) { spin_lock(&swap_token_lock); - if (likely(mm == swap_token_mm)) + if (likely(mm == swap_token_mm)) { swap_token_mm = NULL; + swap_token_memcg = NULL; + } spin_unlock(&swap_token_lock); } + +static bool match_memcg(struct mem_cgroup *a, struct mem_cgroup *b) +{ + if (!a) + return true; + if (!b) + return true; + if (a == b) + return true; + return false; +} + +void disable_swap_token(struct mem_cgroup *memcg) +{ + /* memcg reclaim don't disable unrelated mm token. */ + if (match_memcg(memcg, swap_token_memcg)) { + spin_lock(&swap_token_lock); + if (match_memcg(memcg, swap_token_memcg)) { + swap_token_mm = NULL; + swap_token_memcg = NULL; + } + spin_unlock(&swap_token_lock); + } +} diff --git a/mm/vmscan.c b/mm/vmscan.c index faa0a088f9cc..dbe6ea321df4 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2081,7 +2081,7 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, for (priority = DEF_PRIORITY; priority >= 0; priority--) { sc->nr_scanned = 0; if (!priority) - disable_swap_token(); + disable_swap_token(sc->mem_cgroup); total_scanned += shrink_zones(priority, zonelist, sc); /* * Don't shrink slabs when reclaiming memory from @@ -2407,7 +2407,7 @@ loop_again: /* The swap token gets in the way of swapout... */ if (!priority) - disable_swap_token(); + disable_swap_token(NULL); all_zones_ok = 1; balanced = 0; -- cgit v1.2.3 From 83cd81a34357a632509f7491eec81e62e71d65f7 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Wed, 15 Jun 2011 15:08:14 -0700 Subject: vmscan: implement swap token trace This is useful for observing swap token activity. example output: zsh-1845 [000] 598.962716: update_swap_token_priority: mm=ffff88015eaf7700 old_prio=1 new_prio=0 memtoy-1830 [001] 602.033900: update_swap_token_priority: mm=ffff880037a45880 old_prio=947 new_prio=949 memtoy-1830 [000] 602.041509: update_swap_token_priority: mm=ffff880037a45880 old_prio=949 new_prio=951 memtoy-1830 [000] 602.051959: update_swap_token_priority: mm=ffff880037a45880 old_prio=951 new_prio=953 memtoy-1830 [000] 602.052188: update_swap_token_priority: mm=ffff880037a45880 old_prio=953 new_prio=955 memtoy-1830 [001] 602.427184: put_swap_token: token_mm=ffff880037a45880 zsh-1789 [000] 602.427281: replace_swap_token: old_token_mm= (null) old_prio=0 new_token_mm=ffff88015eaf7018 new_prio=2 zsh-1789 [001] 602.433456: update_swap_token_priority: mm=ffff88015eaf7018 old_prio=2 new_prio=4 zsh-1789 [000] 602.437613: update_swap_token_priority: mm=ffff88015eaf7018 old_prio=4 new_prio=6 zsh-1789 [000] 602.443924: update_swap_token_priority: mm=ffff88015eaf7018 old_prio=6 new_prio=8 zsh-1789 [000] 602.451873: update_swap_token_priority: mm=ffff88015eaf7018 old_prio=8 new_prio=10 zsh-1789 [001] 602.462639: update_swap_token_priority: mm=ffff88015eaf7018 old_prio=10 new_prio=12 Signed-off-by: KOSAKI Motohiro Acked-by: Rik van Riel Reviewed-by: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/trace/events/vmscan.h | 77 +++++++++++++++++++++++++++++++++++++++++++ mm/thrash.c | 11 ++++++- 2 files changed, 87 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h index ea422aaa23e1..1798e0cee2a9 100644 --- a/include/trace/events/vmscan.h +++ b/include/trace/events/vmscan.h @@ -6,6 +6,8 @@ #include #include +#include +#include #include "gfpflags.h" #define RECLAIM_WB_ANON 0x0001u @@ -310,6 +312,81 @@ TRACE_EVENT(mm_vmscan_lru_shrink_inactive, show_reclaim_flags(__entry->reclaim_flags)) ); +TRACE_EVENT(replace_swap_token, + TP_PROTO(struct mm_struct *old_mm, + struct mm_struct *new_mm), + + TP_ARGS(old_mm, new_mm), + + TP_STRUCT__entry( + __field(struct mm_struct*, old_mm) + __field(unsigned int, old_prio) + __field(struct mm_struct*, new_mm) + __field(unsigned int, new_prio) + ), + + TP_fast_assign( + __entry->old_mm = old_mm; + __entry->old_prio = old_mm ? old_mm->token_priority : 0; + __entry->new_mm = new_mm; + __entry->new_prio = new_mm->token_priority; + ), + + TP_printk("old_token_mm=%p old_prio=%u new_token_mm=%p new_prio=%u", + __entry->old_mm, __entry->old_prio, + __entry->new_mm, __entry->new_prio) +); + +DECLARE_EVENT_CLASS(put_swap_token_template, + TP_PROTO(struct mm_struct *swap_token_mm), + + TP_ARGS(swap_token_mm), + + TP_STRUCT__entry( + __field(struct mm_struct*, swap_token_mm) + ), + + TP_fast_assign( + __entry->swap_token_mm = swap_token_mm; + ), + + TP_printk("token_mm=%p", __entry->swap_token_mm) +); + +DEFINE_EVENT(put_swap_token_template, put_swap_token, + TP_PROTO(struct mm_struct *swap_token_mm), + TP_ARGS(swap_token_mm) +); + +DEFINE_EVENT_CONDITION(put_swap_token_template, disable_swap_token, + TP_PROTO(struct mm_struct *swap_token_mm), + TP_ARGS(swap_token_mm), + TP_CONDITION(swap_token_mm != NULL) +); + +TRACE_EVENT_CONDITION(update_swap_token_priority, + TP_PROTO(struct mm_struct *mm, + unsigned int old_prio), + + TP_ARGS(mm, old_prio), + + TP_CONDITION(mm->token_priority != old_prio), + + TP_STRUCT__entry( + __field(struct mm_struct*, mm) + __field(unsigned int, old_prio) + __field(unsigned int, new_prio) + ), + + TP_fast_assign( + __entry->mm = mm; + __entry->old_prio = old_prio; + __entry->new_prio = mm->token_priority; + ), + + TP_printk("mm=%p old_prio=%u new_prio=%u", + __entry->mm, __entry->old_prio, __entry->new_prio) +); #endif /* _TRACE_VMSCAN_H */ diff --git a/mm/thrash.c b/mm/thrash.c index 6cdf86511385..17d9e29e4c9c 100644 --- a/mm/thrash.c +++ b/mm/thrash.c @@ -23,6 +23,8 @@ #include #include +#include + static DEFINE_SPINLOCK(swap_token_lock); struct mm_struct *swap_token_mm; struct mem_cgroup *swap_token_memcg; @@ -49,6 +51,7 @@ static struct mem_cgroup *swap_token_memcg_from_mm(struct mm_struct *mm) void grab_swap_token(struct mm_struct *mm) { int current_interval; + unsigned int old_prio = mm->token_priority; global_faults++; @@ -63,7 +66,7 @@ void grab_swap_token(struct mm_struct *mm) if (mm == swap_token_mm) { mm->token_priority += 2; - goto out; + goto update_priority; } if (current_interval < mm->last_interval) @@ -77,6 +80,9 @@ void grab_swap_token(struct mm_struct *mm) if (mm->token_priority > swap_token_mm->token_priority) goto replace_token; +update_priority: + trace_update_swap_token_priority(mm, old_prio); + out: mm->faultstamp = global_faults; mm->last_interval = current_interval; @@ -85,6 +91,7 @@ out: replace_token: mm->token_priority += 2; + trace_replace_swap_token(swap_token_mm, mm); swap_token_mm = mm; swap_token_memcg = swap_token_memcg_from_mm(mm); goto out; @@ -95,6 +102,7 @@ void __put_swap_token(struct mm_struct *mm) { spin_lock(&swap_token_lock); if (likely(mm == swap_token_mm)) { + trace_put_swap_token(swap_token_mm); swap_token_mm = NULL; swap_token_memcg = NULL; } @@ -118,6 +126,7 @@ void disable_swap_token(struct mem_cgroup *memcg) if (match_memcg(memcg, swap_token_memcg)) { spin_lock(&swap_token_lock); if (match_memcg(memcg, swap_token_memcg)) { + trace_disable_swap_token(swap_token_mm); swap_token_mm = NULL; swap_token_memcg = NULL; } -- cgit v1.2.3 From d7911ef30cb7bec52234c2b7a5c275ac8f07905a Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Wed, 15 Jun 2011 15:08:15 -0700 Subject: vmscan: implement swap token priority aging While testing for memcg aware swap token, I observed a swap token was often grabbed an intermittent running process (eg init, auditd) and they never release a token. Why? Some processes (eg init, auditd, audispd) wake up when a process exiting. And swap token can be get first page-in process when a process exiting makes no swap token owner. Thus such above intermittent running process often get a token. And currently, swap token priority is only decreased at page fault path. Then, if the process sleep immediately after to grab swap token, the swap token priority never be decreased. That's obviously undesirable. This patch implement very poor (and lightweight) priority aging. It only be affect to the above corner case and doesn't change swap tendency workload performance (eg multi process qsbench load) Signed-off-by: KOSAKI Motohiro Reviewed-by: Rik van Riel Reviewed-by: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/trace/events/vmscan.h | 20 +++++++++++++------- mm/thrash.c | 11 ++++++++++- 2 files changed, 23 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h index 1798e0cee2a9..b2c33bd955fa 100644 --- a/include/trace/events/vmscan.h +++ b/include/trace/events/vmscan.h @@ -366,9 +366,10 @@ DEFINE_EVENT_CONDITION(put_swap_token_template, disable_swap_token, TRACE_EVENT_CONDITION(update_swap_token_priority, TP_PROTO(struct mm_struct *mm, - unsigned int old_prio), + unsigned int old_prio, + struct mm_struct *swap_token_mm), - TP_ARGS(mm, old_prio), + TP_ARGS(mm, old_prio, swap_token_mm), TP_CONDITION(mm->token_priority != old_prio), @@ -376,16 +377,21 @@ TRACE_EVENT_CONDITION(update_swap_token_priority, __field(struct mm_struct*, mm) __field(unsigned int, old_prio) __field(unsigned int, new_prio) + __field(struct mm_struct*, swap_token_mm) + __field(unsigned int, swap_token_prio) ), TP_fast_assign( - __entry->mm = mm; - __entry->old_prio = old_prio; - __entry->new_prio = mm->token_priority; + __entry->mm = mm; + __entry->old_prio = old_prio; + __entry->new_prio = mm->token_priority; + __entry->swap_token_mm = swap_token_mm; + __entry->swap_token_prio = swap_token_mm ? swap_token_mm->token_priority : 0; ), - TP_printk("mm=%p old_prio=%u new_prio=%u", - __entry->mm, __entry->old_prio, __entry->new_prio) + TP_printk("mm=%p old_prio=%u new_prio=%u swap_token_mm=%p token_prio=%u", + __entry->mm, __entry->old_prio, __entry->new_prio, + __entry->swap_token_mm, __entry->swap_token_prio) ); #endif /* _TRACE_VMSCAN_H */ diff --git a/mm/thrash.c b/mm/thrash.c index 17d9e29e4c9c..fabf2d0f5169 100644 --- a/mm/thrash.c +++ b/mm/thrash.c @@ -25,10 +25,13 @@ #include +#define TOKEN_AGING_INTERVAL (0xFF) + static DEFINE_SPINLOCK(swap_token_lock); struct mm_struct *swap_token_mm; struct mem_cgroup *swap_token_memcg; static unsigned int global_faults; +static unsigned int last_aging; #ifdef CONFIG_CGROUP_MEM_RES_CTLR static struct mem_cgroup *swap_token_memcg_from_mm(struct mm_struct *mm) @@ -64,6 +67,11 @@ void grab_swap_token(struct mm_struct *mm) if (!swap_token_mm) goto replace_token; + if ((global_faults - last_aging) > TOKEN_AGING_INTERVAL) { + swap_token_mm->token_priority /= 2; + last_aging = global_faults; + } + if (mm == swap_token_mm) { mm->token_priority += 2; goto update_priority; @@ -81,7 +89,7 @@ void grab_swap_token(struct mm_struct *mm) goto replace_token; update_priority: - trace_update_swap_token_priority(mm, old_prio); + trace_update_swap_token_priority(mm, old_prio, swap_token_mm); out: mm->faultstamp = global_faults; @@ -94,6 +102,7 @@ replace_token: trace_replace_swap_token(swap_token_mm, mm); swap_token_mm = mm; swap_token_memcg = swap_token_memcg_from_mm(mm); + last_aging = global_faults; goto out; } -- cgit v1.2.3 From ac5622418bbff9cd3dc607aa57dfb4f62a7f2043 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 15 Jun 2011 15:08:17 -0700 Subject: kmsg_dump.h: fix build when CONFIG_PRINTK is disabled Fix when CONFIG_PRINTK is not enabled: include/linux/kmsg_dump.h:56: error: 'EINVAL' undeclared (first use in this function) include/linux/kmsg_dump.h:61: error: 'EINVAL' undeclared (first use in this function) Looks like commit 595dd3d8bf95 ("kmsg_dump: fix build for CONFIG_PRINTK=n") uses EINVAL without having the needed header file(s), but I'm sure that I build tested that patch also. oh well. Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kmsg_dump.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/kmsg_dump.h b/include/linux/kmsg_dump.h index 2a0d7d651dc3..ee0c952188de 100644 --- a/include/linux/kmsg_dump.h +++ b/include/linux/kmsg_dump.h @@ -12,6 +12,7 @@ #ifndef _LINUX_KMSG_DUMP_H #define _LINUX_KMSG_DUMP_H +#include #include enum kmsg_dump_reason { -- cgit v1.2.3 From 32e45ff43eaf5c17f5a82c9ad358d515622c2562 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Wed, 15 Jun 2011 15:08:20 -0700 Subject: mm: increase RECLAIM_DISTANCE to 30 Recently, Robert Mueller reported (http://lkml.org/lkml/2010/9/12/236) that zone_reclaim_mode doesn't work properly on his new NUMA server (Dual Xeon E5520 + Intel S5520UR MB). He is using Cyrus IMAPd and it's built on a very traditional single-process model. * a master process which reads config files and manages the other process * multiple imapd processes, one per connection * multiple pop3d processes, one per connection * multiple lmtpd processes, one per connection * periodical "cleanup" processes. There are thousands of independent processes. The problem is, recent Intel motherboard turn on zone_reclaim_mode by default and traditional prefork model software don't work well on it. Unfortunatelly, such models are still typical even in the 21st century. We can't ignore them. This patch raises the zone_reclaim_mode threshold to 30. 30 doesn't have any specific meaning. but 20 means that one-hop QPI/Hypertransport and such relatively cheap 2-4 socket machine are often used for traditional servers as above. The intention is that these machines don't use zone_reclaim_mode. Note: ia64 and Power have arch specific RECLAIM_DISTANCE definitions. This patch doesn't change such high-end NUMA machine behavior. Dave Hansen said: : I know specifically of pieces of x86 hardware that set the information : in the BIOS to '21' *specifically* so they'll get the zone_reclaim_mode : behavior which that implies. : : They've done performance testing and run very large and scary benchmarks : to make sure that they _want_ this turned on. What this means for them : is that they'll probably be de-optimized, at least on newer versions of : the kernel. : : If you want to do this for particular systems, maybe _that_'s what we : should do. Have a list of specific configurations that need the : defaults overridden either because they're buggy, or they have an : unusual hardware configuration not really reflected in the distance : table. And later said: : The original change in the hardware tables was for the benefit of a : benchmark. Said benchmark isn't going to get run on mainline until the : next batch of enterprise distros drops, at which point the hardware where : this was done will be irrelevant for the benchmark. I'm sure any new : hardware will just set this distance to another yet arbitrary value to : make the kernel do what it wants. :) : : Also, when the hardware got _set_ to this initially, I complained. So, I : guess I'm getting my way now, with this patch. I'm cool with it. Reported-by: Robert Mueller Signed-off-by: KOSAKI Motohiro Acked-by: Christoph Lameter Acked-by: David Rientjes Reviewed-by: KAMEZAWA Hiroyuki Cc: Benjamin Herrenschmidt Cc: "Luck, Tony" Acked-by: Dave Hansen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/topology.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/topology.h b/include/linux/topology.h index b91a40e847d2..fc839bfa7935 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -60,7 +60,7 @@ int arch_update_cpu_topology(void); * (in whatever arch specific measurement units returned by node_distance()) * then switch on zone reclaim on boot. */ -#define RECLAIM_DISTANCE 20 +#define RECLAIM_DISTANCE 30 #endif #ifndef PENALTY_FOR_NODE_WITH_CPUS #define PENALTY_FOR_NODE_WITH_CPUS (1) -- cgit v1.2.3 From ca39599c633fb02aceac31a7e67563612e4fe347 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 15 Jun 2011 15:08:27 -0700 Subject: BUILD_BUG_ON_ZERO: fix sparse breakage BUILD_BUG_ON_ZERO and BUILD_BUG_ON_NULL must return values, even in the CHECKER case otherwise various users of it become syntactically invalid. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kernel.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index fb0e7329fee1..953352a88336 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -671,8 +671,8 @@ struct sysinfo { #ifdef __CHECKER__ #define BUILD_BUG_ON_NOT_POWER_OF_2(n) -#define BUILD_BUG_ON_ZERO(e) -#define BUILD_BUG_ON_NULL(e) +#define BUILD_BUG_ON_ZERO(e) (0) +#define BUILD_BUG_ON_NULL(e) ((void*)0) #define BUILD_BUG_ON(condition) #else /* __CHECKER__ */ -- cgit v1.2.3 From bd5dc17be87b3a3073d50b23802647db3ae3fa8e Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 15 Jun 2011 15:08:28 -0700 Subject: uts: make default hostname configurable, rather than always using "(none)" The "hostname" tool falls back to setting the hostname to "localhost" if /etc/hostname does not exist. Distribution init scripts have the same fallback. However, if userspace never calls sethostname, such as when booting with init=/bin/sh, or otherwise booting a minimal system without the usual init scripts, the default hostname of "(none)" remains, unhelpfully appearing in various places such as prompts ("root@(none):~#") and logs. Furthermore, "(none)" doesn't typically resolve to anything useful. Make the default hostname configurable. This removes the need for the standard fallback, provides a useful default for systems that never call sethostname, and makes minimal systems that much more useful with less configuration. Distributions could choose to use "localhost" here to avoid the fallback, while embedded systems may wish to use a specific target hostname. Signed-off-by: Josh Triplett Acked-by: Linus Torvalds Acked-by: David Miller Cc: Serge Hallyn Cc: Kel Modderman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/uts.h | 2 +- init/Kconfig | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/uts.h b/include/linux/uts.h index 73eb1ed36ec4..6ddbd86377de 100644 --- a/include/linux/uts.h +++ b/include/linux/uts.h @@ -9,7 +9,7 @@ #endif #ifndef UTS_NODENAME -#define UTS_NODENAME "(none)" /* set by sethostname() */ +#define UTS_NODENAME CONFIG_DEFAULT_HOSTNAME /* set by sethostname() */ #endif #ifndef UTS_DOMAINNAME diff --git a/init/Kconfig b/init/Kconfig index ebafac4231ee..e0a22e42d39a 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -204,6 +204,15 @@ config KERNEL_LZO endchoice +config DEFAULT_HOSTNAME + string "Default hostname" + default "(none)" + help + This option determines the default system hostname before userspace + calls sethostname(2). The kernel traditionally uses "(none)" here, + but you may wish to use a different default here to make a minimal + system more usable with less configuration. + config SWAP bool "Support for paging of anonymous memory (swap)" depends on MMU && BLOCK -- cgit v1.2.3 From 49b24d6b41c576ba43153fc94695f871cce139a5 Mon Sep 17 00:00:00 2001 From: Nicolas Kaiser Date: Wed, 15 Jun 2011 15:08:34 -0700 Subject: include/asm-generic/pgtable.h: fix unbalanced parenthesis Signed-off-by: Nicolas Kaiser Reviewed-by: Andrea Arcangeli Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/pgtable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index e9b8e5926bef..76bff2bff15e 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -88,7 +88,7 @@ static inline pmd_t pmdp_get_and_clear(struct mm_struct *mm, pmd_t pmd = *pmdp; pmd_clear(mm, address, pmdp); return pmd; -}) +} #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ #endif -- cgit v1.2.3 From 4a9a8b71e12d41abb71c4e741bff524f016cfef4 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 14 Jun 2011 06:13:55 +0000 Subject: drm/radeon: workaround a hw bug on some radeon chipsets with all-0 EDIDs. Some RS690 chipsets seem to end up with floating connectors, either a DVI connector isn't actually populated, or an add-in HDMI card is available but not installed. In this case we seem to get a NULL byte response for each byte of the i2c transaction, so we detect this case and if we see it we don't do anymore DDC transactions on this connector. I've tested this on my RS690 without the HDMI card installed and it seems to work fine. Signed-off-by: Dave Airlie Reviewed-by: Alex Deucher --- drivers/gpu/drm/drm_edid.c | 15 +++++++++++++++ drivers/gpu/drm/radeon/radeon_connectors.c | 7 +++++++ include/drm/drm_crtc.h | 2 ++ 3 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 3618d29c79a2..09292193dafe 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -258,6 +258,17 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, return ret == 2 ? 0 : -1; } +static bool drm_edid_is_zero(u8 *in_edid, int length) +{ + int i; + u32 *raw_edid = (u32 *)in_edid; + + for (i = 0; i < length / 4; i++) + if (*(raw_edid + i) != 0) + return false; + return true; +} + static u8 * drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) { @@ -273,6 +284,10 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) goto out; if (drm_edid_block_valid(block)) break; + if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) { + connector->null_edid_counter++; + goto carp; + } } if (i == 4) goto carp; diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 9c2929c7e79f..c04e18ee8a87 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -836,6 +836,13 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) if (!radeon_connector->edid) { DRM_ERROR("%s: probed a monitor but no|invalid EDID\n", drm_get_connector_name(connector)); + /* rs690 seems to have a problem with connectors not existing and always + * return a block of 0's. If we see this just stop polling on this output */ + if ((rdev->family == CHIP_RS690 || rdev->family == CHIP_RS740) && radeon_connector->base.null_edid_counter) { + ret = connector_status_disconnected; + DRM_ERROR("%s: detected RS690 floating bus bug, stopping ddc detect\n", drm_get_connector_name(connector)); + radeon_connector->ddc_bus = NULL; + } } else { radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 9573e0ce3120..33d12f87f0e0 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -520,6 +520,8 @@ struct drm_connector { uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER]; uint32_t force_encoder_id; struct drm_encoder *encoder; /* currently active encoder */ + + int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */ }; /** -- cgit v1.2.3 From c001fb72a7b705f902bdfdd05b5d2408efe6f848 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 14 Jun 2011 17:05:11 -0700 Subject: gpio: add GPIOF_ values regardless on kconfig settings Make GPIOF_ defined values available even when GPIOLIB nor GENERIC_GPIO is enabled by moving them to . Fixes these build errors in linux-next: sound/soc/codecs/ak4641.c:524: error: 'GPIOF_OUT_INIT_LOW' undeclared (first use in this function) sound/soc/codecs/wm8915.c:2921: error: 'GPIOF_OUT_INIT_LOW' undeclared (first use in this function) Signed-off-by: Randy Dunlap Signed-off-by: Grant Likely --- include/asm-generic/gpio.h | 10 ---------- include/linux/gpio.h | 11 +++++++++++ 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index fcdcb5d5c995..d494001b1226 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -170,16 +170,6 @@ extern int __gpio_cansleep(unsigned gpio); extern int __gpio_to_irq(unsigned gpio); -#define GPIOF_DIR_OUT (0 << 0) -#define GPIOF_DIR_IN (1 << 0) - -#define GPIOF_INIT_LOW (0 << 1) -#define GPIOF_INIT_HIGH (1 << 1) - -#define GPIOF_IN (GPIOF_DIR_IN) -#define GPIOF_OUT_INIT_LOW (GPIOF_DIR_OUT | GPIOF_INIT_LOW) -#define GPIOF_OUT_INIT_HIGH (GPIOF_DIR_OUT | GPIOF_INIT_HIGH) - /** * struct gpio - a structure describing a GPIO with configuration * @gpio: the GPIO number diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 32d47e710661..17b5a0d80e42 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -3,6 +3,17 @@ /* see Documentation/gpio.txt */ +/* make these flag values available regardless of GPIO kconfig options */ +#define GPIOF_DIR_OUT (0 << 0) +#define GPIOF_DIR_IN (1 << 0) + +#define GPIOF_INIT_LOW (0 << 1) +#define GPIOF_INIT_HIGH (1 << 1) + +#define GPIOF_IN (GPIOF_DIR_IN) +#define GPIOF_OUT_INIT_LOW (GPIOF_DIR_OUT | GPIOF_INIT_LOW) +#define GPIOF_OUT_INIT_HIGH (GPIOF_DIR_OUT | GPIOF_INIT_HIGH) + #ifdef CONFIG_GENERIC_GPIO #include -- cgit v1.2.3 From 42c1edd345c8412d96e7a362ee06feb7be73bb6c Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Thu, 16 Jun 2011 17:29:22 +0200 Subject: netfilter: nf_nat: avoid double seq_adjust for loopback Avoid double seq adjustment for loopback traffic because it causes silent repetition of TCP data. One example is passive FTP with DNAT rule and difference in the length of IP addresses. This patch adds check if packet is sent and received via loopback device. As the same conntrack is used both for outgoing and incoming direction, we restrict seq adjustment to happen only in POSTROUTING. Signed-off-by: Julian Anastasov Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack.h | 6 ++++++ net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index c7c42e7acc31..5d4f8e586e32 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -307,6 +307,12 @@ static inline int nf_ct_is_untracked(const struct nf_conn *ct) return test_bit(IPS_UNTRACKED_BIT, &ct->status); } +/* Packet is received from loopback */ +static inline bool nf_is_loopback_packet(const struct sk_buff *skb) +{ + return skb->dev && skb->skb_iif && skb->dev->flags & IFF_LOOPBACK; +} + extern int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp); extern unsigned int nf_conntrack_htable_size; extern unsigned int nf_conntrack_max; diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index db10075dd88e..de9da21113a1 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -121,7 +121,9 @@ static unsigned int ipv4_confirm(unsigned int hooknum, return ret; } - if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { + /* adjust seqs for loopback traffic only in outgoing direction */ + if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && + !nf_is_loopback_packet(skb)) { typeof(nf_nat_seq_adjust_hook) seq_adjust; seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook); -- cgit v1.2.3 From 3192b920bf7d0c528ab54e7d3689f44055316a37 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Tue, 14 Jun 2011 16:16:36 -0500 Subject: slab, slub, slob: Unify alignment definition Every slab has its on alignment definition in include/linux/sl?b_def.h. Extract those and define a common set in include/linux/slab.h. SLOB: As notes sometimes we need double word alignment on 32 bit. This gives all structures allocated by SLOB a unsigned long long alignment like the others do. SLAB: If ARCH_SLAB_MINALIGN is not set SLAB would set ARCH_SLAB_MINALIGN to zero meaning no alignment at all. Give it the default unsigned long long alignment. Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 10 ++++++++++ include/linux/slab_def.h | 26 -------------------------- include/linux/slob_def.h | 10 ---------- include/linux/slub_def.h | 10 ---------- 4 files changed, 10 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index ad4dd1c8d30a..646a639a4aae 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -133,6 +133,16 @@ unsigned int kmem_cache_size(struct kmem_cache *); #define KMALLOC_MAX_SIZE (1UL << KMALLOC_SHIFT_HIGH) #define KMALLOC_MAX_ORDER (KMALLOC_SHIFT_HIGH - PAGE_SHIFT) +#ifdef ARCH_DMA_MINALIGN +#define ARCH_KMALLOC_MINALIGN ARCH_DMA_MINALIGN +#else +#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long) +#endif + +#ifndef ARCH_SLAB_MINALIGN +#define ARCH_SLAB_MINALIGN __alignof__(unsigned long long) +#endif + /* * Common kmalloc functions provided by all allocators */ diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index 83203ae9390b..d7f63112f63c 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -17,32 +17,6 @@ #include -/* - * Enforce a minimum alignment for the kmalloc caches. - * Usually, the kmalloc caches are cache_line_size() aligned, except when - * DEBUG and FORCED_DEBUG are enabled, then they are BYTES_PER_WORD aligned. - * Some archs want to perform DMA into kmalloc caches and need a guaranteed - * alignment larger than the alignment of a 64-bit integer. - * ARCH_KMALLOC_MINALIGN allows that. - * Note that increasing this value may disable some debug features. - */ -#ifdef ARCH_DMA_MINALIGN -#define ARCH_KMALLOC_MINALIGN ARCH_DMA_MINALIGN -#else -#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long) -#endif - -#ifndef ARCH_SLAB_MINALIGN -/* - * Enforce a minimum alignment for all caches. - * Intended for archs that get misalignment faults even for BYTES_PER_WORD - * aligned buffers. Includes ARCH_KMALLOC_MINALIGN. - * If possible: Do not enable this flag for CONFIG_DEBUG_SLAB, it disables - * some debug features. - */ -#define ARCH_SLAB_MINALIGN 0 -#endif - /* * struct kmem_cache * diff --git a/include/linux/slob_def.h b/include/linux/slob_def.h index 4382db09df4f..0ec00b39d006 100644 --- a/include/linux/slob_def.h +++ b/include/linux/slob_def.h @@ -1,16 +1,6 @@ #ifndef __LINUX_SLOB_DEF_H #define __LINUX_SLOB_DEF_H -#ifdef ARCH_DMA_MINALIGN -#define ARCH_KMALLOC_MINALIGN ARCH_DMA_MINALIGN -#else -#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long) -#endif - -#ifndef ARCH_SLAB_MINALIGN -#define ARCH_SLAB_MINALIGN __alignof__(unsigned long) -#endif - void *kmem_cache_alloc_node(struct kmem_cache *, gfp_t flags, int node); static __always_inline void *kmem_cache_alloc(struct kmem_cache *cachep, diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index c8668d161dd8..fd4fdc72bc8c 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -113,16 +113,6 @@ struct kmem_cache { #define KMALLOC_SHIFT_LOW ilog2(KMALLOC_MIN_SIZE) -#ifdef ARCH_DMA_MINALIGN -#define ARCH_KMALLOC_MINALIGN ARCH_DMA_MINALIGN -#else -#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long) -#endif - -#ifndef ARCH_SLAB_MINALIGN -#define ARCH_SLAB_MINALIGN __alignof__(unsigned long long) -#endif - /* * Maximum kmalloc object size handled by SLUB. Larger object allocations * are passed through to the page allocator. The page allocator "fastpath" -- cgit v1.2.3 From 5416219e5ca4504ea80d662fdda7337e52e86ee5 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 16 Jun 2011 18:40:55 +0200 Subject: netfilter: ipset: timeout can be modified for already added elements When an element to a set with timeout added, one can change the timeout by "readding" the element with the "-exist" flag. That means the timeout value is reset to the specified one (or to the default from the set specification if the "timeout n" option is not used). Example ipset add foo 1.2.3.4 timeout 10 ipset add foo 1.2.3.4 timeout 600 -exist Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set.h | 3 +- include/linux/netfilter/ipset/ip_set_ahash.h | 15 ++--- net/netfilter/ipset/ip_set_bitmap_ip.c | 20 +++--- net/netfilter/ipset/ip_set_bitmap_ipmac.c | 21 ++++--- net/netfilter/ipset/ip_set_bitmap_port.c | 20 +++--- net/netfilter/ipset/ip_set_hash_ip.c | 10 +-- net/netfilter/ipset/ip_set_hash_ipport.c | 12 ++-- net/netfilter/ipset/ip_set_hash_ipportip.c | 12 ++-- net/netfilter/ipset/ip_set_hash_ipportnet.c | 12 ++-- net/netfilter/ipset/ip_set_hash_net.c | 8 +-- net/netfilter/ipset/ip_set_hash_netport.c | 12 ++-- net/netfilter/ipset/ip_set_list_set.c | 92 +++++++++++++++++++--------- 12 files changed, 136 insertions(+), 101 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 5a262e3ae715..277b7fbc7fb2 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -214,7 +214,8 @@ enum ip_set_feature { struct ip_set; -typedef int (*ipset_adtfn)(struct ip_set *set, void *value, u32 timeout); +typedef int (*ipset_adtfn)(struct ip_set *set, void *value, + u32 timeout, u32 flags); /* Set type, variant-specific part */ struct ip_set_type_variant { diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index ac3c822eb39a..36cf4dc703bb 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -349,7 +349,7 @@ retry: /* Add an element to a hash and update the internal counters when succeeded, * otherwise report the proper error code. */ static int -type_pf_add(struct ip_set *set, void *value, u32 timeout) +type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct ip_set_hash *h = set->data; struct htable *t; @@ -388,7 +388,7 @@ out: * and free up space if possible. */ static int -type_pf_del(struct ip_set *set, void *value, u32 timeout) +type_pf_del(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct ip_set_hash *h = set->data; struct htable *t = h->table; @@ -463,7 +463,7 @@ type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) /* Test whether the element is added to the set */ static int -type_pf_test(struct ip_set *set, void *value, u32 timeout) +type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct ip_set_hash *h = set->data; struct htable *t = h->table; @@ -776,7 +776,7 @@ retry: } static int -type_pf_tadd(struct ip_set *set, void *value, u32 timeout) +type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct ip_set_hash *h = set->data; struct htable *t = h->table; @@ -784,6 +784,7 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout) struct hbucket *n; struct type_pf_elem *data; int ret = 0, i, j = AHASH_MAX_SIZE + 1; + bool flag_exist = flags & IPSET_FLAG_EXIST; u32 key; if (h->elements >= h->maxelem) @@ -799,7 +800,7 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout) for (i = 0; i < n->pos; i++) { data = ahash_tdata(n, i); if (type_pf_data_equal(data, d)) { - if (type_pf_data_expired(data)) + if (type_pf_data_expired(data) || flag_exist) j = i; else { ret = -IPSET_ERR_EXIST; @@ -833,7 +834,7 @@ out: } static int -type_pf_tdel(struct ip_set *set, void *value, u32 timeout) +type_pf_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct ip_set_hash *h = set->data; struct htable *t = h->table; @@ -905,7 +906,7 @@ type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) #endif static int -type_pf_ttest(struct ip_set *set, void *value, u32 timeout) +type_pf_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct ip_set_hash *h = set->data; struct htable *t = h->table; diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index ba2d16607f48..85b1cdf0a4b8 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -54,7 +54,7 @@ ip_to_id(const struct bitmap_ip *m, u32 ip) } static int -bitmap_ip_test(struct ip_set *set, void *value, u32 timeout) +bitmap_ip_test(struct ip_set *set, void *value, u32 timeout, u32 flags) { const struct bitmap_ip *map = set->data; u16 id = *(u16 *)value; @@ -63,7 +63,7 @@ bitmap_ip_test(struct ip_set *set, void *value, u32 timeout) } static int -bitmap_ip_add(struct ip_set *set, void *value, u32 timeout) +bitmap_ip_add(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct bitmap_ip *map = set->data; u16 id = *(u16 *)value; @@ -75,7 +75,7 @@ bitmap_ip_add(struct ip_set *set, void *value, u32 timeout) } static int -bitmap_ip_del(struct ip_set *set, void *value, u32 timeout) +bitmap_ip_del(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct bitmap_ip *map = set->data; u16 id = *(u16 *)value; @@ -131,7 +131,7 @@ nla_put_failure: /* Timeout variant */ static int -bitmap_ip_ttest(struct ip_set *set, void *value, u32 timeout) +bitmap_ip_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags) { const struct bitmap_ip *map = set->data; const unsigned long *members = map->members; @@ -141,13 +141,13 @@ bitmap_ip_ttest(struct ip_set *set, void *value, u32 timeout) } static int -bitmap_ip_tadd(struct ip_set *set, void *value, u32 timeout) +bitmap_ip_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct bitmap_ip *map = set->data; unsigned long *members = map->members; u16 id = *(u16 *)value; - if (ip_set_timeout_test(members[id])) + if (ip_set_timeout_test(members[id]) && !(flags & IPSET_FLAG_EXIST)) return -IPSET_ERR_EXIST; members[id] = ip_set_timeout_set(timeout); @@ -156,7 +156,7 @@ bitmap_ip_tadd(struct ip_set *set, void *value, u32 timeout) } static int -bitmap_ip_tdel(struct ip_set *set, void *value, u32 timeout) +bitmap_ip_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct bitmap_ip *map = set->data; unsigned long *members = map->members; @@ -231,7 +231,7 @@ bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb, ip = ip_to_id(map, ip); - return adtfn(set, &ip, map->timeout); + return adtfn(set, &ip, map->timeout, flags); } static int @@ -266,7 +266,7 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[], if (adt == IPSET_TEST) { id = ip_to_id(map, ip); - return adtfn(set, &id, timeout); + return adtfn(set, &id, timeout, flags); } if (tb[IPSET_ATTR_IP_TO]) { @@ -293,7 +293,7 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[], for (; !before(ip_to, ip); ip += map->hosts) { id = ip_to_id(map, ip); - ret = adtfn(set, &id, timeout); + ret = adtfn(set, &id, timeout, flags); if (ret && !ip_set_eexist(ret, flags)) return ret; diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index a274300b6a56..913a461382e4 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -99,7 +99,7 @@ bitmap_ipmac_exist(const struct ipmac_telem *elem) /* Base variant */ static int -bitmap_ipmac_test(struct ip_set *set, void *value, u32 timeout) +bitmap_ipmac_test(struct ip_set *set, void *value, u32 timeout, u32 flags) { const struct bitmap_ipmac *map = set->data; const struct ipmac *data = value; @@ -117,7 +117,7 @@ bitmap_ipmac_test(struct ip_set *set, void *value, u32 timeout) } static int -bitmap_ipmac_add(struct ip_set *set, void *value, u32 timeout) +bitmap_ipmac_add(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct bitmap_ipmac *map = set->data; const struct ipmac *data = value; @@ -146,7 +146,7 @@ bitmap_ipmac_add(struct ip_set *set, void *value, u32 timeout) } static int -bitmap_ipmac_del(struct ip_set *set, void *value, u32 timeout) +bitmap_ipmac_del(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct bitmap_ipmac *map = set->data; const struct ipmac *data = value; @@ -212,7 +212,7 @@ nla_put_failure: /* Timeout variant */ static int -bitmap_ipmac_ttest(struct ip_set *set, void *value, u32 timeout) +bitmap_ipmac_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags) { const struct bitmap_ipmac *map = set->data; const struct ipmac *data = value; @@ -231,15 +231,16 @@ bitmap_ipmac_ttest(struct ip_set *set, void *value, u32 timeout) } static int -bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout) +bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct bitmap_ipmac *map = set->data; const struct ipmac *data = value; struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id); + bool flag_exist = flags & IPSET_FLAG_EXIST; switch (elem->match) { case MAC_UNSET: - if (!data->ether) + if (!(data->ether || flag_exist)) /* Already added without ethernet address */ return -IPSET_ERR_EXIST; /* Fill the MAC address and activate the timer */ @@ -251,7 +252,7 @@ bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout) elem->timeout = ip_set_timeout_set(timeout); break; case MAC_FILLED: - if (!bitmap_expired(map, data->id)) + if (!(bitmap_expired(map, data->id) || flag_exist)) return -IPSET_ERR_EXIST; /* Fall through */ case MAC_EMPTY: @@ -273,7 +274,7 @@ bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout) } static int -bitmap_ipmac_tdel(struct ip_set *set, void *value, u32 timeout) +bitmap_ipmac_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct bitmap_ipmac *map = set->data; const struct ipmac *data = value; @@ -359,7 +360,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, data.id -= map->first_ip; data.ether = eth_hdr(skb)->h_source; - return adtfn(set, &data, map->timeout); + return adtfn(set, &data, map->timeout, flags); } static int @@ -399,7 +400,7 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[], data.id -= map->first_ip; - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index 6b38eb8f6ed8..a3935eef76fc 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c @@ -40,7 +40,7 @@ struct bitmap_port { /* Base variant */ static int -bitmap_port_test(struct ip_set *set, void *value, u32 timeout) +bitmap_port_test(struct ip_set *set, void *value, u32 timeout, u32 flags) { const struct bitmap_port *map = set->data; u16 id = *(u16 *)value; @@ -49,7 +49,7 @@ bitmap_port_test(struct ip_set *set, void *value, u32 timeout) } static int -bitmap_port_add(struct ip_set *set, void *value, u32 timeout) +bitmap_port_add(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct bitmap_port *map = set->data; u16 id = *(u16 *)value; @@ -61,7 +61,7 @@ bitmap_port_add(struct ip_set *set, void *value, u32 timeout) } static int -bitmap_port_del(struct ip_set *set, void *value, u32 timeout) +bitmap_port_del(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct bitmap_port *map = set->data; u16 id = *(u16 *)value; @@ -119,7 +119,7 @@ nla_put_failure: /* Timeout variant */ static int -bitmap_port_ttest(struct ip_set *set, void *value, u32 timeout) +bitmap_port_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags) { const struct bitmap_port *map = set->data; const unsigned long *members = map->members; @@ -129,13 +129,13 @@ bitmap_port_ttest(struct ip_set *set, void *value, u32 timeout) } static int -bitmap_port_tadd(struct ip_set *set, void *value, u32 timeout) +bitmap_port_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct bitmap_port *map = set->data; unsigned long *members = map->members; u16 id = *(u16 *)value; - if (ip_set_timeout_test(members[id])) + if (ip_set_timeout_test(members[id]) && !(flags & IPSET_FLAG_EXIST)) return -IPSET_ERR_EXIST; members[id] = ip_set_timeout_set(timeout); @@ -144,7 +144,7 @@ bitmap_port_tadd(struct ip_set *set, void *value, u32 timeout) } static int -bitmap_port_tdel(struct ip_set *set, void *value, u32 timeout) +bitmap_port_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags) { struct bitmap_port *map = set->data; unsigned long *members = map->members; @@ -225,7 +225,7 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, port -= map->first_port; - return adtfn(set, &port, map->timeout); + return adtfn(set, &port, map->timeout, flags); } static int @@ -259,7 +259,7 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[], if (adt == IPSET_TEST) { id = port - map->first_port; - return adtfn(set, &id, timeout); + return adtfn(set, &id, timeout, flags); } if (tb[IPSET_ATTR_PORT_TO]) { @@ -277,7 +277,7 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[], for (; port <= port_to; port++) { id = port - map->first_port; - ret = adtfn(set, &id, timeout); + ret = adtfn(set, &id, timeout, flags); if (ret && !ip_set_eexist(ret, flags)) return ret; diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index 43bcce200129..368302001120 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -121,7 +121,7 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, if (ip == 0) return -EINVAL; - return adtfn(set, &ip, h->timeout); + return adtfn(set, &ip, h->timeout, flags); } static int @@ -157,7 +157,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], nip = htonl(ip); if (nip == 0) return -IPSET_ERR_HASH_ELEM; - return adtfn(set, &nip, timeout); + return adtfn(set, &nip, timeout, flags); } if (tb[IPSET_ATTR_IP_TO]) { @@ -182,7 +182,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], nip = htonl(ip); if (nip == 0) return -IPSET_ERR_HASH_ELEM; - ret = adtfn(set, &nip, timeout); + ret = adtfn(set, &nip, timeout, flags); if (ret && !ip_set_eexist(ret, flags)) return ret; @@ -294,7 +294,7 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, if (ipv6_addr_any(&ip.in6)) return -EINVAL; - return adtfn(set, &ip, h->timeout); + return adtfn(set, &ip, h->timeout, flags); } static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = { @@ -336,7 +336,7 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ret = adtfn(set, &ip, timeout); + ret = adtfn(set, &ip, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index 14281b6b8074..65c2ff4b27aa 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -138,7 +138,7 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); - return adtfn(set, &data, h->timeout); + return adtfn(set, &data, h->timeout, flags); } static int @@ -192,7 +192,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_PORT_TO])) { - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -224,7 +224,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], for (p = port; p <= port_to; p++) { data.ip = htonl(ip); data.port = htons(p); - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); if (ret && !ip_set_eexist(ret, flags)) return ret; @@ -342,7 +342,7 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); - return adtfn(set, &data, h->timeout); + return adtfn(set, &data, h->timeout, flags); } static int @@ -396,7 +396,7 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[], } if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -407,7 +407,7 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[], for (; port <= port_to; port++) { data.port = htons(port); - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); if (ret && !ip_set_eexist(ret, flags)) return ret; diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index 401c8a2531db..670e5e4a1232 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -142,7 +142,7 @@ hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); - return adtfn(set, &data, h->timeout); + return adtfn(set, &data, h->timeout, flags); } static int @@ -200,7 +200,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_PORT_TO])) { - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -232,7 +232,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], for (p = port; p <= port_to; p++) { data.ip = htonl(ip); data.port = htons(p); - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); if (ret && !ip_set_eexist(ret, flags)) return ret; @@ -356,7 +356,7 @@ hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); - return adtfn(set, &data, h->timeout); + return adtfn(set, &data, h->timeout, flags); } static int @@ -414,7 +414,7 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[], } if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -425,7 +425,7 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[], for (; port <= port_to; port++) { data.port = htons(port); - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); if (ret && !ip_set_eexist(ret, flags)) return ret; diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 4743e5402522..4bb365c9f3db 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -162,7 +162,7 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); data.ip2 &= ip_set_netmask(data.cidr); - return adtfn(set, &data, h->timeout); + return adtfn(set, &data, h->timeout, flags); } static int @@ -228,7 +228,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_PORT_TO])) { - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -260,7 +260,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], for (p = port; p <= port_to; p++) { data.ip = htonl(ip); data.port = htons(p); - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); if (ret && !ip_set_eexist(ret, flags)) return ret; @@ -410,7 +410,7 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); ip6_netmask(&data.ip2, data.cidr); - return adtfn(set, &data, h->timeout); + return adtfn(set, &data, h->timeout, flags); } static int @@ -476,7 +476,7 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], } if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -487,7 +487,7 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], for (; port <= port_to; port++) { data.port = htons(port); - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); if (ret && !ip_set_eexist(ret, flags)) return ret; diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index c4db202b7da4..440b38f9fe3b 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -141,7 +141,7 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); data.ip &= ip_set_netmask(data.cidr); - return adtfn(set, &data, h->timeout); + return adtfn(set, &data, h->timeout, flags); } static int @@ -179,7 +179,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -306,7 +306,7 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6_netmask(&data.ip, data.cidr); - return adtfn(set, &data, h->timeout); + return adtfn(set, &data, h->timeout, flags); } static int @@ -344,7 +344,7 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index d2a40362dd3a..2d31291ba83e 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -158,7 +158,7 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); data.ip &= ip_set_netmask(data.cidr); - return adtfn(set, &data, h->timeout); + return adtfn(set, &data, h->timeout, flags); } static int @@ -216,7 +216,7 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], } if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -227,7 +227,7 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], for (; port <= port_to; port++) { data.port = htons(port); - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); if (ret && !ip_set_eexist(ret, flags)) return ret; @@ -371,7 +371,7 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6_netmask(&data.ip, data.cidr); - return adtfn(set, &data, h->timeout); + return adtfn(set, &data, h->timeout, flags); } static int @@ -429,7 +429,7 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], } if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -440,7 +440,7 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], for (; port <= port_to; port++) { data.port = htons(port); - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); if (ret && !ip_set_eexist(ret, flags)) return ret; diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index e9159e99fc4b..a0290ffad355 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -109,15 +109,28 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb, } static bool -next_id_eq(const struct list_set *map, u32 i, ip_set_id_t id) +id_eq(const struct list_set *map, u32 i, ip_set_id_t id) { const struct set_elem *elem; - if (i + 1 < map->size) { - elem = list_set_elem(map, i + 1); + if (i < map->size) { + elem = list_set_elem(map, i); + return elem->id == id; + } + + return 0; +} + +static bool +id_eq_timeout(const struct list_set *map, u32 i, ip_set_id_t id) +{ + const struct set_elem *elem; + + if (i < map->size) { + elem = list_set_elem(map, i); return !!(elem->id == id && !(with_timeout(map->timeout) && - list_set_expired(map, i + 1))); + list_set_expired(map, i))); } return 0; @@ -190,12 +203,26 @@ list_set_del(struct list_set *map, u32 i) return 0; } +static void +cleanup_entries(struct list_set *map) +{ + struct set_telem *e; + u32 i; + + for (i = 0; i < map->size; i++) { + e = list_set_telem(map, i); + if (e->id != IPSET_INVALID_ID && list_set_expired(map, i)) + list_set_del(map, i); + } +} + static int list_set_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags) { struct list_set *map = set->data; bool with_timeout = with_timeout(map->timeout); + bool flag_exist = flags & IPSET_FLAG_EXIST; int before = 0; u32 timeout = map->timeout; ip_set_id_t id, refid = IPSET_INVALID_ID; @@ -248,6 +275,8 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], } timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } + if (with_timeout && adt != IPSET_TEST) + cleanup_entries(map); switch (adt) { case IPSET_TEST: @@ -259,22 +288,37 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], else if (with_timeout && list_set_expired(map, i)) continue; else if (before > 0 && elem->id == id) - ret = next_id_eq(map, i, refid); + ret = id_eq_timeout(map, i + 1, refid); else if (before < 0 && elem->id == refid) - ret = next_id_eq(map, i, id); + ret = id_eq_timeout(map, i + 1, id); else if (before == 0 && elem->id == id) ret = 1; } break; case IPSET_ADD: - for (i = 0; i < map->size && !ret; i++) { + for (i = 0; i < map->size; i++) { elem = list_set_elem(map, i); - if (elem->id == id && - !(with_timeout && list_set_expired(map, i))) + if (elem->id != id) + continue; + if (!(with_timeout && flag_exist)) { ret = -IPSET_ERR_EXIST; + goto finish; + } else { + struct set_telem *e = list_set_telem(map, i); + + if ((before > 1 && + !id_eq(map, i + 1, refid)) || + (before < 0 && + (i == 0 || !id_eq(map, i - 1, refid)))) { + ret = -IPSET_ERR_EXIST; + goto finish; + } + e->timeout = ip_set_timeout_set(timeout); + ip_set_put_byindex(id); + ret = 0; + goto finish; + } } - if (ret == -IPSET_ERR_EXIST) - break; ret = -IPSET_ERR_LIST_FULL; for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { elem = list_set_elem(map, i); @@ -283,9 +327,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], : list_set_add(map, i, id, timeout); else if (elem->id != refid) continue; - else if (with_timeout && list_set_expired(map, i)) - ret = -IPSET_ERR_REF_EXIST; - else if (before) + else if (before > 0) ret = list_set_add(map, i, id, timeout); else if (i + 1 < map->size) ret = list_set_add(map, i + 1, id, timeout); @@ -299,16 +341,12 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], ret = before != 0 ? -IPSET_ERR_REF_EXIST : -IPSET_ERR_EXIST; break; - } else if (with_timeout && list_set_expired(map, i)) - continue; - else if (elem->id == id && - (before == 0 || - (before > 0 && - next_id_eq(map, i, refid)))) + } else if (elem->id == id && + (before == 0 || + (before > 0 && id_eq(map, i + 1, refid)))) ret = list_set_del(map, i); - else if (before < 0 && - elem->id == refid && - next_id_eq(map, i, id)) + else if (elem->id == refid && + before < 0 && id_eq(map, i + 1, id)) ret = list_set_del(map, i + 1); } break; @@ -454,15 +492,9 @@ list_set_gc(unsigned long ul_set) { struct ip_set *set = (struct ip_set *) ul_set; struct list_set *map = set->data; - struct set_telem *e; - u32 i; write_lock_bh(&set->lock); - for (i = 0; i < map->size; i++) { - e = list_set_telem(map, i); - if (e->id != IPSET_INVALID_ID && list_set_expired(map, i)) - list_set_del(map, i); - } + cleanup_entries(map); write_unlock_bh(&set->lock); map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; -- cgit v1.2.3 From ac8cc925d35fc5a05da2bd097e602f20de2478a4 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 16 Jun 2011 18:42:40 +0200 Subject: netfilter: ipset: options and flags support added to the kernel API The support makes possible to specify the timeout value for the SET target and a flag to reset the timeout for already existing entries. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set.h | 18 ++- include/linux/netfilter/ipset/ip_set_ahash.h | 2 +- include/linux/netfilter/ipset/ip_set_timeout.h | 3 + include/linux/netfilter/xt_set.h | 15 ++- net/netfilter/ipset/ip_set_bitmap_ip.c | 6 +- net/netfilter/ipset/ip_set_bitmap_ipmac.c | 8 +- net/netfilter/ipset/ip_set_bitmap_port.c | 7 +- net/netfilter/ipset/ip_set_core.c | 26 ++--- net/netfilter/ipset/ip_set_hash_ip.c | 12 +- net/netfilter/ipset/ip_set_hash_ipport.c | 16 +-- net/netfilter/ipset/ip_set_hash_ipportip.c | 20 ++-- net/netfilter/ipset/ip_set_hash_ipportnet.c | 20 ++-- net/netfilter/ipset/ip_set_hash_net.c | 12 +- net/netfilter/ipset/ip_set_hash_netport.c | 16 +-- net/netfilter/ipset/ip_set_list_set.c | 8 +- net/netfilter/xt_set.c | 151 ++++++++++++++++--------- 16 files changed, 206 insertions(+), 134 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 277b7fbc7fb2..68b21f5d6a84 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -217,6 +217,15 @@ struct ip_set; typedef int (*ipset_adtfn)(struct ip_set *set, void *value, u32 timeout, u32 flags); +/* Kernel API function options */ +struct ip_set_adt_opt { + u8 family; /* Actual protocol family */ + u8 dim; /* Dimension of match/target */ + u8 flags; /* Direction and negation flags */ + u32 cmdflags; /* Command-like flags */ + u32 timeout; /* Timeout value */ +}; + /* Set type, variant-specific part */ struct ip_set_type_variant { /* Kernelspace: test/add/del entries @@ -224,7 +233,7 @@ struct ip_set_type_variant { * zero for no match/success to add/delete * positive for matching element */ int (*kadt)(struct ip_set *set, const struct sk_buff * skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags); + enum ipset_adt adt, const struct ip_set_adt_opt *opt); /* Userspace: test/add/del entries * returns negative error code, @@ -314,12 +323,13 @@ extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index); extern void ip_set_nfnl_put(ip_set_id_t index); /* API for iptables set match, and SET target */ + extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb, - u8 family, u8 dim, u8 flags); + const struct ip_set_adt_opt *opt); extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb, - u8 family, u8 dim, u8 flags); + const struct ip_set_adt_opt *opt); extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb, - u8 family, u8 dim, u8 flags); + const struct ip_set_adt_opt *opt); /* Utility functions */ extern void * ip_set_alloc(size_t size); diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index 36cf4dc703bb..6c0219348b43 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -586,7 +586,7 @@ nla_put_failure: static int type_pf_kadt(struct ip_set *set, const struct sk_buff * skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags); + enum ipset_adt adt, const struct ip_set_adt_opt *opt); static int type_pf_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags); diff --git a/include/linux/netfilter/ipset/ip_set_timeout.h b/include/linux/netfilter/ipset/ip_set_timeout.h index bcdd40ad39ed..bae086af0e44 100644 --- a/include/linux/netfilter/ipset/ip_set_timeout.h +++ b/include/linux/netfilter/ipset/ip_set_timeout.h @@ -22,6 +22,9 @@ #define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT) +#define opt_timeout(opt, map) \ + (with_timeout((opt)->timeout) ? (opt)->timeout : (map)->timeout) + static inline unsigned int ip_set_timeout_uget(struct nlattr *tb) { diff --git a/include/linux/netfilter/xt_set.h b/include/linux/netfilter/xt_set.h index 081f1ded2842..c0405ac92870 100644 --- a/include/linux/netfilter/xt_set.h +++ b/include/linux/netfilter/xt_set.h @@ -35,7 +35,7 @@ struct xt_set_info_target_v0 { struct xt_set_info_v0 del_set; }; -/* Revision 1: current interface to netfilter/iptables */ +/* Revision 1 match and target */ struct xt_set_info { ip_set_id_t index; @@ -44,13 +44,22 @@ struct xt_set_info { }; /* match and target infos */ -struct xt_set_info_match { +struct xt_set_info_match_v1 { struct xt_set_info match_set; }; -struct xt_set_info_target { +struct xt_set_info_target_v1 { struct xt_set_info add_set; struct xt_set_info del_set; }; +/* Revision 2 target */ + +struct xt_set_info_target_v2 { + struct xt_set_info add_set; + struct xt_set_info del_set; + u32 flags; + u32 timeout; +}; + #endif /*_XT_SET_H*/ diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index 85b1cdf0a4b8..75990b37ca37 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -219,19 +219,19 @@ nla_put_failure: static int bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { struct bitmap_ip *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; u32 ip; - ip = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC)); + ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC)); if (ip < map->first_ip || ip > map->last_ip) return -IPSET_ERR_BITMAP_RANGE; ip = ip_to_id(map, ip); - return adtfn(set, &ip, map->timeout, flags); + return adtfn(set, &ip, opt_timeout(opt, map), opt->cmdflags); } static int diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index 913a461382e4..cbe77f36650b 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -338,17 +338,17 @@ nla_put_failure: static int bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { struct bitmap_ipmac *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct ipmac data; /* MAC can be src only */ - if (!(flags & IPSET_DIM_TWO_SRC)) + if (!(opt->flags & IPSET_DIM_TWO_SRC)) return 0; - data.id = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC)); + data.id = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC)); if (data.id < map->first_ip || data.id > map->last_ip) return -IPSET_ERR_BITMAP_RANGE; @@ -360,7 +360,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, data.id -= map->first_ip; data.ether = eth_hdr(skb)->h_source; - return adtfn(set, &data, map->timeout, flags); + return adtfn(set, &data, opt_timeout(opt, map), opt->cmdflags); } static int diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index a3935eef76fc..0b0ae19d0290 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c @@ -208,14 +208,15 @@ nla_put_failure: static int bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { struct bitmap_port *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; __be16 __port; u16 port = 0; - if (!ip_set_get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &__port)) + if (!ip_set_get_ip_port(skb, opt->family, + opt->flags & IPSET_DIM_ONE_SRC, &__port)) return -EINVAL; port = ntohs(__port); @@ -225,7 +226,7 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, port -= map->first_port; - return adtfn(set, &port, map->timeout, flags); + return adtfn(set, &port, opt_timeout(opt, map), opt->cmdflags); } static int diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 333b0bedf298..c15c0624d37f 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -325,7 +325,7 @@ __ip_set_put(ip_set_id_t index) int ip_set_test(ip_set_id_t index, const struct sk_buff *skb, - u8 family, u8 dim, u8 flags) + const struct ip_set_adt_opt *opt) { struct ip_set *set = ip_set_list[index]; int ret = 0; @@ -333,19 +333,19 @@ ip_set_test(ip_set_id_t index, const struct sk_buff *skb, BUG_ON(set == NULL); pr_debug("set %s, index %u\n", set->name, index); - if (dim < set->type->dimension || - !(family == set->family || set->family == AF_UNSPEC)) + if (opt->dim < set->type->dimension || + !(opt->family == set->family || set->family == AF_UNSPEC)) return 0; read_lock_bh(&set->lock); - ret = set->variant->kadt(set, skb, IPSET_TEST, family, dim, flags); + ret = set->variant->kadt(set, skb, IPSET_TEST, opt); read_unlock_bh(&set->lock); if (ret == -EAGAIN) { /* Type requests element to be completed */ pr_debug("element must be competed, ADD is triggered\n"); write_lock_bh(&set->lock); - set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags); + set->variant->kadt(set, skb, IPSET_ADD, opt); write_unlock_bh(&set->lock); ret = 1; } @@ -357,7 +357,7 @@ EXPORT_SYMBOL_GPL(ip_set_test); int ip_set_add(ip_set_id_t index, const struct sk_buff *skb, - u8 family, u8 dim, u8 flags) + const struct ip_set_adt_opt *opt) { struct ip_set *set = ip_set_list[index]; int ret; @@ -365,12 +365,12 @@ ip_set_add(ip_set_id_t index, const struct sk_buff *skb, BUG_ON(set == NULL); pr_debug("set %s, index %u\n", set->name, index); - if (dim < set->type->dimension || - !(family == set->family || set->family == AF_UNSPEC)) + if (opt->dim < set->type->dimension || + !(opt->family == set->family || set->family == AF_UNSPEC)) return 0; write_lock_bh(&set->lock); - ret = set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags); + ret = set->variant->kadt(set, skb, IPSET_ADD, opt); write_unlock_bh(&set->lock); return ret; @@ -379,7 +379,7 @@ EXPORT_SYMBOL_GPL(ip_set_add); int ip_set_del(ip_set_id_t index, const struct sk_buff *skb, - u8 family, u8 dim, u8 flags) + const struct ip_set_adt_opt *opt) { struct ip_set *set = ip_set_list[index]; int ret = 0; @@ -387,12 +387,12 @@ ip_set_del(ip_set_id_t index, const struct sk_buff *skb, BUG_ON(set == NULL); pr_debug("set %s, index %u\n", set->name, index); - if (dim < set->type->dimension || - !(family == set->family || set->family == AF_UNSPEC)) + if (opt->dim < set->type->dimension || + !(opt->family == set->family || set->family == AF_UNSPEC)) return 0; write_lock_bh(&set->lock); - ret = set->variant->kadt(set, skb, IPSET_DEL, family, dim, flags); + ret = set->variant->kadt(set, skb, IPSET_DEL, opt); write_unlock_bh(&set->lock); return ret; diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index 368302001120..65a445477f64 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -110,18 +110,18 @@ nla_put_failure: static int hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; __be32 ip; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip); + ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip); ip &= ip_set_netmask(h->netmask); if (ip == 0) return -EINVAL; - return adtfn(set, &ip, h->timeout, flags); + return adtfn(set, &ip, opt_timeout(opt, h), opt->cmdflags); } static int @@ -283,18 +283,18 @@ nla_put_failure: static int hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; union nf_inet_addr ip; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip.in6); + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip.in6); ip6_netmask(&ip, h->netmask); if (ipv6_addr_any(&ip.in6)) return -EINVAL; - return adtfn(set, &ip, h->timeout, flags); + return adtfn(set, &ip, opt_timeout(opt, h), opt->cmdflags); } static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = { diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index 65c2ff4b27aa..9f179bb4f13e 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -126,19 +126,19 @@ nla_put_failure: static int hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport4_elem data = { }; - if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, + if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) return -EINVAL; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); + ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip); - return adtfn(set, &data, h->timeout, flags); + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } static int @@ -330,19 +330,19 @@ nla_put_failure: static int hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport6_elem data = { }; - if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, + if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) return -EINVAL; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6); - return adtfn(set, &data, h->timeout, flags); + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } static int diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index 670e5e4a1232..7cfa52b34981 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -129,20 +129,20 @@ nla_put_failure: static int hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip4_elem data = { }; - if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, + if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) return -EINVAL; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); - ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); + ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip); + ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2); - return adtfn(set, &data, h->timeout, flags); + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } static int @@ -343,20 +343,20 @@ nla_put_failure: static int hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip6_elem data = { }; - if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, + if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) return -EINVAL; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); - ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6); + ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); - return adtfn(set, &data, h->timeout, flags); + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } static int diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 4bb365c9f3db..104042aba92b 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -142,7 +142,7 @@ nla_put_failure: static int hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -154,15 +154,15 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, if (adt == IPSET_TEST) data.cidr = HOST_MASK; - if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, + if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) return -EINVAL; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); - ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); + 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); - return adtfn(set, &data, h->timeout, flags); + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } static int @@ -390,7 +390,7 @@ nla_put_failure: static int hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -402,15 +402,15 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, if (adt == IPSET_TEST) data.cidr = HOST_MASK; - if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, + if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) return -EINVAL; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); - ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); + 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); - return adtfn(set, &data, h->timeout, flags); + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } static int diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index 440b38f9fe3b..0024053e409a 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -127,7 +127,7 @@ nla_put_failure: static int hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -138,10 +138,10 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, if (adt == IPSET_TEST) data.cidr = HOST_MASK; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); + ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip); data.ip &= ip_set_netmask(data.cidr); - return adtfn(set, &data, h->timeout, flags); + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } static int @@ -292,7 +292,7 @@ nla_put_failure: static int hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -303,10 +303,10 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, if (adt == IPSET_TEST) data.cidr = HOST_MASK; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6_netmask(&data.ip, data.cidr); - return adtfn(set, &data, h->timeout, flags); + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } static int diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index 2d31291ba83e..7a2327b3407d 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -139,7 +139,7 @@ nla_put_failure: static int hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -151,14 +151,14 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, if (adt == IPSET_TEST) data.cidr = HOST_MASK; - if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, + if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) return -EINVAL; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); + ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip); data.ip &= ip_set_netmask(data.cidr); - return adtfn(set, &data, h->timeout, flags); + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } static int @@ -352,7 +352,7 @@ nla_put_failure: static int hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -364,14 +364,14 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, if (adt == IPSET_TEST) data.cidr = HOST_MASK; - if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, + if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) return -EINVAL; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6_netmask(&data.ip, data.cidr); - return adtfn(set, &data, h->timeout, flags); + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } static int diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index c2c29daab0ff..f05e9eb863dc 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -72,7 +72,7 @@ list_set_expired(const struct list_set *map, u32 id) static int list_set_kadt(struct ip_set *set, const struct sk_buff *skb, - enum ipset_adt adt, u8 pf, u8 dim, u8 flags) + enum ipset_adt adt, const struct ip_set_adt_opt *opt) { struct list_set *map = set->data; struct set_elem *elem; @@ -87,17 +87,17 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb, continue; switch (adt) { case IPSET_TEST: - ret = ip_set_test(elem->id, skb, pf, dim, flags); + ret = ip_set_test(elem->id, skb, opt); if (ret > 0) return ret; break; case IPSET_ADD: - ret = ip_set_add(elem->id, skb, pf, dim, flags); + ret = ip_set_add(elem->id, skb, opt); if (ret == 0) return ret; break; case IPSET_DEL: - ret = ip_set_del(elem->id, skb, pf, dim, flags); + ret = ip_set_del(elem->id, skb, opt); if (ret == 0) return ret; break; diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c index b3babaed7719..eb265bdfc1d0 100644 --- a/net/netfilter/xt_set.c +++ b/net/netfilter/xt_set.c @@ -29,23 +29,32 @@ MODULE_ALIAS("ip6t_SET"); static inline int match_set(ip_set_id_t index, const struct sk_buff *skb, - u8 pf, u8 dim, u8 flags, int inv) + const struct ip_set_adt_opt *opt, int inv) { - if (ip_set_test(index, skb, pf, dim, flags)) + if (ip_set_test(index, skb, opt)) inv = !inv; return inv; } +#define ADT_OPT(n, f, d, fs, cfs, t) \ +const struct ip_set_adt_opt n = { \ + .family = f, \ + .dim = d, \ + .flags = fs, \ + .cmdflags = cfs, \ + .timeout = t, \ +} + /* Revision 0 interface: backward compatible with netfilter/iptables */ static bool set_match_v0(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_set_info_match_v0 *info = par->matchinfo; + ADT_OPT(opt, par->family, info->match_set.u.compat.dim, + info->match_set.u.compat.flags, 0, UINT_MAX); - return match_set(info->match_set.index, skb, par->family, - info->match_set.u.compat.dim, - info->match_set.u.compat.flags, + return match_set(info->match_set.index, skb, &opt, info->match_set.u.compat.flags & IPSET_INV_MATCH); } @@ -103,15 +112,15 @@ static unsigned int set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_set_info_target_v0 *info = par->targinfo; + ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim, + info->add_set.u.compat.flags, 0, UINT_MAX); + ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim, + info->del_set.u.compat.flags, 0, UINT_MAX); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_add(info->add_set.index, skb, par->family, - info->add_set.u.compat.dim, - info->add_set.u.compat.flags); + ip_set_add(info->add_set.index, skb, &add_opt); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_del(info->del_set.index, skb, par->family, - info->del_set.u.compat.dim, - info->del_set.u.compat.flags); + ip_set_del(info->del_set.index, skb, &del_opt); return XT_CONTINUE; } @@ -170,23 +179,23 @@ set_target_v0_destroy(const struct xt_tgdtor_param *par) ip_set_nfnl_put(info->del_set.index); } -/* Revision 1: current interface to netfilter/iptables */ +/* Revision 1 match and target */ static bool -set_match(const struct sk_buff *skb, struct xt_action_param *par) +set_match_v1(const struct sk_buff *skb, struct xt_action_param *par) { - const struct xt_set_info_match *info = par->matchinfo; + const struct xt_set_info_match_v1 *info = par->matchinfo; + ADT_OPT(opt, par->family, info->match_set.dim, + info->match_set.flags, 0, UINT_MAX); - return match_set(info->match_set.index, skb, par->family, - info->match_set.dim, - info->match_set.flags, + return match_set(info->match_set.index, skb, &opt, info->match_set.flags & IPSET_INV_MATCH); } static int -set_match_checkentry(const struct xt_mtchk_param *par) +set_match_v1_checkentry(const struct xt_mtchk_param *par) { - struct xt_set_info_match *info = par->matchinfo; + struct xt_set_info_match_v1 *info = par->matchinfo; ip_set_id_t index; index = ip_set_nfnl_get_byindex(info->match_set.index); @@ -207,36 +216,34 @@ set_match_checkentry(const struct xt_mtchk_param *par) } static void -set_match_destroy(const struct xt_mtdtor_param *par) +set_match_v1_destroy(const struct xt_mtdtor_param *par) { - struct xt_set_info_match *info = par->matchinfo; + struct xt_set_info_match_v1 *info = par->matchinfo; ip_set_nfnl_put(info->match_set.index); } static unsigned int -set_target(struct sk_buff *skb, const struct xt_action_param *par) +set_target_v1(struct sk_buff *skb, const struct xt_action_param *par) { - const struct xt_set_info_target *info = par->targinfo; + const struct xt_set_info_target_v1 *info = par->targinfo; + ADT_OPT(add_opt, par->family, info->add_set.dim, + info->add_set.flags, 0, UINT_MAX); + ADT_OPT(del_opt, par->family, info->del_set.dim, + info->del_set.flags, 0, UINT_MAX); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_add(info->add_set.index, - skb, par->family, - info->add_set.dim, - info->add_set.flags); + ip_set_add(info->add_set.index, skb, &add_opt); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_del(info->del_set.index, - skb, par->family, - info->del_set.dim, - info->del_set.flags); + ip_set_del(info->del_set.index, skb, &del_opt); return XT_CONTINUE; } static int -set_target_checkentry(const struct xt_tgchk_param *par) +set_target_v1_checkentry(const struct xt_tgchk_param *par) { - const struct xt_set_info_target *info = par->targinfo; + const struct xt_set_info_target_v1 *info = par->targinfo; ip_set_id_t index; if (info->add_set.index != IPSET_INVALID_ID) { @@ -273,9 +280,9 @@ set_target_checkentry(const struct xt_tgchk_param *par) } static void -set_target_destroy(const struct xt_tgdtor_param *par) +set_target_v1_destroy(const struct xt_tgdtor_param *par) { - const struct xt_set_info_target *info = par->targinfo; + const struct xt_set_info_target_v1 *info = par->targinfo; if (info->add_set.index != IPSET_INVALID_ID) ip_set_nfnl_put(info->add_set.index); @@ -283,6 +290,28 @@ set_target_destroy(const struct xt_tgdtor_param *par) ip_set_nfnl_put(info->del_set.index); } +/* Revision 2 target */ + +static unsigned int +set_target_v2(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_set_info_target_v2 *info = par->targinfo; + ADT_OPT(add_opt, par->family, info->add_set.dim, + info->add_set.flags, info->flags, info->timeout); + ADT_OPT(del_opt, par->family, info->del_set.dim, + info->del_set.flags, 0, UINT_MAX); + + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_add(info->add_set.index, skb, &add_opt); + if (info->del_set.index != IPSET_INVALID_ID) + ip_set_del(info->del_set.index, skb, &del_opt); + + return XT_CONTINUE; +} + +#define set_target_v2_checkentry set_target_v1_checkentry +#define set_target_v2_destroy set_target_v1_destroy + static struct xt_match set_matches[] __read_mostly = { { .name = "set", @@ -298,20 +327,20 @@ static struct xt_match set_matches[] __read_mostly = { .name = "set", .family = NFPROTO_IPV4, .revision = 1, - .match = set_match, - .matchsize = sizeof(struct xt_set_info_match), - .checkentry = set_match_checkentry, - .destroy = set_match_destroy, + .match = set_match_v1, + .matchsize = sizeof(struct xt_set_info_match_v1), + .checkentry = set_match_v1_checkentry, + .destroy = set_match_v1_destroy, .me = THIS_MODULE }, { .name = "set", .family = NFPROTO_IPV6, .revision = 1, - .match = set_match, - .matchsize = sizeof(struct xt_set_info_match), - .checkentry = set_match_checkentry, - .destroy = set_match_destroy, + .match = set_match_v1, + .matchsize = sizeof(struct xt_set_info_match_v1), + .checkentry = set_match_v1_checkentry, + .destroy = set_match_v1_destroy, .me = THIS_MODULE }, }; @@ -331,20 +360,40 @@ static struct xt_target set_targets[] __read_mostly = { .name = "SET", .revision = 1, .family = NFPROTO_IPV4, - .target = set_target, - .targetsize = sizeof(struct xt_set_info_target), - .checkentry = set_target_checkentry, - .destroy = set_target_destroy, + .target = set_target_v1, + .targetsize = sizeof(struct xt_set_info_target_v1), + .checkentry = set_target_v1_checkentry, + .destroy = set_target_v1_destroy, .me = THIS_MODULE }, { .name = "SET", .revision = 1, .family = NFPROTO_IPV6, - .target = set_target, - .targetsize = sizeof(struct xt_set_info_target), - .checkentry = set_target_checkentry, - .destroy = set_target_destroy, + .target = set_target_v1, + .targetsize = sizeof(struct xt_set_info_target_v1), + .checkentry = set_target_v1_checkentry, + .destroy = set_target_v1_destroy, + .me = THIS_MODULE + }, + { + .name = "SET", + .revision = 2, + .family = NFPROTO_IPV4, + .target = set_target_v2, + .targetsize = sizeof(struct xt_set_info_target_v2), + .checkentry = set_target_v2_checkentry, + .destroy = set_target_v2_destroy, + .me = THIS_MODULE + }, + { + .name = "SET", + .revision = 2, + .family = NFPROTO_IPV6, + .target = set_target_v2, + .targetsize = sizeof(struct xt_set_info_target_v2), + .checkentry = set_target_v2_checkentry, + .destroy = set_target_v2_destroy, .me = THIS_MODULE }, }; -- cgit v1.2.3 From c1e2e04388b2539960453689b8e721709f71dc9c Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 16 Jun 2011 18:47:07 +0200 Subject: netfilter: ipset: support listing setnames and headers too Current listing makes possible to list sets with full content only. The patch adds support partial listings, i.e. listing just the existing setnames or listing set headers, without set members. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set.h | 4 ++ net/netfilter/ipset/ip_set_core.c | 73 +++++++++++++++++++++------------- 2 files changed, 50 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 68b21f5d6a84..e677c4d8f00e 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -142,6 +142,10 @@ enum ipset_errno { enum ipset_cmd_flags { IPSET_FLAG_BIT_EXIST = 0, IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST), + IPSET_FLAG_BIT_LIST_SETNAME = 1, + 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), }; /* Flags at CADT attribute level */ diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index c15c0624d37f..8446c7d81898 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -939,10 +939,13 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, /* List/save set data */ -#define DUMP_INIT 0L -#define DUMP_ALL 1L -#define DUMP_ONE 2L -#define DUMP_LAST 3L +#define DUMP_INIT 0 +#define DUMP_ALL 1 +#define DUMP_ONE 2 +#define DUMP_LAST 3 + +#define DUMP_TYPE(arg) (((u32)(arg)) & 0x0000FFFF) +#define DUMP_FLAGS(arg) (((u32)(arg)) >> 16) static int ip_set_dump_done(struct netlink_callback *cb) @@ -973,6 +976,7 @@ dump_init(struct netlink_callback *cb) int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); struct nlattr *cda[IPSET_ATTR_CMD_MAX+1]; struct nlattr *attr = (void *)nlh + min_len; + u32 dump_type; ip_set_id_t index; /* Second pass, so parser can't fail */ @@ -984,17 +988,22 @@ dump_init(struct netlink_callback *cb) * [..]: type specific */ - if (!cda[IPSET_ATTR_SETNAME]) { - cb->args[0] = DUMP_ALL; - return 0; - } + if (cda[IPSET_ATTR_SETNAME]) { + index = find_set_id(nla_data(cda[IPSET_ATTR_SETNAME])); + if (index == IPSET_INVALID_ID) + return -ENOENT; - index = find_set_id(nla_data(cda[IPSET_ATTR_SETNAME])); - if (index == IPSET_INVALID_ID) - return -ENOENT; + dump_type = DUMP_ONE; + cb->args[1] = index; + } else + dump_type = DUMP_ALL; + + if (cda[IPSET_ATTR_FLAGS]) { + u32 f = ip_set_get_h32(cda[IPSET_ATTR_FLAGS]); + dump_type |= (f << 16); + } + cb->args[0] = dump_type; - cb->args[0] = DUMP_ONE; - cb->args[1] = index; return 0; } @@ -1005,9 +1014,10 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) struct ip_set *set = NULL; struct nlmsghdr *nlh = NULL; unsigned int flags = NETLINK_CB(cb->skb).pid ? NLM_F_MULTI : 0; + u32 dump_type, dump_flags; int ret = 0; - if (cb->args[0] == DUMP_INIT) { + if (!cb->args[0]) { ret = dump_init(cb); if (ret < 0) { nlh = nlmsg_hdr(cb->skb); @@ -1022,14 +1032,17 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) if (cb->args[1] >= ip_set_max) goto out; - max = cb->args[0] == DUMP_ONE ? cb->args[1] + 1 : ip_set_max; + dump_type = DUMP_TYPE(cb->args[0]); + dump_flags = DUMP_FLAGS(cb->args[0]); + max = dump_type == DUMP_ONE ? cb->args[1] + 1 : ip_set_max; dump_last: - pr_debug("args[0]: %ld args[1]: %ld\n", cb->args[0], cb->args[1]); + pr_debug("args[0]: %u %u args[1]: %ld\n", + dump_type, dump_flags, cb->args[1]); for (; cb->args[1] < max; cb->args[1]++) { index = (ip_set_id_t) cb->args[1]; set = ip_set_list[index]; if (set == NULL) { - if (cb->args[0] == DUMP_ONE) { + if (dump_type == DUMP_ONE) { ret = -ENOENT; goto out; } @@ -1038,8 +1051,8 @@ dump_last: /* When dumping all sets, we must dump "sorted" * so that lists (unions of sets) are dumped last. */ - if (cb->args[0] != DUMP_ONE && - ((cb->args[0] == DUMP_ALL) == + if (dump_type != DUMP_ONE && + ((dump_type == DUMP_ALL) == !!(set->type->features & IPSET_DUMP_LAST))) continue; pr_debug("List set: %s\n", set->name); @@ -1057,6 +1070,8 @@ dump_last: } NLA_PUT_U8(skb, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); NLA_PUT_STRING(skb, IPSET_ATTR_SETNAME, set->name); + if (dump_flags & IPSET_FLAG_LIST_SETNAME) + goto next_set; switch (cb->args[2]) { case 0: /* Core header data */ @@ -1069,24 +1084,23 @@ dump_last: ret = set->variant->head(set, skb); if (ret < 0) goto release_refcount; + if (dump_flags & IPSET_FLAG_LIST_HEADER) + goto next_set; /* Fall through and add elements */ default: read_lock_bh(&set->lock); ret = set->variant->list(set, skb, cb); read_unlock_bh(&set->lock); - if (!cb->args[2]) { + if (!cb->args[2]) /* Set is done, proceed with next one */ - if (cb->args[0] == DUMP_ONE) - cb->args[1] = IPSET_INVALID_ID; - else - cb->args[1]++; - } + goto next_set; goto release_refcount; } } /* If we dump all sets, continue with dumping last ones */ - if (cb->args[0] == DUMP_ALL) { - cb->args[0] = DUMP_LAST; + if (dump_type == DUMP_ALL) { + dump_type = DUMP_LAST; + cb->args[0] = dump_type | (dump_flags << 16); cb->args[1] = 0; goto dump_last; } @@ -1094,6 +1108,11 @@ dump_last: nla_put_failure: ret = -EFAULT; +next_set: + if (dump_type == DUMP_ONE) + cb->args[1] = IPSET_INVALID_ID; + else + cb->args[1]++; release_refcount: /* If there was an error or set is done, release set */ if (ret || !cb->args[2]) { -- cgit v1.2.3 From 3d14b171f004f75c2d1e82e10545966f94132705 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 16 Jun 2011 18:49:17 +0200 Subject: netfilter: ipset: fix adding ranges to hash types When ranges are added to hash types, the elements may trigger rehashing the set. However, the last successfully added element was not kept track so the adding started again with the first element after the rehashing. Bug reported by Mr Dash Four. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set.h | 2 +- include/linux/netfilter/ipset/ip_set_ahash.h | 22 +++++++++++++++++--- net/netfilter/ipset/ip_set_bitmap_ip.c | 2 +- net/netfilter/ipset/ip_set_bitmap_ipmac.c | 2 +- net/netfilter/ipset/ip_set_bitmap_port.c | 2 +- net/netfilter/ipset/ip_set_core.c | 11 +++++----- net/netfilter/ipset/ip_set_hash_ip.c | 17 +++++++++++++-- net/netfilter/ipset/ip_set_hash_ipport.c | 31 +++++++++++++++++++++++----- net/netfilter/ipset/ip_set_hash_ipportip.c | 31 +++++++++++++++++++++++----- net/netfilter/ipset/ip_set_hash_ipportnet.c | 31 +++++++++++++++++++++++----- net/netfilter/ipset/ip_set_hash_net.c | 16 ++++++++++++-- net/netfilter/ipset/ip_set_hash_netport.c | 22 ++++++++++++++++++-- net/netfilter/ipset/ip_set_list_set.c | 2 +- 13 files changed, 157 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index e677c4d8f00e..710ba0070298 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -244,7 +244,7 @@ struct ip_set_type_variant { * zero for no match/success to add/delete * positive for matching element */ int (*uadt)(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags); + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried); /* Low level add/del/test functions */ ipset_adtfn adt[IPSET_ADT_MAX]; diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index 6c0219348b43..8709bd950c8b 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -5,6 +5,11 @@ #include #include +#define CONCAT(a, b, c) a##b##c +#define TOKEN(a, b, c) CONCAT(a, b, c) + +#define type_pf_next TOKEN(TYPE, PF, _elem) + /* Hashing which uses arrays to resolve clashing. The hash table is resized * (doubled) when searching becomes too long. * Internally jhash is used with the assumption that the size of the @@ -54,6 +59,7 @@ struct ip_set_hash { u32 initval; /* random jhash init value */ u32 timeout; /* timeout value, if enabled */ struct timer_list gc; /* garbage collection when timeout enabled */ + struct type_pf_next next; /* temporary storage for uadd */ #ifdef IP_SET_HASH_WITH_NETMASK u8 netmask; /* netmask value for subnets to store */ #endif @@ -217,6 +223,7 @@ ip_set_hash_destroy(struct ip_set *set) #define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask) #define type_pf_data_list TOKEN(TYPE, PF, _data_list) #define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist) +#define type_pf_data_next TOKEN(TYPE, PF, _data_next) #define type_pf_elem TOKEN(TYPE, PF, _elem) #define type_pf_telem TOKEN(TYPE, PF, _telem) @@ -346,6 +353,9 @@ retry: return 0; } +static inline void +type_pf_data_next(struct ip_set_hash *h, const struct type_pf_elem *d); + /* Add an element to a hash and update the internal counters when succeeded, * otherwise report the proper error code. */ static int @@ -372,8 +382,11 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags) } ret = type_pf_elem_add(n, value); - if (ret != 0) + if (ret != 0) { + if (ret == -EAGAIN) + type_pf_data_next(h, d); goto out; + } #ifdef IP_SET_HASH_WITH_NETS add_cidr(h, d->cidr, HOST_MASK); @@ -589,7 +602,7 @@ type_pf_kadt(struct ip_set *set, const struct sk_buff * skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt); static int type_pf_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags); + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried); static const struct ip_set_type_variant type_pf_variant = { .kadt = type_pf_kadt, @@ -821,8 +834,11 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) goto out; } ret = type_pf_elem_tadd(n, d, timeout); - if (ret != 0) + if (ret != 0) { + if (ret == -EEXIST) + type_pf_data_next(h, d); goto out; + } #ifdef IP_SET_HASH_WITH_NETS add_cidr(h, d->cidr, HOST_MASK); diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index 75990b37ca37..3a71c8e41557 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -236,7 +236,7 @@ bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb, static int bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { struct bitmap_ip *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index cbe77f36650b..fdd5f79d93f3 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -365,7 +365,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, static int bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct bitmap_ipmac *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index 0b0ae19d0290..a6a5b3558ddc 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c @@ -231,7 +231,7 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, static int bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { struct bitmap_port *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 8446c7d81898..528a9b3933ab 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1158,17 +1158,18 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 flags, bool use_lineno) { - int ret, retried = 0; + int ret; u32 lineno = 0; - bool eexist = flags & IPSET_FLAG_EXIST; + bool eexist = flags & IPSET_FLAG_EXIST, retried = false; do { write_lock_bh(&set->lock); - ret = set->variant->uadt(set, tb, adt, &lineno, flags); + ret = set->variant->uadt(set, tb, adt, &lineno, flags, retried); write_unlock_bh(&set->lock); + retried = true; } while (ret == -EAGAIN && set->variant->resize && - (ret = set->variant->resize(set, retried++)) == 0); + (ret = set->variant->resize(set, retried)) == 0); if (!ret || (ret == -IPSET_ERR_EXIST && eexist)) return 0; @@ -1341,7 +1342,7 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb, return -IPSET_ERR_PROTOCOL; read_lock_bh(&set->lock); - ret = set->variant->uadt(set, tb, IPSET_TEST, NULL, 0); + ret = set->variant->uadt(set, tb, IPSET_TEST, NULL, 0, 0); read_unlock_bh(&set->lock); /* Userspace can't trigger element to be re-added */ if (ret == -EAGAIN) diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index 65a445477f64..c99e861ce031 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -108,6 +108,12 @@ nla_put_failure: #define HOST_MASK 32 #include +static inline void +hash_ip4_data_next(struct ip_set_hash *h, const struct hash_ip4_elem *d) +{ + h->next.ip = ntohl(d->ip); +} + static int hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt) @@ -126,7 +132,7 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, static int hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -178,6 +184,8 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1); + if (retried) + ip = h->next.ip; for (; !before(ip_to, ip); ip += hosts) { nip = htonl(ip); if (nip == 0) @@ -281,6 +289,11 @@ nla_put_failure: #define HOST_MASK 128 #include +static inline void +hash_ip6_data_next(struct ip_set_hash *h, const struct hash_ip6_elem *d) +{ +} + static int hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt) @@ -305,7 +318,7 @@ static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = { static int hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index 9f179bb4f13e..aa91b2c73be3 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -124,6 +124,14 @@ nla_put_failure: #define HOST_MASK 32 #include +static inline void +hash_ipport4_data_next(struct ip_set_hash *h, + const struct hash_ipport4_elem *d) +{ + h->next.ip = ntohl(d->ip); + h->next.port = ntohs(d->port); +} + static int hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt) @@ -143,12 +151,12 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, static int hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport4_elem data = { }; - u32 ip, ip_to, p, port, port_to; + u32 ip, ip_to, p = 0, port, port_to; u32 timeout = h->timeout; bool with_ports = false; int ret; @@ -220,8 +228,11 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], swap(port, port_to); } - for (; !before(ip_to, ip); ip++) - for (p = port; p <= port_to; p++) { + if (retried) + ip = h->next.ip; + for (; !before(ip_to, ip); ip++) { + p = retried && ip == h->next.ip ? h->next.port : port; + for (; p <= port_to; p++) { data.ip = htonl(ip); data.port = htons(p); ret = adtfn(set, &data, timeout, flags); @@ -231,6 +242,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], else ret = 0; } + } return ret; } @@ -328,6 +340,13 @@ nla_put_failure: #define HOST_MASK 128 #include +static inline void +hash_ipport6_data_next(struct ip_set_hash *h, + const struct hash_ipport6_elem *d) +{ + h->next.port = ntohs(d->port); +} + static int hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt) @@ -347,7 +366,7 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, static int hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -405,6 +424,8 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[], if (port > port_to) swap(port, port_to); + if (retried) + port = h->next.port; for (; port <= port_to; port++) { data.port = htons(port); ret = adtfn(set, &data, timeout, flags); diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index 7cfa52b34981..b88e74e0bf06 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -127,6 +127,14 @@ nla_put_failure: #define HOST_MASK 32 #include +static inline void +hash_ipportip4_data_next(struct ip_set_hash *h, + const struct hash_ipportip4_elem *d) +{ + h->next.ip = ntohl(d->ip); + h->next.port = ntohs(d->port); +} + static int hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt) @@ -147,12 +155,12 @@ hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, static int hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip4_elem data = { }; - u32 ip, ip_to, p, port, port_to; + u32 ip, ip_to, p = 0, port, port_to; u32 timeout = h->timeout; bool with_ports = false; int ret; @@ -228,8 +236,11 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], swap(port, port_to); } - for (; !before(ip_to, ip); ip++) - for (p = port; p <= port_to; p++) { + if (retried) + ip = h->next.ip; + for (; !before(ip_to, ip); ip++) { + p = retried && ip == h->next.ip ? h->next.port : port; + for (; p <= port_to; p++) { data.ip = htonl(ip); data.port = htons(p); ret = adtfn(set, &data, timeout, flags); @@ -239,6 +250,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], else ret = 0; } + } return ret; } @@ -341,6 +353,13 @@ nla_put_failure: #define HOST_MASK 128 #include +static inline void +hash_ipportip6_data_next(struct ip_set_hash *h, + const struct hash_ipportip6_elem *d) +{ + h->next.port = ntohs(d->port); +} + static int hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt) @@ -361,7 +380,7 @@ hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, static int hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -423,6 +442,8 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[], if (port > port_to) swap(port, port_to); + if (retried) + port = h->next.port; for (; port <= port_to; port++) { data.port = htons(port); ret = adtfn(set, &data, timeout, flags); diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 104042aba92b..605ef3bf94ef 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -140,6 +140,14 @@ nla_put_failure: #define HOST_MASK 32 #include +static inline void +hash_ipportnet4_data_next(struct ip_set_hash *h, + const struct hash_ipportnet4_elem *d) +{ + h->next.ip = ntohl(d->ip); + h->next.port = ntohs(d->port); +} + static int hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt) @@ -167,12 +175,12 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, static int hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem data = { .cidr = HOST_MASK }; - u32 ip, ip_to, p, port, port_to; + u32 ip, ip_to, p = 0, port, port_to; u32 timeout = h->timeout; bool with_ports = false; int ret; @@ -256,8 +264,11 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], swap(port, port_to); } - for (; !before(ip_to, ip); ip++) - for (p = port; p <= port_to; p++) { + if (retried) + ip = h->next.ip; + for (; !before(ip_to, ip); ip++) { + p = retried && ip == h->next.ip ? h->next.port : port; + for (; p <= port_to; p++) { data.ip = htonl(ip); data.port = htons(p); ret = adtfn(set, &data, timeout, flags); @@ -267,6 +278,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], else ret = 0; } + } return ret; } @@ -388,6 +400,13 @@ nla_put_failure: #define HOST_MASK 128 #include +static inline void +hash_ipportnet6_data_next(struct ip_set_hash *h, + const struct hash_ipportnet6_elem *d) +{ + h->next.port = ntohs(d->port); +} + static int hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt) @@ -415,7 +434,7 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, static int hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -485,6 +504,8 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], if (port > port_to) swap(port, port_to); + if (retried) + port = h->next.port; for (; port <= port_to; port++) { data.port = htons(port); ret = adtfn(set, &data, timeout, flags); diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index 0024053e409a..e6f8bc5771ca 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -125,6 +125,12 @@ nla_put_failure: #define HOST_MASK 32 #include +static inline void +hash_net4_data_next(struct ip_set_hash *h, + const struct hash_net4_elem *d) +{ +} + static int hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt) @@ -146,7 +152,7 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, static int hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -290,6 +296,12 @@ nla_put_failure: #define HOST_MASK 128 #include +static inline void +hash_net6_data_next(struct ip_set_hash *h, + const struct hash_net6_elem *d) +{ +} + static int hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt) @@ -311,7 +323,7 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, static int hash_net6_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index 7a2327b3407d..037b829178dc 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -137,6 +137,13 @@ nla_put_failure: #define HOST_MASK 32 #include +static inline void +hash_netport4_data_next(struct ip_set_hash *h, + const struct hash_netport4_elem *d) +{ + h->next.port = ntohs(d->port); +} + static int hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt) @@ -163,7 +170,7 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, static int hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -225,6 +232,8 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], if (port > port_to) swap(port, port_to); + if (retried) + port = h->next.port; for (; port <= port_to; port++) { data.port = htons(port); ret = adtfn(set, &data, timeout, flags); @@ -350,6 +359,13 @@ nla_put_failure: #define HOST_MASK 128 #include +static inline void +hash_netport6_data_next(struct ip_set_hash *h, + const struct hash_netport6_elem *d) +{ + h->next.port = ntohs(d->port); +} + static int hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, const struct ip_set_adt_opt *opt) @@ -376,7 +392,7 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, static int hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; @@ -438,6 +454,8 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], if (port > port_to) swap(port, port_to); + if (retried) + port = h->next.port; for (; port <= port_to; port++) { data.port = htons(port); ret = adtfn(set, &data, timeout, flags); diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index f05e9eb863dc..74f0dcc30d98 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -218,7 +218,7 @@ cleanup_entries(struct list_set *map) static int list_set_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags) + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { struct list_set *map = set->data; bool with_timeout = with_timeout(map->timeout); -- cgit v1.2.3 From f1e00b39797944bf25addaf543839feeb25fbdc5 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 16 Jun 2011 18:51:41 +0200 Subject: netfilter: ipset: set type support with multiple revisions added A set type may have multiple revisions, for example when syntax is extended. Support continuous revision ranges in set types. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set.h | 6 ++-- net/netfilter/ipset/ip_set_bitmap_ip.c | 3 +- net/netfilter/ipset/ip_set_bitmap_ipmac.c | 3 +- net/netfilter/ipset/ip_set_bitmap_port.c | 3 +- net/netfilter/ipset/ip_set_core.c | 45 ++++++++++++++++------------- net/netfilter/ipset/ip_set_hash_ip.c | 3 +- net/netfilter/ipset/ip_set_hash_ipport.c | 3 +- net/netfilter/ipset/ip_set_hash_ipportip.c | 3 +- net/netfilter/ipset/ip_set_hash_ipportnet.c | 3 +- net/netfilter/ipset/ip_set_hash_net.c | 3 +- net/netfilter/ipset/ip_set_hash_netport.c | 3 +- net/netfilter/ipset/ip_set_list_set.c | 3 +- 12 files changed, 49 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 710ba0070298..ac31e382472b 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -282,8 +282,8 @@ struct ip_set_type { u8 dimension; /* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */ u8 family; - /* Type revision */ - u8 revision; + /* Type revisions */ + u8 revision_min, revision_max; /* Create set */ int (*create)(struct ip_set *set, struct nlattr *tb[], u32 flags); @@ -314,6 +314,8 @@ struct ip_set { const struct ip_set_type_variant *variant; /* The actual INET family of the set */ u8 family; + /* The type revision */ + u8 revision; /* The type specific data */ void *data; }; diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index 3a71c8e41557..3b5920bfc784 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -551,7 +551,8 @@ static struct ip_set_type bitmap_ip_type __read_mostly = { .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, .family = AF_INET, - .revision = 0, + .revision_min = 0, + .revision_max = 0, .create = bitmap_ip_create, .create_policy = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index fdd5f79d93f3..5deb7bb37468 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -623,7 +623,8 @@ static struct ip_set_type bitmap_ipmac_type = { .features = IPSET_TYPE_IP | IPSET_TYPE_MAC, .dimension = IPSET_DIM_TWO, .family = AF_INET, - .revision = 0, + .revision_min = 0, + .revision_max = 0, .create = bitmap_ipmac_create, .create_policy = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index a6a5b3558ddc..c3e906fcc22c 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c @@ -483,7 +483,8 @@ static struct ip_set_type bitmap_port_type = { .features = IPSET_TYPE_PORT, .dimension = IPSET_DIM_ONE, .family = AF_UNSPEC, - .revision = 0, + .revision_min = 0, + .revision_max = 0, .create = bitmap_port_create, .create_policy = { [IPSET_ATTR_PORT] = { .type = NLA_U16 }, diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 528a9b3933ab..6a82cc0c9e00 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -70,7 +70,8 @@ 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->revision == revision) + revision >= type->revision_min && + revision <= type->revision_max) return type; return NULL; } @@ -135,10 +136,10 @@ find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max) if (STREQ(type->name, name) && (type->family == family || type->family == AF_UNSPEC)) { found = true; - if (type->revision < *min) - *min = type->revision; - if (type->revision > *max) - *max = type->revision; + if (type->revision_min < *min) + *min = type->revision_min; + if (type->revision_max > *max) + *max = type->revision_max; } rcu_read_unlock(); if (found) @@ -159,25 +160,27 @@ ip_set_type_register(struct ip_set_type *type) int ret = 0; if (type->protocol != IPSET_PROTOCOL) { - pr_warning("ip_set type %s, family %s, revision %u uses " + pr_warning("ip_set type %s, family %s, revision %u:%u uses " "wrong protocol version %u (want %u)\n", type->name, family_name(type->family), - type->revision, type->protocol, IPSET_PROTOCOL); + type->revision_min, type->revision_max, + type->protocol, IPSET_PROTOCOL); return -EINVAL; } ip_set_type_lock(); - if (find_set_type(type->name, type->family, type->revision)) { + if (find_set_type(type->name, type->family, type->revision_min)) { /* Duplicate! */ - pr_warning("ip_set type %s, family %s, revision %u " + pr_warning("ip_set type %s, family %s with revision min %u " "already registered!\n", type->name, - family_name(type->family), type->revision); + family_name(type->family), type->revision_min); ret = -EINVAL; goto unlock; } list_add_rcu(&type->list, &ip_set_type_list); - pr_debug("type %s, family %s, revision %u registered.\n", - type->name, family_name(type->family), type->revision); + pr_debug("type %s, family %s, revision %u:%u registered.\n", + type->name, family_name(type->family), + type->revision_min, type->revision_max); unlock: ip_set_type_unlock(); return ret; @@ -189,15 +192,15 @@ void ip_set_type_unregister(struct ip_set_type *type) { ip_set_type_lock(); - if (!find_set_type(type->name, type->family, type->revision)) { - pr_warning("ip_set type %s, family %s, revision %u " + if (!find_set_type(type->name, type->family, type->revision_min)) { + pr_warning("ip_set type %s, family %s with revision min %u " "not registered\n", type->name, - family_name(type->family), type->revision); + family_name(type->family), type->revision_min); goto unlock; } list_del_rcu(&type->list); - pr_debug("type %s, family %s, revision %u unregistered.\n", - type->name, family_name(type->family), type->revision); + pr_debug("type %s, family %s with revision min %u unregistered.\n", + type->name, family_name(type->family), type->revision_min); unlock: ip_set_type_unlock(); @@ -656,6 +659,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, rwlock_init(&set->lock); strlcpy(set->name, name, IPSET_MAXNAMELEN); set->family = family; + set->revision = revision; /* * Next, check that we know the type, and take @@ -696,7 +700,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, (flags & IPSET_FLAG_EXIST) && STREQ(set->type->name, clash->type->name) && set->type->family == clash->type->family && - set->type->revision == clash->type->revision && + set->type->revision_min == clash->type->revision_min && + set->type->revision_max == clash->type->revision_max && set->variant->same_set(set, clash)) ret = 0; goto cleanup; @@ -1080,7 +1085,7 @@ dump_last: NLA_PUT_U8(skb, IPSET_ATTR_FAMILY, set->family); NLA_PUT_U8(skb, IPSET_ATTR_REVISION, - set->type->revision); + set->revision); ret = set->variant->head(set, skb); if (ret < 0) goto release_refcount; @@ -1385,7 +1390,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb, NLA_PUT_STRING(skb2, IPSET_ATTR_SETNAME, set->name); NLA_PUT_STRING(skb2, IPSET_ATTR_TYPENAME, set->type->name); NLA_PUT_U8(skb2, IPSET_ATTR_FAMILY, set->family); - NLA_PUT_U8(skb2, IPSET_ATTR_REVISION, set->type->revision); + NLA_PUT_U8(skb2, IPSET_ATTR_REVISION, set->revision); nlmsg_end(skb2, nlh2); ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index c99e861ce031..c3bc06d353d3 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -441,7 +441,8 @@ static struct ip_set_type hash_ip_type __read_mostly = { .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, .family = AF_UNSPEC, - .revision = 0, + .revision_min = 0, + .revision_max = 0, .create = hash_ip_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index aa91b2c73be3..de2e351034a1 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -512,7 +512,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, .dimension = IPSET_DIM_TWO, .family = AF_UNSPEC, - .revision = 1, + .revision_min = 0, + .revision_max = 1, /* SCTP and UDPLITE support added */ .create = hash_ipport_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index b88e74e0bf06..031ed057c811 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -530,7 +530,8 @@ static struct ip_set_type hash_ipportip_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, .dimension = IPSET_DIM_THREE, .family = AF_UNSPEC, - .revision = 1, + .revision_min = 0, + .revision_max = 1, /* SCTP and UDPLITE support added */ .create = hash_ipportip_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 605ef3bf94ef..0b54fdea9794 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -595,7 +595,8 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, .dimension = IPSET_DIM_THREE, .family = AF_UNSPEC, - .revision = 1, + .revision_min = 0, + .revision_max = 1, /* SCTP and UDPLITE support added */ .create = hash_ipportnet_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index e6f8bc5771ca..360cf5b3ddf6 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -437,7 +437,8 @@ static struct ip_set_type hash_net_type __read_mostly = { .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, .family = AF_UNSPEC, - .revision = 0, + .revision_min = 0, + .revision_max = 0, .create = hash_net_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 037b829178dc..09f807fa24ac 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -544,7 +544,8 @@ static struct ip_set_type hash_netport_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, .dimension = IPSET_DIM_TWO, .family = AF_UNSPEC, - .revision = 1, + .revision_min = 0, + .revision_max = 1, /* SCTP and UDPLITE support added */ .create = hash_netport_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 74f0dcc30d98..898fe68ec4a4 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -575,7 +575,8 @@ static struct ip_set_type list_set_type __read_mostly = { .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST, .dimension = IPSET_DIM_ONE, .family = AF_UNSPEC, - .revision = 0, + .revision_min = 0, + .revision_max = 0, .create = list_set_create, .create_policy = { [IPSET_ATTR_SIZE] = { .type = NLA_U32 }, -- cgit v1.2.3 From d0d9e0a5a8db05b2179c2ffb25d1c2850cce3c8e Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 16 Jun 2011 18:52:41 +0200 Subject: netfilter: ipset: support range for IPv4 at adding/deleting elements for hash:*net* types The range internally is converted to the network(s) equal to the range. Example: # ipset new test hash:net # ipset add test 10.2.0.0-10.2.1.12 # ipset list test Name: test Type: hash:net Header: family inet hashsize 1024 maxelem 65536 Size in memory: 16888 References: 0 Members: 10.2.1.12 10.2.1.0/29 10.2.0.0/24 10.2.1.8/30 Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set.h | 1 + include/linux/netfilter/ipset/ip_set_ahash.h | 2 +- include/linux/netfilter/ipset/ip_set_hash.h | 4 ++ include/linux/netfilter/ipset/pfxlen.h | 3 ++ net/netfilter/ipset/ip_set_hash_ipportnet.c | 69 +++++++++++++++++++--------- net/netfilter/ipset/ip_set_hash_net.c | 51 +++++++++++++++----- net/netfilter/ipset/ip_set_hash_netport.c | 69 +++++++++++++++++++--------- net/netfilter/ipset/pfxlen.c | 21 +++++++++ 8 files changed, 165 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index ac31e382472b..fd83f4f436c3 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -104,6 +104,7 @@ enum { IPSET_ATTR_NAMEREF, IPSET_ATTR_IP2, IPSET_ATTR_CIDR2, + IPSET_ATTR_IP2_TO, __IPSET_ATTR_ADT_MAX, }; #define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1) diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index 8709bd950c8b..905e2ac32daf 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -353,7 +353,7 @@ retry: return 0; } -static inline void +static void type_pf_data_next(struct ip_set_hash *h, const struct type_pf_elem *d); /* Add an element to a hash and update the internal counters when succeeded, diff --git a/include/linux/netfilter/ipset/ip_set_hash.h b/include/linux/netfilter/ipset/ip_set_hash.h index b86f15c04524..e2a9fae767f6 100644 --- a/include/linux/netfilter/ipset/ip_set_hash.h +++ b/include/linux/netfilter/ipset/ip_set_hash.h @@ -11,6 +11,10 @@ enum { IPSET_ERR_INVALID_PROTO, /* Protocol missing but must be specified */ IPSET_ERR_MISSING_PROTO, + /* Range not supported */ + IPSET_ERR_HASH_RANGE_UNSUPPORTED, + /* Invalid range */ + IPSET_ERR_HASH_RANGE, }; #ifdef __KERNEL__ diff --git a/include/linux/netfilter/ipset/pfxlen.h b/include/linux/netfilter/ipset/pfxlen.h index 0e1fb50da562..84efa3351e0e 100644 --- a/include/linux/netfilter/ipset/pfxlen.h +++ b/include/linux/netfilter/ipset/pfxlen.h @@ -3,6 +3,7 @@ #include #include +#include /* Prefixlen maps, by Jan Engelhardt */ extern const union nf_inet_addr ip_set_netmask_map[]; @@ -32,4 +33,6 @@ ip_set_hostmask6(u8 pfxlen) return &ip_set_hostmask_map[pfxlen].ip6[0]; } +extern u32 ip_set_range_to_cidr(u32 from, u32 to, u8 *cidr); + #endif /*_PFXLEN_H */ diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 0b54fdea9794..ef068b03ec1a 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -146,6 +146,7 @@ hash_ipportnet4_data_next(struct ip_set_hash *h, { h->next.ip = ntohl(d->ip); h->next.port = ntohs(d->port); + h->next.ip2 = ntohl(d->ip2); } static int @@ -181,6 +182,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem data = { .cidr = HOST_MASK }; u32 ip, ip_to, p = 0, port, port_to; + u32 ip2_from = 0, ip2_to, ip2_last, ip2; u32 timeout = h->timeout; bool with_ports = false; int ret; @@ -194,21 +196,19 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip); + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); if (ret) return ret; - ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &data.ip2); + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from); if (ret) return ret; - if (tb[IPSET_ATTR_CIDR2]) + if (tb[IPSET_ATTR_CIDR2]) { data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); - - if (!data.cidr) - return -IPSET_ERR_INVALID_CIDR; - - data.ip2 &= ip_set_netmask(data.cidr); + if (!data.cidr) + return -IPSET_ERR_INVALID_CIDR; + } if (tb[IPSET_ATTR_PORT]) data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); @@ -233,14 +233,16 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } + with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; if (adt == IPSET_TEST || - !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || - tb[IPSET_ATTR_PORT_TO])) { + !(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)); ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } - ip = ntohl(data.ip); if (tb[IPSET_ATTR_IP_TO]) { ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); if (ret) @@ -254,29 +256,48 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], return -IPSET_ERR_INVALID_CIDR; ip &= ip_set_hostmask(cidr); ip_to = ip | ~ip_set_hostmask(cidr); - } else - ip_to = ip; + } port_to = port = ntohs(data.port); - if (with_ports && tb[IPSET_ATTR_PORT_TO]) { + if (tb[IPSET_ATTR_PORT_TO]) { port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); if (port > port_to) swap(port, port_to); } + if (tb[IPSET_ATTR_IP2_TO]) { + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); + if (ret) + return ret; + if (ip2_from > ip2_to) + swap(ip2_from, ip2_to); + if (ip2_from + UINT_MAX == ip2_to) + return -IPSET_ERR_HASH_RANGE; + } else { + ip2_from &= ip_set_hostmask(data.cidr); + ip2_to = ip2_from | ~ip_set_hostmask(data.cidr); + } if (retried) ip = h->next.ip; for (; !before(ip_to, ip); ip++) { + data.ip = htonl(ip); p = retried && ip == h->next.ip ? h->next.port : port; for (; p <= port_to; p++) { - data.ip = htonl(ip); data.port = htons(p); - ret = adtfn(set, &data, timeout, flags); - - if (ret && !ip_set_eexist(ret, flags)) - return ret; - else - ret = 0; + ip2 = retried && ip == h->next.ip && p == h->next.port + ? h->next.ip2 : ip2_from; + while (!after(ip2, ip2_to)) { + data.ip2 = htonl(ip2); + ip2_last = ip_set_range_to_cidr(ip2, ip2_to, + &data.cidr); + ret = adtfn(set, &data, timeout, flags); + + if (ret && !ip_set_eexist(ret, flags)) + return ret; + else + ret = 0; + ip2 = ip2_last + 1; + } } } return ret; @@ -451,6 +472,8 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR])) return -IPSET_ERR_PROTOCOL; + if (unlikely(tb[IPSET_ATTR_IP_TO])) + return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); @@ -596,7 +619,8 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { .dimension = IPSET_DIM_THREE, .family = AF_UNSPEC, .revision_min = 0, - .revision_max = 1, /* SCTP and UDPLITE support added */ + /* 1 SCTP and UDPLITE support added */ + .revision_max = 2, /* Range as input support for IPv4 added */ .create = hash_ipportnet_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -609,6 +633,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index 360cf5b3ddf6..8d3c3efbbf17 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -129,6 +129,7 @@ static inline void hash_net4_data_next(struct ip_set_hash *h, const struct hash_net4_elem *d) { + h->next.ip = ntohl(d->ip); } static int @@ -158,6 +159,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net4_elem data = { .cidr = HOST_MASK }; u32 timeout = h->timeout; + u32 ip = 0, ip_to, last; int ret; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -167,27 +169,51 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip); + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); if (ret) return ret; - if (tb[IPSET_ATTR_CIDR]) + if (tb[IPSET_ATTR_CIDR]) { data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); - - if (!data.cidr) - return -IPSET_ERR_INVALID_CIDR; - - data.ip &= ip_set_netmask(data.cidr); + if (!data.cidr) + return -IPSET_ERR_INVALID_CIDR; + } if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } + + if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) { + data.ip = htonl(ip & ip_set_hostmask(data.cidr)); + ret = adtfn(set, &data, timeout, flags); + return ip_set_eexist(ret, flags) ? 0 : ret; + } - ret = adtfn(set, &data, timeout, flags); - - return ip_set_eexist(ret, flags) ? 0 : ret; + ip_to = ip; + if (tb[IPSET_ATTR_IP_TO]) { + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); + if (ret) + return ret; + if (ip_to < ip) + swap(ip, ip_to); + if (ip + UINT_MAX == ip_to) + return -IPSET_ERR_HASH_RANGE; + } + 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); + ret = adtfn(set, &data, timeout, flags); + if (ret && !ip_set_eexist(ret, flags)) + return ret; + else + ret = 0; + ip = last + 1; + } + return ret; } static bool @@ -334,6 +360,8 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[], if (unlikely(!tb[IPSET_ATTR_IP] || !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) return -IPSET_ERR_PROTOCOL; + if (unlikely(tb[IPSET_ATTR_IP_TO])) + return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); @@ -438,7 +466,7 @@ static struct ip_set_type hash_net_type __read_mostly = { .dimension = IPSET_DIM_ONE, .family = AF_UNSPEC, .revision_min = 0, - .revision_max = 0, + .revision_max = 1, /* Range as input support for IPv4 added */ .create = hash_net_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -449,6 +477,7 @@ static struct ip_set_type hash_net_type __read_mostly = { }, .adt_policy = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, }, diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index 09f807fa24ac..300103096879 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -141,6 +141,7 @@ static inline void hash_netport4_data_next(struct ip_set_hash *h, const struct hash_netport4_elem *d) { + h->next.ip = ntohl(d->ip); h->next.port = ntohs(d->port); } @@ -175,7 +176,7 @@ 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 }; - u32 port, port_to; + u32 port, port_to, p = 0, ip = 0, ip_to, last; u32 timeout = h->timeout; bool with_ports = false; int ret; @@ -189,15 +190,15 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip); + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); if (ret) return ret; - if (tb[IPSET_ATTR_CIDR]) + if (tb[IPSET_ATTR_CIDR]) { data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); - if (!data.cidr) - return -IPSET_ERR_INVALID_CIDR; - data.ip &= ip_set_netmask(data.cidr); + if (!data.cidr) + return -IPSET_ERR_INVALID_CIDR; + } if (tb[IPSET_ATTR_PORT]) data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); @@ -222,26 +223,48 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { + with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; + if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) { + data.ip = htonl(ip & ip_set_hostmask(data.cidr)); ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } - port = ntohs(data.port); - port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); - if (port > port_to) - swap(port, port_to); + port = port_to = ntohs(data.port); + if (tb[IPSET_ATTR_PORT_TO]) { + port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); + if (port_to < port) + swap(port, port_to); + } + if (tb[IPSET_ATTR_IP_TO]) { + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); + if (ret) + return ret; + if (ip_to < ip) + swap(ip, ip_to); + if (ip + UINT_MAX == ip_to) + return -IPSET_ERR_HASH_RANGE; + } else { + ip &= ip_set_hostmask(data.cidr); + ip_to = ip | ~ip_set_hostmask(data.cidr); + } if (retried) - port = h->next.port; - for (; port <= port_to; port++) { - data.port = htons(port); - ret = adtfn(set, &data, timeout, flags); - - if (ret && !ip_set_eexist(ret, flags)) - return ret; - else - ret = 0; + ip = h->next.ip; + while (!after(ip, ip_to)) { + data.ip = htonl(ip); + last = ip_set_range_to_cidr(ip, ip_to, &data.cidr); + p = retried && ip == h->next.ip ? h->next.port : port; + for (; p <= port_to; p++) { + data.port = htons(p); + ret = adtfn(set, &data, timeout, flags); + + if (ret && !ip_set_eexist(ret, flags)) + return ret; + else + ret = 0; + } + ip = last + 1; } return ret; } @@ -407,6 +430,8 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) return -IPSET_ERR_PROTOCOL; + if (unlikely(tb[IPSET_ATTR_IP_TO])) + return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); @@ -545,7 +570,8 @@ static struct ip_set_type hash_netport_type __read_mostly = { .dimension = IPSET_DIM_TWO, .family = AF_UNSPEC, .revision_min = 0, - .revision_max = 1, /* SCTP and UDPLITE support added */ + /* 1 SCTP and UDPLITE support added */ + .revision_max = 2, /* Range as input support for IPv4 added */ .create = hash_netport_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -557,6 +583,7 @@ static struct ip_set_type hash_netport_type __read_mostly = { }, .adt_policy = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, diff --git a/net/netfilter/ipset/pfxlen.c b/net/netfilter/ipset/pfxlen.c index 23f8c8162214..b57a85673de7 100644 --- a/net/netfilter/ipset/pfxlen.c +++ b/net/netfilter/ipset/pfxlen.c @@ -289,3 +289,24 @@ const union nf_inet_addr ip_set_hostmask_map[] = { E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF), }; EXPORT_SYMBOL_GPL(ip_set_hostmask_map); + +/* Find the largest network which matches the range from left, in host order. */ +u32 +ip_set_range_to_cidr(u32 from, u32 to, u8 *cidr) +{ + u32 last; + u8 i; + + for (i = 1; i < 32; i++) { + if ((from & ip_set_hostmask(i)) != from) + continue; + last = from | ~ip_set_hostmask(i); + if (!after(last, to)) { + *cidr = i; + return last; + } + } + *cidr = 32; + return from; +} +EXPORT_SYMBOL_GPL(ip_set_range_to_cidr); -- cgit v1.2.3 From c64562eaf2ad61d7492788ef95678f52d0d28f2a Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 16 Jun 2011 18:53:51 +0200 Subject: netfilter: ipset: adding ranges to hash types with timeout could still fail, fixed The patch "Fix adding ranges to hash types" had got a mistypeing in the timeout variant of the hash types, which actually made the patch ineffective. Fixed! Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set_ahash.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index 905e2ac32daf..a099d40d120a 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -835,7 +835,7 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) } ret = type_pf_elem_tadd(n, d, timeout); if (ret != 0) { - if (ret == -EEXIST) + if (ret == -EAGAIN) type_pf_data_next(h, d); goto out; } -- cgit v1.2.3 From e6146e8684ed6dd4c0ff85ca21bf4324114fbbfa Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 16 Jun 2011 18:55:58 +0200 Subject: netfilter: ipset: use unified from/to address masking and check the usage Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/pfxlen.h | 6 ++++++ net/netfilter/ipset/ip_set_bitmap_ip.c | 6 ++---- net/netfilter/ipset/ip_set_bitmap_ipmac.c | 2 +- net/netfilter/ipset/ip_set_hash_ip.c | 3 +-- net/netfilter/ipset/ip_set_hash_ipport.c | 3 +-- net/netfilter/ipset/ip_set_hash_ipportip.c | 3 +-- net/netfilter/ipset/ip_set_hash_ipportnet.c | 6 ++---- net/netfilter/ipset/ip_set_hash_netport.c | 3 +-- 8 files changed, 15 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/pfxlen.h b/include/linux/netfilter/ipset/pfxlen.h index 84efa3351e0e..d55a6ccf662e 100644 --- a/include/linux/netfilter/ipset/pfxlen.h +++ b/include/linux/netfilter/ipset/pfxlen.h @@ -35,4 +35,10 @@ ip_set_hostmask6(u8 pfxlen) extern u32 ip_set_range_to_cidr(u32 from, u32 to, u8 *cidr); +#define ip_set_mask_from_to(from, to, cidr) \ +do { \ + from &= ip_set_hostmask(cidr); \ + to = from | ~ip_set_hostmask(cidr); \ +} while (0) + #endif /*_PFXLEN_H */ diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index 49323110560c..c46e34401597 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -283,8 +283,7 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[], if (cidr > 32) return -IPSET_ERR_INVALID_CIDR; - ip &= ip_set_hostmask(cidr); - ip_to = ip | ~ip_set_hostmask(cidr); + ip_set_mask_from_to(ip, ip_to, cidr); } else ip_to = ip; @@ -478,8 +477,7 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) if (cidr >= 32) return -IPSET_ERR_INVALID_CIDR; - first_ip &= ip_set_hostmask(cidr); - last_ip = first_ip | ~ip_set_hostmask(cidr); + ip_set_mask_from_to(first_ip, last_ip, cidr); } else return -IPSET_ERR_PROTOCOL; diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index 5deb7bb37468..aa2cfa1ed474 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -578,7 +578,7 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[], if (cidr >= 32) return -IPSET_ERR_INVALID_CIDR; - last_ip = first_ip | ~ip_set_hostmask(cidr); + ip_set_mask_from_to(first_ip, last_ip, cidr); } else return -IPSET_ERR_PROTOCOL; diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index c3bc06d353d3..bdb432e22a8a 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -177,8 +177,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], if (cidr > 32) return -IPSET_ERR_INVALID_CIDR; - ip &= ip_set_hostmask(cidr); - ip_to = ip | ~ip_set_hostmask(cidr); + ip_set_mask_from_to(ip, ip_to, cidr); } else ip_to = ip; diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index de2e351034a1..bdeb71605080 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -216,8 +216,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], if (cidr > 32) return -IPSET_ERR_INVALID_CIDR; - ip &= ip_set_hostmask(cidr); - ip_to = ip | ~ip_set_hostmask(cidr); + ip_set_mask_from_to(ip, ip_to, cidr); } else ip_to = ip; diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index 031ed057c811..fb986fc6a6f2 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -224,8 +224,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], if (cidr > 32) return -IPSET_ERR_INVALID_CIDR; - ip &= ip_set_hostmask(cidr); - ip_to = ip | ~ip_set_hostmask(cidr); + ip_set_mask_from_to(ip, ip_to, cidr); } else ip_to = ip; diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index ef068b03ec1a..2ed5e7581055 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -254,8 +254,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], if (cidr > 32) return -IPSET_ERR_INVALID_CIDR; - ip &= ip_set_hostmask(cidr); - ip_to = ip | ~ip_set_hostmask(cidr); + ip_set_mask_from_to(ip, ip_to, cidr); } port_to = port = ntohs(data.port); @@ -273,8 +272,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], if (ip2_from + UINT_MAX == ip2_to) return -IPSET_ERR_HASH_RANGE; } else { - ip2_from &= ip_set_hostmask(data.cidr); - ip2_to = ip2_from | ~ip_set_hostmask(data.cidr); + ip_set_mask_from_to(ip2_from, ip2_to, data.cidr); } if (retried) diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index 300103096879..90adc2c30665 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -245,8 +245,7 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], if (ip + UINT_MAX == ip_to) return -IPSET_ERR_HASH_RANGE; } else { - ip &= ip_set_hostmask(data.cidr); - ip_to = ip | ~ip_set_hostmask(data.cidr); + ip_set_mask_from_to(ip, ip_to, data.cidr); } if (retried) -- cgit v1.2.3 From b66554cf03fe866b3fb7b9f40f430b8ba09f41c8 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 16 Jun 2011 18:56:47 +0200 Subject: netfilter: ipset: add xt_action_param to the variant level kadt functions, ipset API change With the change the sets can use any parameter available for the match and target extensions, like input/output interface. It's required for the hash:net,iface set type. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set.h | 5 +++++ include/linux/netfilter/ipset/ip_set_ahash.h | 1 + net/netfilter/ipset/ip_set_bitmap_ip.c | 1 + net/netfilter/ipset/ip_set_bitmap_ipmac.c | 1 + net/netfilter/ipset/ip_set_bitmap_port.c | 1 + net/netfilter/ipset/ip_set_core.c | 12 ++++++++---- net/netfilter/ipset/ip_set_hash_ip.c | 2 ++ net/netfilter/ipset/ip_set_hash_ipport.c | 2 ++ net/netfilter/ipset/ip_set_hash_ipportip.c | 2 ++ net/netfilter/ipset/ip_set_hash_ipportnet.c | 2 ++ net/netfilter/ipset/ip_set_hash_net.c | 2 ++ net/netfilter/ipset/ip_set_hash_netport.c | 2 ++ net/netfilter/ipset/ip_set_list_set.c | 7 ++++--- net/netfilter/xt_set.c | 19 ++++++++++--------- 14 files changed, 43 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index fd83f4f436c3..8955165f7dbb 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -170,6 +170,7 @@ enum ipset_adt { #include #include #include +#include #include #include @@ -238,6 +239,7 @@ struct ip_set_type_variant { * zero for no match/success to add/delete * positive for matching element */ int (*kadt)(struct ip_set *set, const struct sk_buff * skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt); /* Userspace: test/add/del entries @@ -332,10 +334,13 @@ extern void ip_set_nfnl_put(ip_set_id_t index); /* API for iptables set match, and SET target */ extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb, + const struct xt_action_param *par, const struct ip_set_adt_opt *opt); extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb, + const struct xt_action_param *par, const struct ip_set_adt_opt *opt); extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb, + const struct xt_action_param *par, const struct ip_set_adt_opt *opt); /* Utility functions */ diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index a099d40d120a..1c977e143885 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -599,6 +599,7 @@ nla_put_failure: static int type_pf_kadt(struct ip_set *set, const struct sk_buff * skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt); static int type_pf_uadt(struct ip_set *set, struct nlattr *tb[], diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index c46e34401597..e3e73997c3be 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -219,6 +219,7 @@ nla_put_failure: static int bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { struct bitmap_ip *map = set->data; diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index aa2cfa1ed474..51ab66435a0a 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -338,6 +338,7 @@ nla_put_failure: static int bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { struct bitmap_ipmac *map = set->data; diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index c3e906fcc22c..29ba93bb94be 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c @@ -208,6 +208,7 @@ nla_put_failure: static int bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { struct bitmap_port *map = set->data; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 6a82cc0c9e00..64e7b04759a7 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -328,6 +329,7 @@ __ip_set_put(ip_set_id_t index) int ip_set_test(ip_set_id_t index, const struct sk_buff *skb, + const struct xt_action_param *par, const struct ip_set_adt_opt *opt) { struct ip_set *set = ip_set_list[index]; @@ -341,14 +343,14 @@ ip_set_test(ip_set_id_t index, const struct sk_buff *skb, return 0; read_lock_bh(&set->lock); - ret = set->variant->kadt(set, skb, IPSET_TEST, opt); + ret = set->variant->kadt(set, skb, par, IPSET_TEST, opt); read_unlock_bh(&set->lock); if (ret == -EAGAIN) { /* Type requests element to be completed */ pr_debug("element must be competed, ADD is triggered\n"); write_lock_bh(&set->lock); - set->variant->kadt(set, skb, IPSET_ADD, opt); + set->variant->kadt(set, skb, par, IPSET_ADD, opt); write_unlock_bh(&set->lock); ret = 1; } @@ -360,6 +362,7 @@ EXPORT_SYMBOL_GPL(ip_set_test); int ip_set_add(ip_set_id_t index, const struct sk_buff *skb, + const struct xt_action_param *par, const struct ip_set_adt_opt *opt) { struct ip_set *set = ip_set_list[index]; @@ -373,7 +376,7 @@ ip_set_add(ip_set_id_t index, const struct sk_buff *skb, return 0; write_lock_bh(&set->lock); - ret = set->variant->kadt(set, skb, IPSET_ADD, opt); + ret = set->variant->kadt(set, skb, par, IPSET_ADD, opt); write_unlock_bh(&set->lock); return ret; @@ -382,6 +385,7 @@ EXPORT_SYMBOL_GPL(ip_set_add); int ip_set_del(ip_set_id_t index, const struct sk_buff *skb, + const struct xt_action_param *par, const struct ip_set_adt_opt *opt) { struct ip_set *set = ip_set_list[index]; @@ -395,7 +399,7 @@ ip_set_del(ip_set_id_t index, const struct sk_buff *skb, return 0; write_lock_bh(&set->lock); - ret = set->variant->kadt(set, skb, IPSET_DEL, opt); + ret = set->variant->kadt(set, skb, par, IPSET_DEL, opt); write_unlock_bh(&set->lock); return ret; diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index bdb432e22a8a..fa80bb9b9c81 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -116,6 +116,7 @@ hash_ip4_data_next(struct ip_set_hash *h, const struct hash_ip4_elem *d) static int hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; @@ -295,6 +296,7 @@ hash_ip6_data_next(struct ip_set_hash *h, const struct hash_ip6_elem *d) static int hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index bdeb71605080..bbf51b67b170 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -134,6 +134,7 @@ hash_ipport4_data_next(struct ip_set_hash *h, static int hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; @@ -348,6 +349,7 @@ hash_ipport6_data_next(struct ip_set_hash *h, static int hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index fb986fc6a6f2..96525f529a54 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -137,6 +137,7 @@ hash_ipportip4_data_next(struct ip_set_hash *h, static int hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; @@ -361,6 +362,7 @@ hash_ipportip6_data_next(struct ip_set_hash *h, static int hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 2ed5e7581055..dcd351b587d5 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -151,6 +151,7 @@ hash_ipportnet4_data_next(struct ip_set_hash *h, static int hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; @@ -428,6 +429,7 @@ hash_ipportnet6_data_next(struct ip_set_hash *h, static int hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index 8d3c3efbbf17..dcbb5d4c636c 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -134,6 +134,7 @@ hash_net4_data_next(struct ip_set_hash *h, static int hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; @@ -330,6 +331,7 @@ hash_net6_data_next(struct ip_set_hash *h, static int hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index 90adc2c30665..72961ba72a86 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -147,6 +147,7 @@ hash_netport4_data_next(struct ip_set_hash *h, static int hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; @@ -390,6 +391,7 @@ hash_netport6_data_next(struct ip_set_hash *h, static int hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { const struct ip_set_hash *h = set->data; diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 898fe68ec4a4..4d10819d462e 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -72,6 +72,7 @@ list_set_expired(const struct list_set *map, u32 id) static int list_set_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, enum ipset_adt adt, const struct ip_set_adt_opt *opt) { struct list_set *map = set->data; @@ -87,17 +88,17 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb, continue; switch (adt) { case IPSET_TEST: - ret = ip_set_test(elem->id, skb, opt); + ret = ip_set_test(elem->id, skb, par, opt); if (ret > 0) return ret; break; case IPSET_ADD: - ret = ip_set_add(elem->id, skb, opt); + ret = ip_set_add(elem->id, skb, par, opt); if (ret == 0) return ret; break; case IPSET_DEL: - ret = ip_set_del(elem->id, skb, opt); + ret = ip_set_del(elem->id, skb, par, opt); if (ret == 0) return ret; break; diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c index eb265bdfc1d0..453847f293d3 100644 --- a/net/netfilter/xt_set.c +++ b/net/netfilter/xt_set.c @@ -29,9 +29,10 @@ MODULE_ALIAS("ip6t_SET"); static inline int match_set(ip_set_id_t index, const struct sk_buff *skb, + const struct xt_action_param *par, const struct ip_set_adt_opt *opt, int inv) { - if (ip_set_test(index, skb, opt)) + if (ip_set_test(index, skb, par, opt)) inv = !inv; return inv; } @@ -54,7 +55,7 @@ set_match_v0(const struct sk_buff *skb, struct xt_action_param *par) ADT_OPT(opt, par->family, info->match_set.u.compat.dim, info->match_set.u.compat.flags, 0, UINT_MAX); - return match_set(info->match_set.index, skb, &opt, + return match_set(info->match_set.index, skb, par, &opt, info->match_set.u.compat.flags & IPSET_INV_MATCH); } @@ -118,9 +119,9 @@ set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) info->del_set.u.compat.flags, 0, UINT_MAX); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_add(info->add_set.index, skb, &add_opt); + ip_set_add(info->add_set.index, skb, par, &add_opt); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_del(info->del_set.index, skb, &del_opt); + ip_set_del(info->del_set.index, skb, par, &del_opt); return XT_CONTINUE; } @@ -188,7 +189,7 @@ set_match_v1(const struct sk_buff *skb, struct xt_action_param *par) ADT_OPT(opt, par->family, info->match_set.dim, info->match_set.flags, 0, UINT_MAX); - return match_set(info->match_set.index, skb, &opt, + return match_set(info->match_set.index, skb, par, &opt, info->match_set.flags & IPSET_INV_MATCH); } @@ -233,9 +234,9 @@ set_target_v1(struct sk_buff *skb, const struct xt_action_param *par) info->del_set.flags, 0, UINT_MAX); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_add(info->add_set.index, skb, &add_opt); + ip_set_add(info->add_set.index, skb, par, &add_opt); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_del(info->del_set.index, skb, &del_opt); + ip_set_del(info->del_set.index, skb, par, &del_opt); return XT_CONTINUE; } @@ -302,9 +303,9 @@ set_target_v2(struct sk_buff *skb, const struct xt_action_param *par) info->del_set.flags, 0, UINT_MAX); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_add(info->add_set.index, skb, &add_opt); + ip_set_add(info->add_set.index, skb, par, &add_opt); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_del(info->del_set.index, skb, &del_opt); + ip_set_del(info->del_set.index, skb, par, &del_opt); return XT_CONTINUE; } -- cgit v1.2.3 From e385357a2f214e4d4e79c6118f1bede2572e0701 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 16 Jun 2011 19:00:48 +0200 Subject: netfilter: ipset: hash:net,iface type introduced The hash:net,iface type makes possible to store network address and interface name pairs in a set. It's mostly suitable for egress and ingress filtering. Examples: # ipset create test hash:net,iface # ipset add test 192.168.0.0/16,eth0 # ipset add test 192.168.0.0/24,eth1 Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set.h | 5 + include/linux/netfilter/ipset/ip_set_ahash.h | 6 + net/netfilter/ipset/Kconfig | 10 + net/netfilter/ipset/Makefile | 1 + net/netfilter/ipset/ip_set_hash_netiface.c | 762 +++++++++++++++++++++++++++ 5 files changed, 784 insertions(+) create mode 100644 net/netfilter/ipset/ip_set_hash_netiface.c (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 8955165f7dbb..e40917334d04 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -105,6 +105,7 @@ enum { IPSET_ATTR_IP2, IPSET_ATTR_CIDR2, IPSET_ATTR_IP2_TO, + IPSET_ATTR_IFACE, __IPSET_ATTR_ADT_MAX, }; #define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1) @@ -153,6 +154,8 @@ enum ipset_cmd_flags { enum ipset_cadt_flags { IPSET_FLAG_BIT_BEFORE = 0, IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE), + IPSET_FLAG_BIT_PHYSDEV = 1, + IPSET_FLAG_PHYSDEV = (1 << IPSET_FLAG_BIT_PHYSDEV), }; /* Commands with settype-specific attributes */ @@ -212,6 +215,8 @@ enum ip_set_feature { IPSET_TYPE_IP2 = (1 << IPSET_TYPE_IP2_FLAG), IPSET_TYPE_NAME_FLAG = 4, IPSET_TYPE_NAME = (1 << IPSET_TYPE_NAME_FLAG), + IPSET_TYPE_IFACE_FLAG = 5, + IPSET_TYPE_IFACE = (1 << IPSET_TYPE_IFACE_FLAG), /* Strictly speaking not a feature, but a flag for dumping: * this settype must be dumped last */ IPSET_DUMP_LAST_FLAG = 7, diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index 1c977e143885..8a0999a8baa1 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -63,6 +63,9 @@ struct ip_set_hash { #ifdef IP_SET_HASH_WITH_NETMASK u8 netmask; /* netmask value for subnets to store */ #endif +#ifdef IP_SET_HASH_WITH_RBTREE + struct rb_root rbtree; +#endif #ifdef IP_SET_HASH_WITH_NETS struct ip_set_hash_nets nets[0]; /* book-keeping of prefixes */ #endif @@ -200,6 +203,9 @@ ip_set_hash_destroy(struct ip_set *set) del_timer_sync(&h->gc); ahash_destroy(h->table); +#ifdef IP_SET_HASH_WITH_RBTREE + rbtree_destroy(&h->rbtree); +#endif kfree(h); set->data = NULL; diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig index 2c5b348eb3a8..ba36c283d837 100644 --- a/net/netfilter/ipset/Kconfig +++ b/net/netfilter/ipset/Kconfig @@ -109,6 +109,16 @@ config IP_SET_HASH_NETPORT To compile it as a module, choose M here. If unsure, say N. +config IP_SET_HASH_NETIFACE + tristate "hash:net,iface set support" + depends on IP_SET + help + This option adds the hash:net,iface set type support, by which + one can store IPv4/IPv6 network address/prefix and + interface name pairs as elements in a set. + + To compile it as a module, choose M here. If unsure, say N. + config IP_SET_LIST_SET tristate "list:set set support" depends on IP_SET diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile index 5adbdab67bd2..6e965ecd5444 100644 --- a/net/netfilter/ipset/Makefile +++ b/net/netfilter/ipset/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_IP_SET_HASH_IPPORTIP) += ip_set_hash_ipportip.o obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o +obj-$(CONFIG_IP_SET_HASH_NETIFACE) += ip_set_hash_netiface.o # list types obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c new file mode 100644 index 000000000000..51e5df12bd00 --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -0,0 +1,762 @@ +/* Copyright (C) 2011 Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Kernel module implementing an IP set type: the hash:net,iface type */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("hash:net,iface type of IP sets"); +MODULE_ALIAS("ip_set_hash:net,iface"); + +/* Interface name rbtree */ + +struct iface_node { + struct rb_node node; + char iface[IFNAMSIZ]; +}; + +#define iface_data(n) (rb_entry(n, struct iface_node, node)->iface) + +static inline long +ifname_compare(const char *_a, const char *_b) +{ + const long *a = (const long *)_a; + const long *b = (const long *)_b; + + BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long)); + if (a[0] != b[0]) + return a[0] - b[0]; + if (IFNAMSIZ > sizeof(long)) { + if (a[1] != b[1]) + return a[1] - b[1]; + } + if (IFNAMSIZ > 2 * sizeof(long)) { + if (a[2] != b[2]) + return a[2] - b[2]; + } + if (IFNAMSIZ > 3 * sizeof(long)) { + if (a[3] != b[3]) + return a[3] - b[3]; + } + return 0; +} + +static void +rbtree_destroy(struct rb_root *root) +{ + struct rb_node *p, *n = root->rb_node; + struct iface_node *node; + + /* Non-recursive destroy, like in ext3 */ + while (n) { + if (n->rb_left) { + n = n->rb_left; + continue; + } + if (n->rb_right) { + n = n->rb_right; + continue; + } + p = rb_parent(n); + node = rb_entry(n, struct iface_node, node); + if (!p) + *root = RB_ROOT; + else if (p->rb_left == n) + p->rb_left = NULL; + else if (p->rb_right == n) + p->rb_right = NULL; + + kfree(node); + n = p; + } +} + +static int +iface_test(struct rb_root *root, const char **iface) +{ + struct rb_node *n = root->rb_node; + + while (n) { + const char *d = iface_data(n); + int res = ifname_compare(*iface, d); + + if (res < 0) + n = n->rb_left; + else if (res > 0) + n = n->rb_right; + else { + *iface = d; + return 1; + } + } + return 0; +} + +static int +iface_add(struct rb_root *root, const char **iface) +{ + struct rb_node **n = &(root->rb_node), *p = NULL; + struct iface_node *d; + + while (*n) { + char *ifname = iface_data(*n); + int res = ifname_compare(*iface, ifname); + + p = *n; + if (res < 0) + n = &((*n)->rb_left); + else if (res > 0) + n = &((*n)->rb_right); + else { + *iface = ifname; + return 0; + } + } + + d = kzalloc(sizeof(*d), GFP_ATOMIC); + if (!d) + return -ENOMEM; + strcpy(d->iface, *iface); + + rb_link_node(&d->node, p, n); + rb_insert_color(&d->node, root); + + *iface = d->iface; + return 0; +} + +/* Type specific function prefix */ +#define TYPE hash_netiface + +static bool +hash_netiface_same_set(const struct ip_set *a, const struct ip_set *b); + +#define hash_netiface4_same_set hash_netiface_same_set +#define hash_netiface6_same_set hash_netiface_same_set + +#define STREQ(a, b) (strcmp(a, b) == 0) + +/* The type variant functions: IPv4 */ + +/* Member elements without timeout */ +struct hash_netiface4_elem { + __be32 ip; + const char *iface; + u8 physdev; + u8 cidr; + u16 padding; +}; + +/* Member elements with timeout support */ +struct hash_netiface4_telem { + __be32 ip; + const char *iface; + u8 physdev; + u8 cidr; + u16 padding; + unsigned long timeout; +}; + +static inline bool +hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1, + const struct hash_netiface4_elem *ip2) +{ + return ip1->ip == ip2->ip && + ip1->cidr == ip2->cidr && + ip1->physdev == ip2->physdev && + ip1->iface == ip2->iface; +} + +static inline bool +hash_netiface4_data_isnull(const struct hash_netiface4_elem *elem) +{ + return elem->cidr == 0; +} + +static inline void +hash_netiface4_data_copy(struct hash_netiface4_elem *dst, + const struct hash_netiface4_elem *src) { + dst->ip = src->ip; + dst->cidr = src->cidr; + dst->physdev = src->physdev; + dst->iface = src->iface; +} + +static inline void +hash_netiface4_data_netmask(struct hash_netiface4_elem *elem, u8 cidr) +{ + elem->ip &= ip_set_netmask(cidr); + elem->cidr = cidr; +} + +static inline void +hash_netiface4_data_zero_out(struct hash_netiface4_elem *elem) +{ + elem->cidr = 0; +} + +static bool +hash_netiface4_data_list(struct sk_buff *skb, + const struct hash_netiface4_elem *data) +{ + u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0; + + 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); + return 0; + +nla_put_failure: + return 1; +} + +static bool +hash_netiface4_data_tlist(struct sk_buff *skb, + const struct hash_netiface4_elem *data) +{ + const struct hash_netiface4_telem *tdata = + (const struct hash_netiface4_telem *)data; + u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0; + + 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_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout))); + + return 0; + +nla_put_failure: + return 1; +} + +#define IP_SET_HASH_WITH_NETS +#define IP_SET_HASH_WITH_RBTREE + +#define PF 4 +#define HOST_MASK 32 +#include + +static inline void +hash_netiface4_data_next(struct ip_set_hash *h, + const struct hash_netiface4_elem *d) +{ + h->next.ip = ntohl(d->ip); +} + +static int +hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) +{ + struct ip_set_hash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netiface4_elem data = { + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + }; + int ret; + + if (data.cidr == 0) + return -EINVAL; + if (adt == IPSET_TEST) + data.cidr = HOST_MASK; + + ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip); + data.ip &= ip_set_netmask(data.cidr); + +#define IFACE(dir) (par->dir ? par->dir->name : NULL) +#define PHYSDEV(dir) (nf_bridge->dir ? nf_bridge->dir->name : NULL) +#define SRCDIR (opt->flags & IPSET_DIM_TWO_SRC) + + if (opt->cmdflags & IPSET_FLAG_PHYSDEV) { +#ifdef CONFIG_BRIDGE_NETFILTER + const struct nf_bridge_info *nf_bridge = skb->nf_bridge; + + if (!nf_bridge) + return -EINVAL; + data.iface = SRCDIR ? PHYSDEV(physindev): PHYSDEV(physoutdev); + data.physdev = 1; +#else + data.iface = NULL; +#endif + } else + data.iface = SRCDIR ? IFACE(in) : IFACE(out); + + if (!data.iface) + return -EINVAL; + ret = iface_test(&h->rbtree, &data.iface); + if (adt == IPSET_ADD) { + if (!ret) { + ret = iface_add(&h->rbtree, &data.iface); + if (ret) + return ret; + } + } else if (!ret) + return ret; + + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); +} + +static int +hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) +{ + struct ip_set_hash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netiface4_elem data = { .cidr = HOST_MASK }; + u32 ip = 0, ip_to, last; + u32 timeout = h->timeout; + char iface[IFNAMSIZ] = {}; + int ret; + + if (unlikely(!tb[IPSET_ATTR_IP] || + !tb[IPSET_ATTR_IFACE] || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); + 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; + } + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout(h->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + } + + strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE])); + data.iface = iface; + ret = iface_test(&h->rbtree, &data.iface); + if (adt == IPSET_ADD) { + if (!ret) { + ret = iface_add(&h->rbtree, &data.iface); + if (ret) + return ret; + } + } else if (!ret) + return ret; + + if (tb[IPSET_ATTR_CADT_FLAGS]) { + u32 flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (flags & IPSET_FLAG_PHYSDEV) + data.physdev = 1; + } + + if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) { + data.ip = htonl(ip & ip_set_hostmask(data.cidr)); + ret = adtfn(set, &data, timeout, flags); + return ip_set_eexist(ret, flags) ? 0 : ret; + } + + if (tb[IPSET_ATTR_IP_TO]) { + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); + if (ret) + return ret; + if (ip_to < ip) + swap(ip, ip_to); + if (ip + UINT_MAX == ip_to) + return -IPSET_ERR_HASH_RANGE; + } else { + ip_set_mask_from_to(ip, ip_to, data.cidr); + } + + 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); + ret = adtfn(set, &data, timeout, flags); + + if (ret && !ip_set_eexist(ret, flags)) + return ret; + else + ret = 0; + ip = last + 1; + } + return ret; +} + +static bool +hash_netiface_same_set(const struct ip_set *a, const struct ip_set *b) +{ + const struct ip_set_hash *x = a->data; + const struct ip_set_hash *y = b->data; + + /* Resizing changes htable_bits, so we ignore it */ + return x->maxelem == y->maxelem && + x->timeout == y->timeout; +} + +/* The type variant functions: IPv6 */ + +struct hash_netiface6_elem { + union nf_inet_addr ip; + const char *iface; + u8 physdev; + u8 cidr; + u16 padding; +}; + +struct hash_netiface6_telem { + union nf_inet_addr ip; + const char *iface; + u8 physdev; + u8 cidr; + u16 padding; + unsigned long timeout; +}; + +static inline bool +hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1, + const struct hash_netiface6_elem *ip2) +{ + return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && + ip1->cidr == ip2->cidr && + ip1->physdev == ip2->physdev && + ip1->iface == ip2->iface; +} + +static inline bool +hash_netiface6_data_isnull(const struct hash_netiface6_elem *elem) +{ + return elem->cidr == 0; +} + +static inline void +hash_netiface6_data_copy(struct hash_netiface6_elem *dst, + const struct hash_netiface6_elem *src) +{ + memcpy(dst, src, sizeof(*dst)); +} + +static inline void +hash_netiface6_data_zero_out(struct hash_netiface6_elem *elem) +{ +} + +static inline void +ip6_netmask(union nf_inet_addr *ip, u8 prefix) +{ + ip->ip6[0] &= ip_set_netmask6(prefix)[0]; + ip->ip6[1] &= ip_set_netmask6(prefix)[1]; + ip->ip6[2] &= ip_set_netmask6(prefix)[2]; + ip->ip6[3] &= ip_set_netmask6(prefix)[3]; +} + +static inline void +hash_netiface6_data_netmask(struct hash_netiface6_elem *elem, u8 cidr) +{ + ip6_netmask(&elem->ip, cidr); + elem->cidr = cidr; +} + +static bool +hash_netiface6_data_list(struct sk_buff *skb, + const struct hash_netiface6_elem *data) +{ + u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0; + + 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); + return 0; + +nla_put_failure: + return 1; +} + +static bool +hash_netiface6_data_tlist(struct sk_buff *skb, + const struct hash_netiface6_elem *data) +{ + const struct hash_netiface6_telem *e = + (const struct hash_netiface6_telem *)data; + u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0; + + 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_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout))); + return 0; + +nla_put_failure: + return 1; +} + +#undef PF +#undef HOST_MASK + +#define PF 6 +#define HOST_MASK 128 +#include + +static inline void +hash_netiface6_data_next(struct ip_set_hash *h, + const struct hash_netiface6_elem *d) +{ +} + +static int +hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, + enum ipset_adt adt, const struct ip_set_adt_opt *opt) +{ + struct ip_set_hash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netiface6_elem data = { + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + }; + int ret; + + if (data.cidr == 0) + return -EINVAL; + if (adt == IPSET_TEST) + data.cidr = HOST_MASK; + + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6); + ip6_netmask(&data.ip, data.cidr); + + if (opt->cmdflags & IPSET_FLAG_PHYSDEV) { +#ifdef CONFIG_BRIDGE_NETFILTER + const struct nf_bridge_info *nf_bridge = skb->nf_bridge; + + if (!nf_bridge) + return -EINVAL; + data.iface = SRCDIR ? PHYSDEV(physindev): PHYSDEV(physoutdev); + data.physdev = 1; +#else + data.iface = NULL; +#endif + } else + data.iface = SRCDIR ? IFACE(in) : IFACE(out); + + if (!data.iface) + return -EINVAL; + ret = iface_test(&h->rbtree, &data.iface); + if (adt == IPSET_ADD) { + if (!ret) { + ret = iface_add(&h->rbtree, &data.iface); + if (ret) + return ret; + } + } else if (!ret) + return ret; + + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); +} + +static int +hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) +{ + struct ip_set_hash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netiface6_elem data = { .cidr = HOST_MASK }; + u32 timeout = h->timeout; + char iface[IFNAMSIZ] = {}; + int ret; + + if (unlikely(!tb[IPSET_ATTR_IP] || + !tb[IPSET_ATTR_IFACE] || + !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; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip); + 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_TIMEOUT]) { + if (!with_timeout(h->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + } + + strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE])); + data.iface = iface; + ret = iface_test(&h->rbtree, &data.iface); + if (adt == IPSET_ADD) { + if (!ret) { + ret = iface_add(&h->rbtree, &data.iface); + if (ret) + return ret; + } + } else if (!ret) + return ret; + + if (tb[IPSET_ATTR_CADT_FLAGS]) { + u32 flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (flags & IPSET_FLAG_PHYSDEV) + data.physdev = 1; + } + + ret = adtfn(set, &data, timeout, flags); + + return ip_set_eexist(ret, flags) ? 0 : ret; +} + +/* Create hash:ip type of sets */ + +static int +hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags) +{ + struct ip_set_hash *h; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + u8 hbits; + + if (!(set->family == AF_INET || set->family == AF_INET6)) + return -IPSET_ERR_INVALID_FAMILY; + + if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_HASHSIZE]) { + hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); + if (hashsize < IPSET_MIMINAL_HASHSIZE) + hashsize = IPSET_MIMINAL_HASHSIZE; + } + + if (tb[IPSET_ATTR_MAXELEM]) + maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); + + h = kzalloc(sizeof(*h) + + sizeof(struct ip_set_hash_nets) + * (set->family == AF_INET ? 32 : 128), GFP_KERNEL); + if (!h) + return -ENOMEM; + + h->maxelem = maxelem; + get_random_bytes(&h->initval, sizeof(h->initval)); + h->timeout = IPSET_NO_TIMEOUT; + + hbits = htable_bits(hashsize); + h->table = ip_set_alloc( + sizeof(struct htable) + + jhash_size(hbits) * sizeof(struct hbucket)); + if (!h->table) { + kfree(h); + return -ENOMEM; + } + h->table->htable_bits = hbits; + h->rbtree = RB_ROOT; + + set->data = h; + + if (tb[IPSET_ATTR_TIMEOUT]) { + h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + + set->variant = set->family == AF_INET + ? &hash_netiface4_tvariant : &hash_netiface6_tvariant; + + if (set->family == AF_INET) + hash_netiface4_gc_init(set); + else + hash_netiface6_gc_init(set); + } else { + set->variant = set->family == AF_INET + ? &hash_netiface4_variant : &hash_netiface6_variant; + } + + pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", + set->name, jhash_size(h->table->htable_bits), + h->table->htable_bits, h->maxelem, set->data, h->table); + + return 0; +} + +static struct ip_set_type hash_netiface_type __read_mostly = { + .name = "hash:net,iface", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_IP | IPSET_TYPE_IFACE, + .dimension = IPSET_DIM_TWO, + .family = AF_UNSPEC, + .revision_min = 0, + .create = hash_netiface_create, + .create_policy = { + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, + [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + }, + .adt_policy = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, + [IPSET_ATTR_IFACE] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1 }, + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, + [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, + }, + .me = THIS_MODULE, +}; + +static int __init +hash_netiface_init(void) +{ + return ip_set_type_register(&hash_netiface_type); +} + +static void __exit +hash_netiface_fini(void) +{ + ip_set_type_unregister(&hash_netiface_type); +} + +module_init(hash_netiface_init); +module_exit(hash_netiface_fini); -- cgit v1.2.3 From 15b4d93f0316caec44e07255c1d73bde4fac12e4 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 16 Jun 2011 19:01:26 +0200 Subject: netfilter: ipset: whitespace and coding fixes detected by checkpatch.pl Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set.h | 6 +++--- include/linux/netfilter/ipset/ip_set_ahash.h | 2 +- include/linux/netfilter/ipset/ip_set_timeout.h | 2 +- include/linux/netfilter/ipset/pfxlen.h | 2 +- net/netfilter/ipset/ip_set_bitmap_ipmac.c | 3 ++- net/netfilter/ipset/ip_set_core.c | 4 ++-- net/netfilter/ipset/ip_set_hash_net.c | 4 ++-- net/netfilter/ipset/ip_set_hash_netiface.c | 26 +++++++++++++------------- net/netfilter/ipset/ip_set_hash_netport.c | 2 +- net/netfilter/ipset/pfxlen.c | 2 +- net/netfilter/xt_set.c | 2 +- 11 files changed, 28 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index e40917334d04..3540c6e262f7 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -331,7 +331,7 @@ struct ip_set { /* register and unregister set references */ extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set); extern void ip_set_put_byindex(ip_set_id_t index); -extern const char * ip_set_name_byindex(ip_set_id_t index); +extern const char *ip_set_name_byindex(ip_set_id_t index); extern ip_set_id_t ip_set_nfnl_get(const char *name); extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index); extern void ip_set_nfnl_put(ip_set_id_t index); @@ -349,7 +349,7 @@ extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb, const struct ip_set_adt_opt *opt); /* Utility functions */ -extern void * ip_set_alloc(size_t size); +extern void *ip_set_alloc(size_t size); extern void ip_set_free(void *members); extern int ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr); extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr); @@ -359,7 +359,7 @@ ip_set_get_hostipaddr4(struct nlattr *nla, u32 *ipaddr) { __be32 ip; int ret = ip_set_get_ipaddr4(nla, &ip); - + if (ret) return ret; *ipaddr = ntohl(ip); diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index 8a0999a8baa1..c5b06aaa205c 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -43,7 +43,7 @@ struct htable { struct hbucket bucket[0]; /* hashtable buckets */ }; -#define hbucket(h, i) &((h)->bucket[i]) +#define hbucket(h, i) (&((h)->bucket[i])) /* Book-keeping of the prefixes added to the set */ struct ip_set_hash_nets { diff --git a/include/linux/netfilter/ipset/ip_set_timeout.h b/include/linux/netfilter/ipset/ip_set_timeout.h index bae086af0e44..47923205a4ad 100644 --- a/include/linux/netfilter/ipset/ip_set_timeout.h +++ b/include/linux/netfilter/ipset/ip_set_timeout.h @@ -78,7 +78,7 @@ ip_set_timeout_set(u32 timeout) static inline u32 ip_set_timeout_get(unsigned long timeout) { - return timeout == IPSET_ELEM_PERMANENT ? 0 : + return timeout == IPSET_ELEM_PERMANENT ? 0 : jiffies_to_msecs(timeout - jiffies)/1000; } diff --git a/include/linux/netfilter/ipset/pfxlen.h b/include/linux/netfilter/ipset/pfxlen.h index d55a6ccf662e..199fd11fedc0 100644 --- a/include/linux/netfilter/ipset/pfxlen.h +++ b/include/linux/netfilter/ipset/pfxlen.h @@ -2,7 +2,7 @@ #define _PFXLEN_H #include -#include +#include #include /* Prefixlen maps, by Jan Engelhardt */ diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index 51ab66435a0a..56096f544978 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -635,7 +635,8 @@ static struct ip_set_type bitmap_ipmac_type = { }, .adt_policy = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, - [IPSET_ATTR_ETHER] = { .type = NLA_BINARY, .len = ETH_ALEN }, + [IPSET_ATTR_ETHER] = { .type = NLA_BINARY, + .len = ETH_ALEN }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }, diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 80a1262104bf..c012985a5a26 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -683,8 +683,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, if (attr[IPSET_ATTR_DATA] && nla_parse_nested(tb, IPSET_ATTR_CREATE_MAX, attr[IPSET_ATTR_DATA], set->type->create_policy)) { - ret = -IPSET_ERR_PROTOCOL; - goto put_out; + ret = -IPSET_ERR_PROTOCOL; + goto put_out; } ret = set->type->create(set, tb, flags); diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index 050163fb6094..2d4b1f48e8c9 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -187,7 +187,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], return -IPSET_ERR_TIMEOUT; timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - + if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) { data.ip = htonl(ip & ip_set_hostmask(data.cidr)); ret = adtfn(set, &data, timeout, flags); @@ -205,7 +205,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], return -IPSET_ERR_HASH_RANGE; } if (retried) - ip = h->next.ip; + ip = h->next.ip; while (!after(ip, ip_to)) { data.ip = htonl(ip); last = ip_set_range_to_cidr(ip, ip_to, &data.cidr); diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index 51e5df12bd00..3d6c53b6211a 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -100,7 +100,7 @@ iface_test(struct rb_root *root, const char **iface) while (n) { const char *d = iface_data(n); int res = ifname_compare(*iface, d); - + if (res < 0) n = n->rb_left; else if (res > 0) @@ -118,7 +118,7 @@ iface_add(struct rb_root *root, const char **iface) { struct rb_node **n = &(root->rb_node), *p = NULL; struct iface_node *d; - + while (*n) { char *ifname = iface_data(*n); int res = ifname_compare(*iface, ifname); @@ -296,10 +296,10 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb, if (opt->cmdflags & IPSET_FLAG_PHYSDEV) { #ifdef CONFIG_BRIDGE_NETFILTER const struct nf_bridge_info *nf_bridge = skb->nf_bridge; - + if (!nf_bridge) return -EINVAL; - data.iface = SRCDIR ? PHYSDEV(physindev): PHYSDEV(physoutdev); + data.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev); data.physdev = 1; #else data.iface = NULL; @@ -350,7 +350,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) - return -IPSET_ERR_INVALID_CIDR; + return -IPSET_ERR_INVALID_CIDR; } if (tb[IPSET_ATTR_TIMEOUT]) { @@ -359,7 +359,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE])); + strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE])); data.iface = iface; ret = iface_test(&h->rbtree, &data.iface); if (adt == IPSET_ADD) { @@ -372,8 +372,8 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; if (tb[IPSET_ATTR_CADT_FLAGS]) { - u32 flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - if (flags & IPSET_FLAG_PHYSDEV) + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_PHYSDEV) data.physdev = 1; } @@ -559,10 +559,10 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb, if (opt->cmdflags & IPSET_FLAG_PHYSDEV) { #ifdef CONFIG_BRIDGE_NETFILTER const struct nf_bridge_info *nf_bridge = skb->nf_bridge; - + if (!nf_bridge) return -EINVAL; - data.iface = SRCDIR ? PHYSDEV(physindev): PHYSDEV(physoutdev); + data.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev); data.physdev = 1; #else data.iface = NULL; @@ -623,7 +623,7 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE])); + strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE])); data.iface = iface; ret = iface_test(&h->rbtree, &data.iface); if (adt == IPSET_ADD) { @@ -636,8 +636,8 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[], return ret; if (tb[IPSET_ATTR_CADT_FLAGS]) { - u32 flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - if (flags & IPSET_FLAG_PHYSDEV) + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_PHYSDEV) data.physdev = 1; } diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index d7710a9fb7c7..fe203d12f56b 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -199,7 +199,7 @@ hash_netport4_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) - return -IPSET_ERR_INVALID_CIDR; + return -IPSET_ERR_INVALID_CIDR; } if (tb[IPSET_ATTR_PORT]) diff --git a/net/netfilter/ipset/pfxlen.c b/net/netfilter/ipset/pfxlen.c index b57a85673de7..bd13d66220f1 100644 --- a/net/netfilter/ipset/pfxlen.c +++ b/net/netfilter/ipset/pfxlen.c @@ -148,7 +148,7 @@ const union nf_inet_addr ip_set_netmask_map[] = { EXPORT_SYMBOL_GPL(ip_set_netmask_map); #undef E -#define E(a, b, c, d) \ +#define E(a, b, c, d) \ {.ip6 = { (__force __be32) a, (__force __be32) b, \ (__force __be32) c, (__force __be32) d, \ } } diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c index 453847f293d3..19461c462dbd 100644 --- a/net/netfilter/xt_set.c +++ b/net/netfilter/xt_set.c @@ -37,7 +37,7 @@ match_set(ip_set_id_t index, const struct sk_buff *skb, return inv; } -#define ADT_OPT(n, f, d, fs, cfs, t) \ +#define ADT_OPT(n, f, d, fs, cfs, t) \ const struct ip_set_adt_opt n = { \ .family = f, \ .dim = d, \ -- cgit v1.2.3 From b5199515c25cca622495eb9c6a8a1d275e775088 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 16 Jun 2011 16:22:08 +0200 Subject: clocksource: Make watchdog robust vs. interruption The clocksource watchdog code is interruptible and it has been observed that this can trigger false positives which disable the TSC. The reason is that an interrupt storm or a long running interrupt handler between the read of the watchdog source and the read of the TSC brings the two far enough apart that the delta is larger than the unstable treshold. Move both reads into a short interrupt disabled region to avoid that. Reported-and-tested-by: Vernon Mauery Signed-off-by: Thomas Gleixner Cc: stable@kernel.org --- include/linux/clocksource.h | 1 + kernel/time/clocksource.c | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index d4646b48dc4a..18a1baf31f2d 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -188,6 +188,7 @@ struct clocksource { #ifdef CONFIG_CLOCKSOURCE_WATCHDOG /* Watchdog related data, used by the framework */ struct list_head wd_list; + cycle_t cs_last; cycle_t wd_last; #endif } ____cacheline_aligned; diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 1c95fd677328..e0980f0d9a0a 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -185,7 +185,6 @@ static struct clocksource *watchdog; static struct timer_list watchdog_timer; static DECLARE_WORK(watchdog_work, clocksource_watchdog_work); static DEFINE_SPINLOCK(watchdog_lock); -static cycle_t watchdog_last; static int watchdog_running; static int clocksource_watchdog_kthread(void *data); @@ -254,11 +253,6 @@ static void clocksource_watchdog(unsigned long data) if (!watchdog_running) goto out; - wdnow = watchdog->read(watchdog); - wd_nsec = clocksource_cyc2ns((wdnow - watchdog_last) & watchdog->mask, - watchdog->mult, watchdog->shift); - watchdog_last = wdnow; - list_for_each_entry(cs, &watchdog_list, wd_list) { /* Clocksource already marked unstable? */ @@ -268,19 +262,28 @@ static void clocksource_watchdog(unsigned long data) continue; } + local_irq_disable(); csnow = cs->read(cs); + wdnow = watchdog->read(watchdog); + local_irq_enable(); /* Clocksource initialized ? */ if (!(cs->flags & CLOCK_SOURCE_WATCHDOG)) { cs->flags |= CLOCK_SOURCE_WATCHDOG; - cs->wd_last = csnow; + cs->wd_last = wdnow; + cs->cs_last = csnow; continue; } - /* Check the deviation from the watchdog clocksource. */ - cs_nsec = clocksource_cyc2ns((csnow - cs->wd_last) & + wd_nsec = clocksource_cyc2ns((wdnow - cs->wd_last) & watchdog->mask, + watchdog->mult, watchdog->shift); + + cs_nsec = clocksource_cyc2ns((csnow - cs->cs_last) & cs->mask, cs->mult, cs->shift); - cs->wd_last = csnow; + cs->cs_last = csnow; + cs->wd_last = wdnow; + + /* Check the deviation from the watchdog clocksource. */ if (abs(cs_nsec - wd_nsec) > WATCHDOG_THRESHOLD) { clocksource_unstable(cs, cs_nsec - wd_nsec); continue; @@ -318,7 +321,6 @@ static inline void clocksource_start_watchdog(void) return; init_timer(&watchdog_timer); watchdog_timer.function = clocksource_watchdog; - watchdog_last = watchdog->read(watchdog); watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL; add_timer_on(&watchdog_timer, cpumask_first(cpu_online_mask)); watchdog_running = 1; -- cgit v1.2.3 From b2a66aad8620337e38d6692f03d94a03d5129840 Mon Sep 17 00:00:00 2001 From: Antti Julku Date: Wed, 15 Jun 2011 12:01:14 +0300 Subject: Bluetooth: Move blacklisting functions to hci_core Move blacklisting functions to hci_core.c, so that they can be used by both management interface and hci socket interface. Signed-off-by: Antti Julku Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 2 + net/bluetooth/hci_core.c | 79 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_sock.c | 70 +++-------------------------------- 3 files changed, 87 insertions(+), 64 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index fe05946c8f0b..26233d4d371c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -529,6 +529,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_uuids_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f62ca1935f5a..8f5bee15e872 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1205,6 +1205,85 @@ 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 list_head *p; + + list_for_each(p, &hdev->blacklist) { + struct bdaddr_list *b; + + b = list_entry(p, struct bdaddr_list, list); + + if (bacmp(bdaddr, &b->bdaddr) == 0) + return b; + } + + return NULL; +} + +int hci_blacklist_clear(struct hci_dev *hdev) +{ + struct list_head *p, *n; + + list_for_each_safe(p, n, &hdev->blacklist) { + struct bdaddr_list *b; + + b = list_entry(p, struct bdaddr_list, list); + + list_del(p); + kfree(b); + } + + return 0; +} + +int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct bdaddr_list *entry; + + hci_dev_lock(hdev); + + if (bacmp(bdaddr, BDADDR_ANY) == 0) + return -EBADF; + + if (hci_blacklist_lookup(hdev, bdaddr)) + return -EEXIST; + + entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + bacpy(&entry->bdaddr, bdaddr); + + list_add(&entry->list, &hdev->blacklist); + + hci_dev_unlock(hdev); + + return 0; +} + +int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct bdaddr_list *entry; + + hci_dev_lock(hdev); + + if (bacmp(bdaddr, BDADDR_ANY) == 0) + return hci_blacklist_clear(hdev); + + entry = hci_blacklist_lookup(hdev, bdaddr); + if (!entry) + return -ENOENT; + + list_del(&entry->list); + kfree(entry); + + hci_dev_unlock(hdev); + + return 0; +} + static void hci_clear_adv_cache(unsigned long arg) { struct hci_dev *hdev = (void *) arg; diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 295e4a88fff8..ff02cf5e77cc 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -180,82 +180,24 @@ static int hci_sock_release(struct socket *sock) return 0; } -struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct list_head *p; - - list_for_each(p, &hdev->blacklist) { - struct bdaddr_list *b; - - b = list_entry(p, struct bdaddr_list, list); - - if (bacmp(bdaddr, &b->bdaddr) == 0) - return b; - } - - return NULL; -} - -static int hci_blacklist_add(struct hci_dev *hdev, void __user *arg) +static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; - struct bdaddr_list *entry; if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) return -EFAULT; - if (bacmp(&bdaddr, BDADDR_ANY) == 0) - return -EBADF; - - if (hci_blacklist_lookup(hdev, &bdaddr)) - return -EEXIST; - - entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - bacpy(&entry->bdaddr, &bdaddr); - - list_add(&entry->list, &hdev->blacklist); - - return 0; -} - -int hci_blacklist_clear(struct hci_dev *hdev) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->blacklist) { - struct bdaddr_list *b; - - b = list_entry(p, struct bdaddr_list, list); - - list_del(p); - kfree(b); - } - - return 0; + return hci_blacklist_add(hdev, &bdaddr); } -static int hci_blacklist_del(struct hci_dev *hdev, void __user *arg) +static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; - struct bdaddr_list *entry; if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) return -EFAULT; - if (bacmp(&bdaddr, BDADDR_ANY) == 0) - return hci_blacklist_clear(hdev); - - entry = hci_blacklist_lookup(hdev, &bdaddr); - if (!entry) - return -ENOENT; - - list_del(&entry->list); - kfree(entry); - - return 0; + return hci_blacklist_del(hdev, &bdaddr); } /* Ioctls that require bound socket */ @@ -290,12 +232,12 @@ static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsign case HCIBLOCKADDR: if (!capable(CAP_NET_ADMIN)) return -EACCES; - return hci_blacklist_add(hdev, (void __user *) arg); + return hci_sock_blacklist_add(hdev, (void __user *) arg); case HCIUNBLOCKADDR: if (!capable(CAP_NET_ADMIN)) return -EACCES; - return hci_blacklist_del(hdev, (void __user *) arg); + return hci_sock_blacklist_del(hdev, (void __user *) arg); default: if (hdev->ioctl) -- cgit v1.2.3 From c16d51a32bbb61ac8fd96f78b5ce2fccfe0fb4c3 Mon Sep 17 00:00:00 2001 From: Shreshtha Kumar Sahu Date: Mon, 13 Jun 2011 10:11:33 +0200 Subject: amba pl011: workaround for uart registers lockup This workaround aims to break the deadlock situation which raises during continuous transfer of data for long duration over uart with hardware flow control. It is observed that CTS interrupt cannot be cleared in uart interrupt register (ICR). Hence further transfer over uart gets blocked. It is seen that during such deadlock condition ICR don't get cleared even on multiple write. This leads pass_counter to decrease and finally reach zero. This can be taken as trigger point to run this UART_BT_WA. Workaround backups the register configuration, does soft reset of UART using BIT-0 of PRCC_K_SOFTRST_SET/CLEAR registers and restores the registers. This patch also provides support for uart init and exit function calls if present. Signed-off-by: Shreshtha Kumar Sahu Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 123 +++++++++++++++++++++++++++++++++++++++- include/linux/amba/serial.h | 3 + 2 files changed, 125 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 8dc0541feecc..f5f6831b0a64 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -65,6 +66,30 @@ #define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) #define UART_DUMMY_DR_RX (1 << 16) + +#define UART_WA_SAVE_NR 14 + +static void pl011_lockup_wa(unsigned long data); +static const u32 uart_wa_reg[UART_WA_SAVE_NR] = { + ST_UART011_DMAWM, + ST_UART011_TIMEOUT, + ST_UART011_LCRH_RX, + UART011_IBRD, + UART011_FBRD, + ST_UART011_LCRH_TX, + UART011_IFLS, + ST_UART011_XFCR, + ST_UART011_XON1, + ST_UART011_XON2, + ST_UART011_XOFF1, + ST_UART011_XOFF2, + UART011_CR, + UART011_IMSC +}; + +static u32 uart_wa_regdata[UART_WA_SAVE_NR]; +static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa, 0); + /* There is by now at least one vendor with differing details, so handle it */ struct vendor_data { unsigned int ifls; @@ -72,6 +97,7 @@ struct vendor_data { unsigned int lcrh_tx; unsigned int lcrh_rx; bool oversampling; + bool interrupt_may_hang; /* vendor-specific */ bool dma_threshold; }; @@ -90,9 +116,12 @@ static struct vendor_data vendor_st = { .lcrh_tx = ST_UART011_LCRH_TX, .lcrh_rx = ST_UART011_LCRH_RX, .oversampling = true, + .interrupt_may_hang = true, .dma_threshold = true, }; +static struct uart_amba_port *amba_ports[UART_NR]; + /* Deals with DMA transactions */ struct pl011_sgbuf { @@ -132,6 +161,7 @@ struct uart_amba_port { unsigned int lcrh_rx; /* vendor-specific */ bool autorts; char type[12]; + bool interrupt_may_hang; /* vendor-specific */ #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ bool using_tx_dma; @@ -1008,6 +1038,68 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) #endif +/* + * pl011_lockup_wa + * This workaround aims to break the deadlock situation + * when after long transfer over uart in hardware flow + * control, uart interrupt registers cannot be cleared. + * Hence uart transfer gets blocked. + * + * It is seen that during such deadlock condition ICR + * don't get cleared even on multiple write. This leads + * pass_counter to decrease and finally reach zero. This + * can be taken as trigger point to run this UART_BT_WA. + * + */ +static void pl011_lockup_wa(unsigned long data) +{ + struct uart_amba_port *uap = amba_ports[0]; + void __iomem *base = uap->port.membase; + struct circ_buf *xmit = &uap->port.state->xmit; + struct tty_struct *tty = uap->port.state->port.tty; + int buf_empty_retries = 200; + int loop; + + /* Stop HCI layer from submitting data for tx */ + tty->hw_stopped = 1; + while (!uart_circ_empty(xmit)) { + if (buf_empty_retries-- == 0) + break; + udelay(100); + } + + /* Backup registers */ + for (loop = 0; loop < UART_WA_SAVE_NR; loop++) + uart_wa_regdata[loop] = readl(base + uart_wa_reg[loop]); + + /* Disable UART so that FIFO data is flushed out */ + writew(0x00, uap->port.membase + UART011_CR); + + /* Soft reset UART module */ + if (uap->port.dev->platform_data) { + struct amba_pl011_data *plat; + + plat = uap->port.dev->platform_data; + if (plat->reset) + plat->reset(); + } + + /* Restore registers */ + for (loop = 0; loop < UART_WA_SAVE_NR; loop++) + writew(uart_wa_regdata[loop] , + uap->port.membase + uart_wa_reg[loop]); + + /* Initialise the old status of the modem signals */ + uap->old_status = readw(uap->port.membase + UART01x_FR) & + UART01x_FR_MODEM_ANY; + + if (readl(base + UART011_MIS) & 0x2) + printk(KERN_EMERG "UART_BT_WA: ***FAILED***\n"); + + /* Start Tx/Rx */ + tty->hw_stopped = 0; +} + static void pl011_stop_tx(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; @@ -1158,8 +1250,11 @@ static irqreturn_t pl011_int(int irq, void *dev_id) if (status & UART011_TXIS) pl011_tx_chars(uap); - if (pass_counter-- == 0) + if (pass_counter-- == 0) { + if (uap->interrupt_may_hang) + tasklet_schedule(&pl011_lockup_tlet); break; + } status = readw(uap->port.membase + UART011_MIS); } while (status != 0); @@ -1339,6 +1434,14 @@ static int pl011_startup(struct uart_port *port) writew(uap->im, uap->port.membase + UART011_IMSC); spin_unlock_irq(&uap->port.lock); + if (uap->port.dev->platform_data) { + struct amba_pl011_data *plat; + + plat = uap->port.dev->platform_data; + if (plat->init) + plat->init(); + } + return 0; clk_dis: @@ -1394,6 +1497,15 @@ static void pl011_shutdown(struct uart_port *port) * Shut down the clock producer */ clk_disable(uap->clk); + + if (uap->port.dev->platform_data) { + struct amba_pl011_data *plat; + + plat = uap->port.dev->platform_data; + if (plat->exit) + plat->exit(); + } + } static void @@ -1700,6 +1812,14 @@ static int __init pl011_console_setup(struct console *co, char *options) if (!uap) return -ENODEV; + if (uap->port.dev->platform_data) { + struct amba_pl011_data *plat; + + plat = uap->port.dev->platform_data; + if (plat->init) + plat->init(); + } + uap->port.uartclk = clk_get_rate(uap->clk); if (options) @@ -1774,6 +1894,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->lcrh_rx = vendor->lcrh_rx; uap->lcrh_tx = vendor->lcrh_tx; uap->fifosize = vendor->fifosize; + uap->interrupt_may_hang = vendor->interrupt_may_hang; uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h index 5479fdc849e9..514ed45c462e 100644 --- a/include/linux/amba/serial.h +++ b/include/linux/amba/serial.h @@ -201,6 +201,9 @@ struct amba_pl011_data { bool (*dma_filter)(struct dma_chan *chan, void *filter_param); void *dma_rx_param; void *dma_tx_param; + void (*init) (void); + void (*exit) (void); + void (*reset) (void); }; #endif -- cgit v1.2.3 From 73ddff2bee159ffb580bd24faf625cd5e628f5ec Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 Jun 2011 11:20:14 +0200 Subject: job control: introduce JOBCTL_TRAP_STOP and use it for group stop trap do_signal_stop() implemented both normal group stop and trap for group stop while ptraced. This approach has been enough but scheduled changes require trap mechanism which can be used in more generic manner and using group stop trap for generic trap site simplifies both userland visible interface and implementation. This patch adds a new jobctl flag - JOBCTL_TRAP_STOP. When set, it triggers a trap site, which behaves like group stop trap, in get_signal_to_deliver() after checking for pending signals. While ptraced, do_signal_stop() doesn't stop itself. It initiates group stop if requested and schedules JOBCTL_TRAP_STOP and returns. The caller - get_signal_to_deliver() - is responsible for checking whether TRAP_STOP is pending afterwards and handling it. ptrace_attach() is updated to use JOBCTL_TRAP_STOP instead of JOBCTL_STOP_PENDING and __ptrace_unlink() to clear all pending trap bits and TRAPPING so that TRAP_STOP and future trap bits don't linger after detach. While at it, add proper function comment to do_signal_stop() and make it return bool. -v2: __ptrace_unlink() updated to clear JOBCTL_TRAP_MASK and TRAPPING instead of JOBCTL_PENDING_MASK. This avoids accidentally clearing JOBCTL_STOP_CONSUME. Spotted by Oleg. -v3: do_signal_stop() updated to return %false without dropping siglock while ptraced and TRAP_STOP check moved inside for(;;) loop after group stop participation. This avoids unnecessary relocking and also will help avoiding unnecessary traps by consuming group stop before handling pending traps. -v4: Jobctl trap handling moved into a separate function - do_jobctl_trap(). Signed-off-by: Tejun Heo Cc: Oleg Nesterov --- include/linux/sched.h | 6 +++- kernel/ptrace.c | 12 +++++-- kernel/signal.c | 90 ++++++++++++++++++++++++++++++++++----------------- 3 files changed, 75 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 5157bd9eee37..8bd84b83a35b 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1810,17 +1810,21 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t * #define JOBCTL_STOP_DEQUEUED_BIT 16 /* stop signal dequeued */ #define JOBCTL_STOP_PENDING_BIT 17 /* task should stop for group stop */ #define JOBCTL_STOP_CONSUME_BIT 18 /* consume group stop count */ +#define JOBCTL_TRAP_STOP_BIT 19 /* trap for STOP */ #define JOBCTL_TRAPPING_BIT 21 /* switching to TRACED */ #define JOBCTL_STOP_DEQUEUED (1 << JOBCTL_STOP_DEQUEUED_BIT) #define JOBCTL_STOP_PENDING (1 << JOBCTL_STOP_PENDING_BIT) #define JOBCTL_STOP_CONSUME (1 << JOBCTL_STOP_CONSUME_BIT) +#define JOBCTL_TRAP_STOP (1 << JOBCTL_TRAP_STOP_BIT) #define JOBCTL_TRAPPING (1 << JOBCTL_TRAPPING_BIT) -#define JOBCTL_PENDING_MASK JOBCTL_STOP_PENDING +#define JOBCTL_TRAP_MASK JOBCTL_TRAP_STOP +#define JOBCTL_PENDING_MASK (JOBCTL_STOP_PENDING | JOBCTL_TRAP_MASK) extern bool task_set_jobctl_pending(struct task_struct *task, unsigned int mask); +extern void task_clear_jobctl_trapping(struct task_struct *task); extern void task_clear_jobctl_pending(struct task_struct *task, unsigned int mask); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 7f05f3a1267b..45a8a4c5d8b2 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -82,6 +82,13 @@ void __ptrace_unlink(struct task_struct *child) spin_lock(&child->sighand->siglock); + /* + * Clear all pending traps and TRAPPING. TRAPPING should be + * cleared regardless of JOBCTL_STOP_PENDING. Do it explicitly. + */ + task_clear_jobctl_pending(child, JOBCTL_TRAP_MASK); + task_clear_jobctl_trapping(child); + /* * Reinstate JOBCTL_STOP_PENDING if group stop is in effect and * @child isn't dead. @@ -246,7 +253,7 @@ static int ptrace_attach(struct task_struct *task) spin_lock(&task->sighand->siglock); /* - * If the task is already STOPPED, set JOBCTL_STOP_PENDING and + * If the task is already STOPPED, set JOBCTL_TRAP_STOP and * TRAPPING, and kick it so that it transits to TRACED. TRAPPING * will be cleared if the child completes the transition or any * event which clears the group stop states happens. We'll wait @@ -263,8 +270,7 @@ static int ptrace_attach(struct task_struct *task) * in and out of STOPPED are protected by siglock. */ if (task_is_stopped(task) && - task_set_jobctl_pending(task, - JOBCTL_STOP_PENDING | JOBCTL_TRAPPING)) + task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING)) signal_wake_up(task, 1); spin_unlock(&task->sighand->siglock); diff --git a/kernel/signal.c b/kernel/signal.c index c99b8b5c0be7..b5f55ca1f43f 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -266,7 +266,7 @@ bool task_set_jobctl_pending(struct task_struct *task, unsigned int mask) * CONTEXT: * Must be called with @task->sighand->siglock held. */ -static void task_clear_jobctl_trapping(struct task_struct *task) +void task_clear_jobctl_trapping(struct task_struct *task) { if (unlikely(task->jobctl & JOBCTL_TRAPPING)) { task->jobctl &= ~JOBCTL_TRAPPING; @@ -1790,13 +1790,16 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info) /* * If @why is CLD_STOPPED, we're trapping to participate in a group * stop. Do the bookkeeping. Note that if SIGCONT was delievered - * while siglock was released for the arch hook, PENDING could be - * clear now. We act as if SIGCONT is received after TASK_TRACED - * is entered - ignore it. + * across siglock relocks since INTERRUPT was scheduled, PENDING + * could be clear now. We act as if SIGCONT is received after + * TASK_TRACED is entered - ignore it. */ if (why == CLD_STOPPED && (current->jobctl & JOBCTL_STOP_PENDING)) gstop_done = task_participate_group_stop(current); + /* any trap clears pending STOP trap */ + task_clear_jobctl_pending(current, JOBCTL_TRAP_STOP); + /* entering a trap, clear TRAPPING */ task_clear_jobctl_trapping(current); @@ -1888,13 +1891,30 @@ void ptrace_notify(int exit_code) spin_unlock_irq(¤t->sighand->siglock); } -/* - * This performs the stopping for SIGSTOP and other stop signals. - * We have to stop all threads in the thread group. - * Returns non-zero if we've actually stopped and released the siglock. - * Returns zero if we didn't stop and still hold the siglock. +/** + * do_signal_stop - handle group stop for SIGSTOP and other stop signals + * @signr: signr causing group stop if initiating + * + * If %JOBCTL_STOP_PENDING is not set yet, initiate group stop with @signr + * and participate in it. If already set, participate in the existing + * group stop. If participated in a group stop (and thus slept), %true is + * returned with siglock released. + * + * If ptraced, this function doesn't handle stop itself. Instead, + * %JOBCTL_TRAP_STOP is scheduled and %false is returned with siglock + * untouched. The caller must ensure that INTERRUPT trap handling takes + * places afterwards. + * + * CONTEXT: + * Must be called with @current->sighand->siglock held, which is released + * on %true return. + * + * RETURNS: + * %false if group stop is already cancelled or ptrace trap is scheduled. + * %true if participated in group stop. */ -static int do_signal_stop(int signr) +static bool do_signal_stop(int signr) + __releases(¤t->sighand->siglock) { struct signal_struct *sig = current->signal; @@ -1907,7 +1927,7 @@ static int do_signal_stop(int signr) if (!likely(current->jobctl & JOBCTL_STOP_DEQUEUED) || unlikely(signal_group_exit(sig))) - return 0; + return false; /* * There is no group stop already in progress. We must * initiate one now. @@ -1951,7 +1971,7 @@ static int do_signal_stop(int signr) } } } -retry: + if (likely(!task_ptrace(current))) { int notify = 0; @@ -1983,27 +2003,33 @@ retry: /* Now we don't run again until woken by SIGCONT or SIGKILL */ schedule(); - - spin_lock_irq(¤t->sighand->siglock); + return true; } else { - ptrace_stop(current->jobctl & JOBCTL_STOP_SIGMASK, - CLD_STOPPED, 0, NULL); - current->exit_code = 0; - } - - /* - * JOBCTL_STOP_PENDING could be set if another group stop has - * started since being woken up or ptrace wants us to transit - * between TASK_STOPPED and TRACED. Retry group stop. - */ - if (current->jobctl & JOBCTL_STOP_PENDING) { - WARN_ON_ONCE(!(current->jobctl & JOBCTL_STOP_SIGMASK)); - goto retry; + /* + * While ptraced, group stop is handled by STOP trap. + * Schedule it and let the caller deal with it. + */ + task_set_jobctl_pending(current, JOBCTL_TRAP_STOP); + return false; } +} - spin_unlock_irq(¤t->sighand->siglock); +/** + * do_jobctl_trap - take care of ptrace jobctl traps + * + * It is currently used only to trap for group stop while ptraced. + * + * CONTEXT: + * Must be called with @current->sighand->siglock held, which may be + * released and re-acquired before returning with intervening sleep. + */ +static void do_jobctl_trap(void) +{ + int signr = current->jobctl & JOBCTL_STOP_SIGMASK; - return 1; + WARN_ON_ONCE(!signr); + ptrace_stop(signr, CLD_STOPPED, 0, NULL); + current->exit_code = 0; } static int ptrace_signal(int signr, siginfo_t *info, @@ -2110,6 +2136,12 @@ relock: do_signal_stop(0)) goto relock; + if (unlikely(current->jobctl & JOBCTL_TRAP_MASK)) { + do_jobctl_trap(); + spin_unlock_irq(&sighand->siglock); + goto relock; + } + signr = dequeue_signal(current, ¤t->blocked, info); if (!signr) -- cgit v1.2.3 From 3544d72a0e10d0aa1c1bd59ed77a53a59cdc12f7 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 Jun 2011 11:20:15 +0200 Subject: ptrace: implement PTRACE_SEIZE PTRACE_ATTACH implicitly issues SIGSTOP on attach which has side effects on tracee signal and job control states. This patch implements a new ptrace request PTRACE_SEIZE which attaches a tracee without trapping it or affecting its signal and job control states. The usage is the same with PTRACE_ATTACH but it takes PTRACE_SEIZE_* flags in @data. Currently, the only defined flag is PTRACE_SEIZE_DEVEL which is a temporary flag to enable PTRACE_SEIZE. PTRACE_SEIZE will change ptrace behaviors outside of attach itself. The changes will be implemented gradually and the DEVEL flag is to prevent programs which expect full SEIZE behavior from using it before all the behavior modifications are complete while allowing unit testing. The flag will be removed once SEIZE behaviors are completely implemented. * PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap. After attaching tracee continues to run unless a trap condition occurs. * PTRACE_SEIZE doesn't affect signal or group stop state. * If PTRACE_SEIZE'd, group stop uses PTRACE_EVENT_STOP trap which uses exit_code of (signr | PTRACE_EVENT_STOP << 8) where signr is one of the stopping signals if group stop is in effect or SIGTRAP otherwise, and returns usual trap siginfo on PTRACE_GETSIGINFO instead of NULL. Seizing sets PT_SEIZED in ->ptrace of the tracee. This flag will be used to determine whether new SEIZE behaviors should be enabled. Test program follows. #define PTRACE_SEIZE 0x4206 #define PTRACE_SEIZE_DEVEL 0x80000000 static const struct timespec ts100ms = { .tv_nsec = 100000000 }; static const struct timespec ts1s = { .tv_sec = 1 }; static const struct timespec ts3s = { .tv_sec = 3 }; int main(int argc, char **argv) { pid_t tracee; tracee = fork(); if (tracee == 0) { nanosleep(&ts100ms, NULL); while (1) { printf("tracee: alive\n"); nanosleep(&ts1s, NULL); } } if (argc > 1) kill(tracee, SIGSTOP); nanosleep(&ts100ms, NULL); ptrace(PTRACE_SEIZE, tracee, NULL, (void *)(unsigned long)PTRACE_SEIZE_DEVEL); if (argc > 1) { waitid(P_PID, tracee, NULL, WSTOPPED); ptrace(PTRACE_CONT, tracee, NULL, NULL); } nanosleep(&ts3s, NULL); printf("tracer: exiting\n"); return 0; } When the above program is called w/o argument, tracee is seized while running and remains running. When tracer exits, tracee continues to run and print out messages. # ./test-seize-simple tracee: alive tracee: alive tracee: alive tracer: exiting tracee: alive tracee: alive When called with an argument, tracee is seized from stopped state and continued, and returns to stopped state when tracer exits. # ./test-seize tracee: alive tracee: alive tracee: alive tracer: exiting # ps -el|grep test-seize 1 T 0 4720 1 0 80 0 - 941 signal ttyS0 00:00:00 test-seize -v2: SEIZE doesn't schedule TRAP_STOP and leaves tracee running as Jan suggested. -v3: PTRACE_EVENT_STOP traps now report group stop state by signr. If group stop is in effect the stop signal number is returned as part of exit_code; otherwise, SIGTRAP. This was suggested by Denys and Oleg. Signed-off-by: Tejun Heo Cc: Jan Kratochvil Cc: Denys Vlasenko Cc: Oleg Nesterov --- include/linux/ptrace.h | 7 +++++++ kernel/ptrace.c | 35 +++++++++++++++++++++++++++++------ kernel/signal.c | 39 ++++++++++++++++++++++++++++++--------- 3 files changed, 66 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index e93ef1a54fc7..67ad3f152329 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -47,6 +47,11 @@ #define PTRACE_GETREGSET 0x4204 #define PTRACE_SETREGSET 0x4205 +#define PTRACE_SEIZE 0x4206 + +/* flags in @data for PTRACE_SEIZE */ +#define PTRACE_SEIZE_DEVEL 0x80000000 /* temp flag for development */ + /* options set using PTRACE_SETOPTIONS */ #define PTRACE_O_TRACESYSGOOD 0x00000001 #define PTRACE_O_TRACEFORK 0x00000002 @@ -65,6 +70,7 @@ #define PTRACE_EVENT_EXEC 4 #define PTRACE_EVENT_VFORK_DONE 5 #define PTRACE_EVENT_EXIT 6 +#define PTRACE_EVENT_STOP 7 #include @@ -77,6 +83,7 @@ * flags. When the a task is stopped the ptracer owns task->ptrace. */ +#define PT_SEIZED 0x00010000 /* SEIZE used, enable new behavior */ #define PT_PTRACED 0x00000001 #define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */ #define PT_TRACESYSGOOD 0x00000004 diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 45a8a4c5d8b2..dcf9f974198c 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -209,10 +209,28 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode) return !err; } -static int ptrace_attach(struct task_struct *task) +static int ptrace_attach(struct task_struct *task, long request, + unsigned long flags) { + bool seize = (request == PTRACE_SEIZE); int retval; + /* + * SEIZE will enable new ptrace behaviors which will be implemented + * gradually. SEIZE_DEVEL is used to prevent applications + * expecting full SEIZE behaviors trapping on kernel commits which + * are still in the process of implementing them. + * + * Only test programs for new ptrace behaviors being implemented + * should set SEIZE_DEVEL. If unset, SEIZE will fail with -EIO. + * + * Once SEIZE behaviors are completely implemented, this flag and + * the following test will be removed. + */ + retval = -EIO; + if (seize && !(flags & PTRACE_SEIZE_DEVEL)) + goto out; + audit_ptrace(task); retval = -EPERM; @@ -244,11 +262,16 @@ static int ptrace_attach(struct task_struct *task) goto unlock_tasklist; task->ptrace = PT_PTRACED; + if (seize) + task->ptrace |= PT_SEIZED; if (task_ns_capable(task, CAP_SYS_PTRACE)) task->ptrace |= PT_PTRACE_CAP; __ptrace_link(task, current); - send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); + + /* SEIZE doesn't trap tracee on attach */ + if (!seize) + send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); spin_lock(&task->sighand->siglock); @@ -785,8 +808,8 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, goto out; } - if (request == PTRACE_ATTACH) { - ret = ptrace_attach(child); + if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) { + ret = ptrace_attach(child, request, data); /* * Some architectures need to do book-keeping after * a ptrace attach. @@ -927,8 +950,8 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, goto out; } - if (request == PTRACE_ATTACH) { - ret = ptrace_attach(child); + if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) { + ret = ptrace_attach(child, request, data); /* * Some architectures need to do book-keeping after * a ptrace attach. diff --git a/kernel/signal.c b/kernel/signal.c index b5f55ca1f43f..589292f38530 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1873,21 +1873,26 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info) recalc_sigpending_tsk(current); } -void ptrace_notify(int exit_code) +static void ptrace_do_notify(int signr, int exit_code, int why) { siginfo_t info; - BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP); - memset(&info, 0, sizeof info); - info.si_signo = SIGTRAP; + info.si_signo = signr; info.si_code = exit_code; info.si_pid = task_pid_vnr(current); info.si_uid = current_uid(); /* Let the debugger run. */ + ptrace_stop(exit_code, why, 1, &info); +} + +void ptrace_notify(int exit_code) +{ + BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP); + spin_lock_irq(¤t->sighand->siglock); - ptrace_stop(exit_code, CLD_TRAPPED, 1, &info); + ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED); spin_unlock_irq(¤t->sighand->siglock); } @@ -2017,7 +2022,13 @@ static bool do_signal_stop(int signr) /** * do_jobctl_trap - take care of ptrace jobctl traps * - * It is currently used only to trap for group stop while ptraced. + * When PT_SEIZED, it's used for both group stop and explicit + * SEIZE/INTERRUPT traps. Both generate PTRACE_EVENT_STOP trap with + * accompanying siginfo. If stopped, lower eight bits of exit_code contain + * the stop signal; otherwise, %SIGTRAP. + * + * When !PT_SEIZED, it's used only for group stop trap with stop signal + * number as exit_code and no siginfo. * * CONTEXT: * Must be called with @current->sighand->siglock held, which may be @@ -2025,11 +2036,21 @@ static bool do_signal_stop(int signr) */ static void do_jobctl_trap(void) { + struct signal_struct *signal = current->signal; int signr = current->jobctl & JOBCTL_STOP_SIGMASK; - WARN_ON_ONCE(!signr); - ptrace_stop(signr, CLD_STOPPED, 0, NULL); - current->exit_code = 0; + if (current->ptrace & PT_SEIZED) { + if (!signal->group_stop_count && + !(signal->flags & SIGNAL_STOP_STOPPED)) + signr = SIGTRAP; + WARN_ON_ONCE(!signr); + ptrace_do_notify(signr, signr | (PTRACE_EVENT_STOP << 8), + CLD_STOPPED); + } else { + WARN_ON_ONCE(!signr); + ptrace_stop(signr, CLD_STOPPED, 0, NULL); + current->exit_code = 0; + } } static int ptrace_signal(int signr, siginfo_t *info, -- cgit v1.2.3 From fca26f260c528ee51a2e451b5b200aeb528f3e09 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 Jun 2011 11:20:16 +0200 Subject: ptrace: implement PTRACE_INTERRUPT Currently, there's no way to trap a running ptracee short of sending a signal which has various side effects. This patch implements PTRACE_INTERRUPT which traps ptracee without any signal or job control related side effect. The implementation is almost trivial. It uses the group stop trap - SIGTRAP | PTRACE_EVENT_STOP << 8. A new trap flag JOBCTL_TRAP_INTERRUPT is added, which is set on PTRACE_INTERRUPT and cleared when any trap happens. As INTERRUPT should be useable regardless of the current state of tracee, task_is_traced() test in ptrace_check_attach() is skipped for INTERRUPT. PTRACE_INTERRUPT is available iff tracee is attached with PTRACE_SEIZE. Test program follows. #define PTRACE_SEIZE 0x4206 #define PTRACE_INTERRUPT 0x4207 #define PTRACE_SEIZE_DEVEL 0x80000000 static const struct timespec ts100ms = { .tv_nsec = 100000000 }; static const struct timespec ts1s = { .tv_sec = 1 }; static const struct timespec ts3s = { .tv_sec = 3 }; int main(int argc, char **argv) { pid_t tracee; tracee = fork(); if (tracee == 0) { nanosleep(&ts100ms, NULL); while (1) { printf("tracee: alive pid=%d\n", getpid()); nanosleep(&ts1s, NULL); } } if (argc > 1) kill(tracee, SIGSTOP); nanosleep(&ts100ms, NULL); ptrace(PTRACE_SEIZE, tracee, NULL, (void *)(unsigned long)PTRACE_SEIZE_DEVEL); if (argc > 1) { waitid(P_PID, tracee, NULL, WSTOPPED); ptrace(PTRACE_CONT, tracee, NULL, NULL); } nanosleep(&ts3s, NULL); printf("tracer: INTERRUPT and DETACH\n"); ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL); waitid(P_PID, tracee, NULL, WSTOPPED); ptrace(PTRACE_DETACH, tracee, NULL, NULL); nanosleep(&ts3s, NULL); printf("tracer: exiting\n"); kill(tracee, SIGKILL); return 0; } When called without argument, tracee is seized from running state, interrupted and then detached back to running state. # ./test-interrupt tracee: alive pid=4546 tracee: alive pid=4546 tracee: alive pid=4546 tracer: INTERRUPT and DETACH tracee: alive pid=4546 tracee: alive pid=4546 tracee: alive pid=4546 tracer: exiting When called with argument, tracee is seized from stopped state, continued, interrupted and then detached back to stopped state. # ./test-interrupt 1 tracee: alive pid=4548 tracee: alive pid=4548 tracee: alive pid=4548 tracer: INTERRUPT and DETACH tracer: exiting Before PTRACE_INTERRUPT, once the tracee was running, there was no way to trap tracee and do PTRACE_DETACH without causing side effect. -v2: Updated to use task_set_jobctl_pending() so that it doesn't end up scheduling TRAP_STOP if child is dying which may make the child unkillable. Spotted by Oleg. Signed-off-by: Tejun Heo Cc: Oleg Nesterov --- include/linux/ptrace.h | 1 + kernel/ptrace.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 67ad3f152329..ad754d1e0b13 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -48,6 +48,7 @@ #define PTRACE_SETREGSET 0x4205 #define PTRACE_SEIZE 0x4206 +#define PTRACE_INTERRUPT 0x4207 /* flags in @data for PTRACE_SEIZE */ #define PTRACE_SEIZE_DEVEL 0x80000000 /* temp flag for development */ diff --git a/kernel/ptrace.c b/kernel/ptrace.c index dcf9f974198c..6852c0f4a916 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -658,10 +658,12 @@ static int ptrace_regset(struct task_struct *task, int req, unsigned int type, int ptrace_request(struct task_struct *child, long request, unsigned long addr, unsigned long data) { + bool seized = child->ptrace & PT_SEIZED; int ret = -EIO; siginfo_t siginfo; void __user *datavp = (void __user *) data; unsigned long __user *datalp = datavp; + unsigned long flags; switch (request) { case PTRACE_PEEKTEXT: @@ -694,6 +696,27 @@ int ptrace_request(struct task_struct *child, long request, ret = ptrace_setsiginfo(child, &siginfo); break; + case PTRACE_INTERRUPT: + /* + * Stop tracee without any side-effect on signal or job + * control. At least one trap is guaranteed to happen + * after this request. If @child is already trapped, the + * current trap is not disturbed and another trap will + * happen after the current trap is ended with PTRACE_CONT. + * + * The actual trap might not be PTRACE_EVENT_STOP trap but + * the pending condition is cleared regardless. + */ + if (unlikely(!seized || !lock_task_sighand(child, &flags))) + break; + + if (likely(task_set_jobctl_pending(child, JOBCTL_TRAP_STOP))) + signal_wake_up(child, 0); + + unlock_task_sighand(child, &flags); + ret = 0; + break; + case PTRACE_DETACH: /* detach a process that was attached. */ ret = ptrace_detach(child, data); break; @@ -819,7 +842,8 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, goto out_put_task_struct; } - ret = ptrace_check_attach(child, request == PTRACE_KILL); + ret = ptrace_check_attach(child, request == PTRACE_KILL || + request == PTRACE_INTERRUPT); if (ret < 0) goto out_put_task_struct; @@ -961,7 +985,8 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, goto out_put_task_struct; } - ret = ptrace_check_attach(child, request == PTRACE_KILL); + ret = ptrace_check_attach(child, request == PTRACE_KILL || + request == PTRACE_INTERRUPT); if (!ret) ret = compat_arch_ptrace(child, request, addr, data); -- cgit v1.2.3 From fb1d910c178ba0c5bc32d3e5a9e82e05b7aad3cd Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 Jun 2011 11:20:17 +0200 Subject: ptrace: implement TRAP_NOTIFY and use it for group stop events Currently there's no way for ptracer to find out whether group stop finished other than polling with INTERRUPT - GETSIGINFO - CONT sequence. This patch implements group stop notification for ptracer using STOP traps. When group stop state of a seized tracee changes, JOBCTL_TRAP_NOTIFY is set, which schedules a STOP trap which is sticky - it isn't cleared by other traps and at least one STOP trap will happen eventually. STOP trap is synchronization point for event notification and the tracer can determine the current group stop state by looking at the signal number portion of exit code (si_status from waitid(2) or si_code from PTRACE_GETSIGINFO). Notifications are generated both on start and end of group stops but, because group stop participation always happens before STOP trap, this doesn't cause an extra trap while tracee is participating in group stop. The symmetry will be useful later. Note that this notification works iff tracee is not trapped. Currently there is no way to be notified of group stop state changes while tracee is trapped. This will be addressed by a later patch. An example program follows. #define PTRACE_SEIZE 0x4206 #define PTRACE_INTERRUPT 0x4207 #define PTRACE_SEIZE_DEVEL 0x80000000 static const struct timespec ts1s = { .tv_sec = 1 }; int main(int argc, char **argv) { pid_t tracee, tracer; int i; tracee = fork(); if (!tracee) while (1) pause(); tracer = fork(); if (!tracer) { siginfo_t si; ptrace(PTRACE_SEIZE, tracee, NULL, (void *)(unsigned long)PTRACE_SEIZE_DEVEL); ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL); repeat: waitid(P_PID, tracee, NULL, WSTOPPED); ptrace(PTRACE_GETSIGINFO, tracee, NULL, &si); if (!si.si_code) { printf("tracer: SIG %d\n", si.si_signo); ptrace(PTRACE_CONT, tracee, NULL, (void *)(unsigned long)si.si_signo); goto repeat; } printf("tracer: stopped=%d signo=%d\n", si.si_signo != SIGTRAP, si.si_signo); ptrace(PTRACE_CONT, tracee, NULL, NULL); goto repeat; } for (i = 0; i < 3; i++) { nanosleep(&ts1s, NULL); printf("mother: SIGSTOP\n"); kill(tracee, SIGSTOP); nanosleep(&ts1s, NULL); printf("mother: SIGCONT\n"); kill(tracee, SIGCONT); } nanosleep(&ts1s, NULL); kill(tracer, SIGKILL); kill(tracee, SIGKILL); return 0; } In the above program, tracer keeps tracee running and gets notification of each group stop state changes. # ./test-notify tracer: stopped=0 signo=5 mother: SIGSTOP tracer: SIG 19 tracer: stopped=1 signo=19 mother: SIGCONT tracer: stopped=0 signo=5 tracer: SIG 18 mother: SIGSTOP tracer: SIG 19 tracer: stopped=1 signo=19 mother: SIGCONT tracer: stopped=0 signo=5 tracer: SIG 18 mother: SIGSTOP tracer: SIG 19 tracer: stopped=1 signo=19 mother: SIGCONT tracer: stopped=0 signo=5 tracer: SIG 18 Signed-off-by: Tejun Heo Cc: Oleg Nesterov --- include/linux/sched.h | 4 +++- kernel/signal.c | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 8bd84b83a35b..1854def284f5 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1811,15 +1811,17 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t * #define JOBCTL_STOP_PENDING_BIT 17 /* task should stop for group stop */ #define JOBCTL_STOP_CONSUME_BIT 18 /* consume group stop count */ #define JOBCTL_TRAP_STOP_BIT 19 /* trap for STOP */ +#define JOBCTL_TRAP_NOTIFY_BIT 20 /* trap for NOTIFY */ #define JOBCTL_TRAPPING_BIT 21 /* switching to TRACED */ #define JOBCTL_STOP_DEQUEUED (1 << JOBCTL_STOP_DEQUEUED_BIT) #define JOBCTL_STOP_PENDING (1 << JOBCTL_STOP_PENDING_BIT) #define JOBCTL_STOP_CONSUME (1 << JOBCTL_STOP_CONSUME_BIT) #define JOBCTL_TRAP_STOP (1 << JOBCTL_TRAP_STOP_BIT) +#define JOBCTL_TRAP_NOTIFY (1 << JOBCTL_TRAP_NOTIFY_BIT) #define JOBCTL_TRAPPING (1 << JOBCTL_TRAPPING_BIT) -#define JOBCTL_TRAP_MASK JOBCTL_TRAP_STOP +#define JOBCTL_TRAP_MASK (JOBCTL_TRAP_STOP | JOBCTL_TRAP_NOTIFY) #define JOBCTL_PENDING_MASK (JOBCTL_STOP_PENDING | JOBCTL_TRAP_MASK) extern bool task_set_jobctl_pending(struct task_struct *task, diff --git a/kernel/signal.c b/kernel/signal.c index 589292f38530..06177e2b3917 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -817,6 +817,30 @@ static int check_kill_permission(int sig, struct siginfo *info, return security_task_kill(t, info, sig, 0); } +/** + * ptrace_trap_notify - schedule trap to notify ptracer + * @t: tracee wanting to notify tracer + * + * This function schedules sticky ptrace trap which is cleared on the next + * TRAP_STOP to notify ptracer of an event. @t must have been seized by + * ptracer. + * + * If @t is running, STOP trap will be taken. If already trapped, STOP + * trap will be eventually taken without returning to userland after the + * existing traps are finished by PTRACE_CONT. + * + * CONTEXT: + * Must be called with @task->sighand->siglock held. + */ +static void ptrace_trap_notify(struct task_struct *t) +{ + WARN_ON_ONCE(!(t->ptrace & PT_SEIZED)); + assert_spin_locked(&t->sighand->siglock); + + task_set_jobctl_pending(t, JOBCTL_TRAP_NOTIFY); + signal_wake_up(t, 0); +} + /* * Handle magic process-wide effects of stop/continue signals. Unlike * the signal actions, these happen immediately at signal-generation @@ -855,7 +879,10 @@ static int prepare_signal(int sig, struct task_struct *p, int from_ancestor_ns) do { task_clear_jobctl_pending(t, JOBCTL_STOP_PENDING); rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending); - wake_up_state(t, __TASK_STOPPED); + if (likely(!(t->ptrace & PT_SEIZED))) + wake_up_state(t, __TASK_STOPPED); + else + ptrace_trap_notify(t); } while_each_thread(p, t); /* @@ -1797,8 +1824,10 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info) if (why == CLD_STOPPED && (current->jobctl & JOBCTL_STOP_PENDING)) gstop_done = task_participate_group_stop(current); - /* any trap clears pending STOP trap */ + /* any trap clears pending STOP trap, STOP trap clears NOTIFY */ task_clear_jobctl_pending(current, JOBCTL_TRAP_STOP); + if (info && info->si_code >> 8 == PTRACE_EVENT_STOP) + task_clear_jobctl_pending(current, JOBCTL_TRAP_NOTIFY); /* entering a trap, clear TRAPPING */ task_clear_jobctl_trapping(current); @@ -1972,7 +2001,10 @@ static bool do_signal_stop(int signr) if (!task_is_stopped(t) && task_set_jobctl_pending(t, signr | gstop)) { sig->group_stop_count++; - signal_wake_up(t, 0); + if (likely(!(t->ptrace & PT_SEIZED))) + signal_wake_up(t, 0); + else + ptrace_trap_notify(t); } } } -- cgit v1.2.3 From 544b2c91a9f14f9565af1972203438b7f49afd48 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 Jun 2011 11:20:18 +0200 Subject: ptrace: implement PTRACE_LISTEN The previous patch implemented async notification for ptrace but it only worked while trace is running. This patch introduces PTRACE_LISTEN which is suggested by Oleg Nestrov. It's allowed iff tracee is in STOP trap and puts tracee into quasi-running state - tracee never really runs but wait(2) and ptrace(2) consider it to be running. While ptracer is listening, tracee is allowed to re-enter STOP to notify an async event. Listening state is cleared on the first notification. Ptracer can also clear it by issuing INTERRUPT - tracee will re-trap into STOP with listening state cleared. This allows ptracer to monitor group stop state without running tracee - use INTERRUPT to put tracee into STOP trap, issue LISTEN and then wait(2) to wait for the next group stop event. When it happens, PTRACE_GETSIGINFO provides information to determine the current state. Test program follows. #define PTRACE_SEIZE 0x4206 #define PTRACE_INTERRUPT 0x4207 #define PTRACE_LISTEN 0x4208 #define PTRACE_SEIZE_DEVEL 0x80000000 static const struct timespec ts1s = { .tv_sec = 1 }; int main(int argc, char **argv) { pid_t tracee, tracer; int i; tracee = fork(); if (!tracee) while (1) pause(); tracer = fork(); if (!tracer) { siginfo_t si; ptrace(PTRACE_SEIZE, tracee, NULL, (void *)(unsigned long)PTRACE_SEIZE_DEVEL); ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL); repeat: waitid(P_PID, tracee, NULL, WSTOPPED); ptrace(PTRACE_GETSIGINFO, tracee, NULL, &si); if (!si.si_code) { printf("tracer: SIG %d\n", si.si_signo); ptrace(PTRACE_CONT, tracee, NULL, (void *)(unsigned long)si.si_signo); goto repeat; } printf("tracer: stopped=%d signo=%d\n", si.si_signo != SIGTRAP, si.si_signo); if (si.si_signo != SIGTRAP) ptrace(PTRACE_LISTEN, tracee, NULL, NULL); else ptrace(PTRACE_CONT, tracee, NULL, NULL); goto repeat; } for (i = 0; i < 3; i++) { nanosleep(&ts1s, NULL); printf("mother: SIGSTOP\n"); kill(tracee, SIGSTOP); nanosleep(&ts1s, NULL); printf("mother: SIGCONT\n"); kill(tracee, SIGCONT); } nanosleep(&ts1s, NULL); kill(tracer, SIGKILL); kill(tracee, SIGKILL); return 0; } This is identical to the program to test TRAP_NOTIFY except that tracee is PTRACE_LISTEN'd instead of PTRACE_CONT'd when group stopped. This allows ptracer to monitor when group stop ends without running tracee. # ./test-listen tracer: stopped=0 signo=5 mother: SIGSTOP tracer: SIG 19 tracer: stopped=1 signo=19 mother: SIGCONT tracer: stopped=0 signo=5 tracer: SIG 18 mother: SIGSTOP tracer: SIG 19 tracer: stopped=1 signo=19 mother: SIGCONT tracer: stopped=0 signo=5 tracer: SIG 18 mother: SIGSTOP tracer: SIG 19 tracer: stopped=1 signo=19 mother: SIGCONT tracer: stopped=0 signo=5 tracer: SIG 18 -v2: Moved JOBCTL_LISTENING check in wait_task_stopped() into task_stopped_code() as suggested by Oleg. Signed-off-by: Tejun Heo Cc: Oleg Nesterov --- include/linux/ptrace.h | 1 + include/linux/sched.h | 2 ++ kernel/exit.c | 3 ++- kernel/ptrace.c | 42 +++++++++++++++++++++++++++++++++++++++--- kernel/signal.c | 13 +++++++++---- 5 files changed, 53 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index ad754d1e0b13..4f224f169524 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -49,6 +49,7 @@ #define PTRACE_SEIZE 0x4206 #define PTRACE_INTERRUPT 0x4207 +#define PTRACE_LISTEN 0x4208 /* flags in @data for PTRACE_SEIZE */ #define PTRACE_SEIZE_DEVEL 0x80000000 /* temp flag for development */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 1854def284f5..87f7ca7ed6f6 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1813,6 +1813,7 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t * #define JOBCTL_TRAP_STOP_BIT 19 /* trap for STOP */ #define JOBCTL_TRAP_NOTIFY_BIT 20 /* trap for NOTIFY */ #define JOBCTL_TRAPPING_BIT 21 /* switching to TRACED */ +#define JOBCTL_LISTENING_BIT 22 /* ptracer is listening for events */ #define JOBCTL_STOP_DEQUEUED (1 << JOBCTL_STOP_DEQUEUED_BIT) #define JOBCTL_STOP_PENDING (1 << JOBCTL_STOP_PENDING_BIT) @@ -1820,6 +1821,7 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t * #define JOBCTL_TRAP_STOP (1 << JOBCTL_TRAP_STOP_BIT) #define JOBCTL_TRAP_NOTIFY (1 << JOBCTL_TRAP_NOTIFY_BIT) #define JOBCTL_TRAPPING (1 << JOBCTL_TRAPPING_BIT) +#define JOBCTL_LISTENING (1 << JOBCTL_LISTENING_BIT) #define JOBCTL_TRAP_MASK (JOBCTL_TRAP_STOP | JOBCTL_TRAP_NOTIFY) #define JOBCTL_PENDING_MASK (JOBCTL_STOP_PENDING | JOBCTL_TRAP_MASK) diff --git a/kernel/exit.c b/kernel/exit.c index 20a406471525..289f59d686bf 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -1368,7 +1368,8 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p) static int *task_stopped_code(struct task_struct *p, bool ptrace) { if (ptrace) { - if (task_is_stopped_or_traced(p)) + if (task_is_stopped_or_traced(p) && + !(p->jobctl & JOBCTL_LISTENING)) return &p->exit_code; } else { if (p->signal->flags & SIGNAL_STOP_STOPPED) diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 6852c0f4a916..e18966c1c0da 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -146,7 +146,8 @@ int ptrace_check_attach(struct task_struct *child, bool ignore_state) */ spin_lock_irq(&child->sighand->siglock); WARN_ON_ONCE(task_is_stopped(child)); - if (task_is_traced(child) || ignore_state) + if (ignore_state || (task_is_traced(child) && + !(child->jobctl & JOBCTL_LISTENING))) ret = 0; spin_unlock_irq(&child->sighand->siglock); } @@ -660,7 +661,7 @@ int ptrace_request(struct task_struct *child, long request, { bool seized = child->ptrace & PT_SEIZED; int ret = -EIO; - siginfo_t siginfo; + siginfo_t siginfo, *si; void __user *datavp = (void __user *) data; unsigned long __user *datalp = datavp; unsigned long flags; @@ -710,8 +711,43 @@ int ptrace_request(struct task_struct *child, long request, if (unlikely(!seized || !lock_task_sighand(child, &flags))) break; + /* + * INTERRUPT doesn't disturb existing trap sans one + * exception. If ptracer issued LISTEN for the current + * STOP, this INTERRUPT should clear LISTEN and re-trap + * tracee into STOP. + */ if (likely(task_set_jobctl_pending(child, JOBCTL_TRAP_STOP))) - signal_wake_up(child, 0); + signal_wake_up(child, child->jobctl & JOBCTL_LISTENING); + + unlock_task_sighand(child, &flags); + ret = 0; + break; + + case PTRACE_LISTEN: + /* + * Listen for events. Tracee must be in STOP. It's not + * resumed per-se but is not considered to be in TRACED by + * wait(2) or ptrace(2). If an async event (e.g. group + * stop state change) happens, tracee will enter STOP trap + * again. Alternatively, ptracer can issue INTERRUPT to + * finish listening and re-trap tracee into STOP. + */ + if (unlikely(!seized || !lock_task_sighand(child, &flags))) + break; + + si = child->last_siginfo; + if (unlikely(!si || si->si_code >> 8 != PTRACE_EVENT_STOP)) + break; + + child->jobctl |= JOBCTL_LISTENING; + + /* + * If NOTIFY is set, it means event happened between start + * of this trap and now. Trigger re-trap immediately. + */ + if (child->jobctl & JOBCTL_TRAP_NOTIFY) + signal_wake_up(child, true); unlock_task_sighand(child, &flags); ret = 0; diff --git a/kernel/signal.c b/kernel/signal.c index 06177e2b3917..97e575a3387e 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -825,9 +825,11 @@ static int check_kill_permission(int sig, struct siginfo *info, * TRAP_STOP to notify ptracer of an event. @t must have been seized by * ptracer. * - * If @t is running, STOP trap will be taken. If already trapped, STOP - * trap will be eventually taken without returning to userland after the - * existing traps are finished by PTRACE_CONT. + * If @t is running, STOP trap will be taken. If trapped for STOP and + * ptracer is listening for events, tracee is woken up so that it can + * re-trap for the new event. If trapped otherwise, STOP trap will be + * eventually taken without returning to userland after the existing traps + * are finished by PTRACE_CONT. * * CONTEXT: * Must be called with @task->sighand->siglock held. @@ -838,7 +840,7 @@ static void ptrace_trap_notify(struct task_struct *t) assert_spin_locked(&t->sighand->siglock); task_set_jobctl_pending(t, JOBCTL_TRAP_NOTIFY); - signal_wake_up(t, 0); + signal_wake_up(t, t->jobctl & JOBCTL_LISTENING); } /* @@ -1894,6 +1896,9 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info) spin_lock_irq(¤t->sighand->siglock); current->last_siginfo = NULL; + /* LISTENING can be set only during STOP traps, clear it */ + current->jobctl &= ~JOBCTL_LISTENING; + /* * Queued signals ignored us while we were stopped for tracing. * So check for any that we should take before resuming user mode. -- cgit v1.2.3 From 7fbec224cfb44074ab88720c878aa3bdb3158377 Mon Sep 17 00:00:00 2001 From: Antti Julku Date: Wed, 15 Jun 2011 12:01:15 +0300 Subject: Bluetooth: Add blacklisting support for mgmt interface Management interface commands for blocking and unblocking devices. Signed-off-by: Antti Julku Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/mgmt.h | 10 +++++++ net/bluetooth/mgmt.c | 70 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 4899286ed4e4..45bea25d737f 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -199,6 +199,16 @@ struct mgmt_cp_remove_remote_oob_data { #define MGMT_OP_STOP_DISCOVERY 0x001C +#define MGMT_OP_BLOCK_DEVICE 0x001D +struct mgmt_cp_block_device { + bdaddr_t bdaddr; +} __packed; + +#define MGMT_OP_UNBLOCK_DEVICE 0x001E +struct mgmt_cp_unblock_device { + bdaddr_t bdaddr; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d19208903be4..64c0418a6221 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1666,6 +1666,70 @@ failed: return err; } +static int block_device(struct sock *sk, u16 index, unsigned char *data, + u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_block_device *cp; + int err; + + BT_DBG("hci%u", index); + + cp = (void *) data; + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, + EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, + ENODEV); + + err = hci_blacklist_add(hdev, &cp->bdaddr); + + if (err < 0) + err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err); + else + err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, + NULL, 0); + hci_dev_put(hdev); + + return err; +} + +static int unblock_device(struct sock *sk, u16 index, unsigned char *data, + u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_unblock_device *cp; + int err; + + BT_DBG("hci%u", index); + + cp = (void *) data; + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, + EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, + ENODEV); + + err = hci_blacklist_del(hdev, &cp->bdaddr); + + if (err < 0) + err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, -err); + else + err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, + NULL, 0); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -1780,6 +1844,12 @@ 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_BLOCK_DEVICE: + err = block_device(sk, index, buf + sizeof(*hdr), len); + break; + case MGMT_OP_UNBLOCK_DEVICE: + err = unblock_device(sk, index, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, index, opcode, 0x01); -- cgit v1.2.3 From c1360a1cf35117d6f3898cb5183ce4349d06714c Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 10 Jun 2011 17:02:12 -0300 Subject: Bluetooth: use bit operation on conf_state Instead of making the bit operations manually, we now use set_bit, test_bit, etc. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 20 ++++++----- net/bluetooth/l2cap_core.c | 77 ++++++++++++++++++++----------------------- net/bluetooth/l2cap_sock.c | 4 +-- 3 files changed, 48 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index bf1c7f681932..4ab1b27996bc 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -324,7 +324,7 @@ struct l2cap_chan { __u16 monitor_timeout; __u16 mps; - __u8 conf_state; + unsigned long conf_state; __u16 conn_state; __u8 next_tx_seq; @@ -424,14 +424,16 @@ struct l2cap_pinfo { struct l2cap_chan *chan; }; -#define L2CAP_CONF_REQ_SENT 0x01 -#define L2CAP_CONF_INPUT_DONE 0x02 -#define L2CAP_CONF_OUTPUT_DONE 0x04 -#define L2CAP_CONF_MTU_DONE 0x08 -#define L2CAP_CONF_MODE_DONE 0x10 -#define L2CAP_CONF_CONNECT_PEND 0x20 -#define L2CAP_CONF_NO_FCS_RECV 0x40 -#define L2CAP_CONF_STATE2_DEVICE 0x80 +enum { + CONF_REQ_SENT, + CONF_INPUT_DONE, + CONF_OUTPUT_DONE, + CONF_MTU_DONE, + CONF_MODE_DONE, + CONF_CONNECT_PEND, + CONF_NO_FCS_RECV, + CONF_STATE2_DEVICE, +}; #define L2CAP_CONF_MAX_CONF_REQ 2 #define L2CAP_CONF_MAX_CONF_RSP 2 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bbbae2e0aa84..b4e927eb174e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -381,8 +381,8 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) } else sk->sk_state_change(sk); - if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE && - chan->conf_state & L2CAP_CONF_INPUT_DONE)) + if (!(test_bit(CONF_OUTPUT_DONE, &chan->conf_state) && + test_bit(CONF_INPUT_DONE, &chan->conf_state))) return; skb_queue_purge(&chan->tx_q); @@ -633,7 +633,7 @@ static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control) static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan) { - return !(chan->conf_state & L2CAP_CONF_CONNECT_PEND); + return !test_bit(CONF_CONNECT_PEND, &chan->conf_state); } static void l2cap_do_start(struct l2cap_chan *chan) @@ -651,7 +651,7 @@ static void l2cap_do_start(struct l2cap_chan *chan) req.psm = chan->psm; chan->ident = l2cap_get_ident(conn); - chan->conf_state |= L2CAP_CONF_CONNECT_PEND; + set_bit(CONF_CONNECT_PEND, &chan->conf_state); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); @@ -740,10 +740,9 @@ static void l2cap_conn_start(struct l2cap_conn *conn) continue; } - if (!l2cap_mode_supported(chan->mode, - conn->feat_mask) - && chan->conf_state & - L2CAP_CONF_STATE2_DEVICE) { + if (!l2cap_mode_supported(chan->mode, conn->feat_mask) + && test_bit(CONF_STATE2_DEVICE, + &chan->conf_state)) { /* l2cap_chan_close() calls list_del(chan) * so release the lock */ read_unlock_bh(&conn->chan_lock); @@ -757,7 +756,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) req.psm = chan->psm; chan->ident = l2cap_get_ident(conn); - chan->conf_state |= L2CAP_CONF_CONNECT_PEND; + set_bit(CONF_CONNECT_PEND, &chan->conf_state); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); @@ -788,13 +787,13 @@ static void l2cap_conn_start(struct l2cap_conn *conn) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); - if (chan->conf_state & L2CAP_CONF_REQ_SENT || + if (test_bit(CONF_REQ_SENT, &chan->conf_state) || rsp.result != L2CAP_CR_SUCCESS) { bh_unlock_sock(sk); continue; } - chan->conf_state |= L2CAP_CONF_REQ_SENT; + set_bit(CONF_REQ_SENT, &chan->conf_state); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; @@ -1917,7 +1916,7 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) switch (chan->mode) { case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: - if (chan->conf_state & L2CAP_CONF_STATE2_DEVICE) + if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) break; /* fall through */ @@ -1964,7 +1963,7 @@ done: break; if (chan->fcs == L2CAP_FCS_NONE || - chan->conf_state & L2CAP_CONF_NO_FCS_RECV) { + test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } @@ -1987,7 +1986,7 @@ done: break; if (chan->fcs == L2CAP_FCS_NONE || - chan->conf_state & L2CAP_CONF_NO_FCS_RECV) { + test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } @@ -2039,7 +2038,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) case L2CAP_CONF_FCS: if (val == L2CAP_FCS_NONE) - chan->conf_state |= L2CAP_CONF_NO_FCS_RECV; + set_bit(CONF_NO_FCS_RECV, &chan->conf_state); break; @@ -2059,7 +2058,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) switch (chan->mode) { case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: - if (!(chan->conf_state & L2CAP_CONF_STATE2_DEVICE)) { + if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) { chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask); break; @@ -2092,14 +2091,14 @@ done: result = L2CAP_CONF_UNACCEPT; else { chan->omtu = mtu; - chan->conf_state |= L2CAP_CONF_MTU_DONE; + set_bit(CONF_MTU_DONE, &chan->conf_state); } l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu); switch (rfc.mode) { case L2CAP_MODE_BASIC: chan->fcs = L2CAP_FCS_NONE; - chan->conf_state |= L2CAP_CONF_MODE_DONE; + set_bit(CONF_MODE_DONE, &chan->conf_state); break; case L2CAP_MODE_ERTM: @@ -2116,7 +2115,7 @@ done: rfc.monitor_timeout = le16_to_cpu(L2CAP_DEFAULT_MONITOR_TO); - chan->conf_state |= L2CAP_CONF_MODE_DONE; + set_bit(CONF_MODE_DONE, &chan->conf_state); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -2129,7 +2128,7 @@ done: chan->remote_mps = le16_to_cpu(rfc.max_pdu_size); - chan->conf_state |= L2CAP_CONF_MODE_DONE; + set_bit(CONF_MODE_DONE, &chan->conf_state); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -2144,7 +2143,7 @@ done: } if (result == L2CAP_CONF_SUCCESS) - chan->conf_state |= L2CAP_CONF_OUTPUT_DONE; + set_bit(CONF_OUTPUT_DONE, &chan->conf_state); } rsp->scid = cpu_to_le16(chan->dcid); rsp->result = cpu_to_le16(result); @@ -2186,7 +2185,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi if (olen == sizeof(rfc)) memcpy(&rfc, (void *)val, olen); - if ((chan->conf_state & L2CAP_CONF_STATE2_DEVICE) && + if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) && rfc.mode != chan->mode) return -ECONNREFUSED; @@ -2248,10 +2247,9 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); - if (chan->conf_state & L2CAP_CONF_REQ_SENT) + if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) return; - chan->conf_state |= L2CAP_CONF_REQ_SENT; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; @@ -2433,10 +2431,10 @@ sendresp: L2CAP_INFO_REQ, sizeof(info), &info); } - if (chan && !(chan->conf_state & L2CAP_CONF_REQ_SENT) && + if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) && result == L2CAP_CR_SUCCESS) { u8 buf[128]; - chan->conf_state |= L2CAP_CONF_REQ_SENT; + set_bit(CONF_REQ_SENT, &chan->conf_state); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; @@ -2477,20 +2475,18 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd l2cap_state_change(chan, BT_CONFIG); chan->ident = 0; chan->dcid = dcid; - chan->conf_state &= ~L2CAP_CONF_CONNECT_PEND; + clear_bit(CONF_CONNECT_PEND, &chan->conf_state); - if (chan->conf_state & L2CAP_CONF_REQ_SENT) + if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) break; - chan->conf_state |= L2CAP_CONF_REQ_SENT; - l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, req), req); chan->num_conf_req++; break; case L2CAP_CR_PEND: - chan->conf_state |= L2CAP_CONF_CONNECT_PEND; + set_bit(CONF_CONNECT_PEND, &chan->conf_state); break; default: @@ -2512,14 +2508,12 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd static inline void set_default_fcs(struct l2cap_chan *chan) { - struct l2cap_pinfo *pi = l2cap_pi(chan->sk); - /* FCS is enabled only in ERTM or streaming mode, if one or both * sides request it. */ if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING) chan->fcs = L2CAP_FCS_NONE; - else if (!(pi->chan->conf_state & L2CAP_CONF_NO_FCS_RECV)) + else if (!test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) chan->fcs = L2CAP_FCS_CRC16; } @@ -2586,10 +2580,10 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr /* Reset config buffer. */ chan->conf_len = 0; - if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE)) + if (!test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) goto unlock; - if (chan->conf_state & L2CAP_CONF_INPUT_DONE) { + if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) { set_default_fcs(chan); l2cap_state_change(chan, BT_CONNECTED); @@ -2604,9 +2598,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr goto unlock; } - if (!(chan->conf_state & L2CAP_CONF_REQ_SENT)) { + if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) { u8 buf[64]; - chan->conf_state |= L2CAP_CONF_REQ_SENT; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; @@ -2679,9 +2672,9 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (flags & 0x01) goto done; - chan->conf_state |= L2CAP_CONF_INPUT_DONE; + set_bit(CONF_INPUT_DONE, &chan->conf_state); - if (chan->conf_state & L2CAP_CONF_OUTPUT_DONE) { + if (test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) { set_default_fcs(chan); l2cap_state_change(chan, BT_CONNECTED); @@ -4203,7 +4196,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) continue; } - if (chan->conf_state & L2CAP_CONF_CONNECT_PEND) { + if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) { bh_unlock_sock(sk); continue; } @@ -4222,7 +4215,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) req.psm = chan->psm; chan->ident = l2cap_get_ident(conn); - chan->conf_state |= L2CAP_CONF_CONNECT_PEND; + set_bit(CONF_CONNECT_PEND, &chan->conf_state); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 5c819e002fb1..39082d4e77ce 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -512,7 +512,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us chan->mode = opts.mode; switch (chan->mode) { case L2CAP_MODE_BASIC: - chan->conf_state &= ~L2CAP_CONF_STATE2_DEVICE; + clear_bit(CONF_STATE2_DEVICE, &chan->conf_state); break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -891,7 +891,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->omtu = 0; if (!disable_ertm && sk->sk_type == SOCK_STREAM) { chan->mode = L2CAP_MODE_ERTM; - chan->conf_state |= L2CAP_CONF_STATE2_DEVICE; + set_bit(CONF_STATE2_DEVICE, &chan->conf_state); } else { chan->mode = L2CAP_MODE_BASIC; } -- cgit v1.2.3 From e2ab43536c53ba112a0adfb4c0dba286544c41f6 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 10 Jun 2011 21:28:49 -0300 Subject: Bluetooth: Use bit operations on conn_state Instead of setting bits manually we use set_bit, test_bit, etc. Also remove L2CAP_ prefix from macros. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 26 +++---- net/bluetooth/l2cap_core.c | 161 +++++++++++++++++++----------------------- 2 files changed, 88 insertions(+), 99 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 4ab1b27996bc..9c18e555b6ed 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -325,7 +325,7 @@ struct l2cap_chan { __u16 mps; unsigned long conf_state; - __u16 conn_state; + unsigned long conn_state; __u8 next_tx_seq; __u8 expected_ack_seq; @@ -438,17 +438,19 @@ enum { #define L2CAP_CONF_MAX_CONF_REQ 2 #define L2CAP_CONF_MAX_CONF_RSP 2 -#define L2CAP_CONN_SAR_SDU 0x0001 -#define L2CAP_CONN_SREJ_SENT 0x0002 -#define L2CAP_CONN_WAIT_F 0x0004 -#define L2CAP_CONN_SREJ_ACT 0x0008 -#define L2CAP_CONN_SEND_PBIT 0x0010 -#define L2CAP_CONN_REMOTE_BUSY 0x0020 -#define L2CAP_CONN_LOCAL_BUSY 0x0040 -#define L2CAP_CONN_REJ_ACT 0x0080 -#define L2CAP_CONN_SEND_FBIT 0x0100 -#define L2CAP_CONN_RNR_SENT 0x0200 -#define L2CAP_CONN_SAR_RETRY 0x0400 +enum { + CONN_SAR_SDU, + CONN_SREJ_SENT, + CONN_WAIT_F, + CONN_SREJ_ACT, + CONN_SEND_PBIT, + CONN_REMOTE_BUSY, + CONN_LOCAL_BUSY, + CONN_REJ_ACT, + CONN_SEND_FBIT, + CONN_RNR_SENT, + CONN_SAR_RETRY, +}; #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) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b4e927eb174e..25f31f4c7d08 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -584,15 +584,11 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) count = min_t(unsigned int, conn->mtu, hlen); control |= L2CAP_CTRL_FRAME_TYPE; - if (chan->conn_state & L2CAP_CONN_SEND_FBIT) { + if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control |= L2CAP_CTRL_FINAL; - chan->conn_state &= ~L2CAP_CONN_SEND_FBIT; - } - if (chan->conn_state & L2CAP_CONN_SEND_PBIT) { + if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state)) control |= L2CAP_CTRL_POLL; - chan->conn_state &= ~L2CAP_CONN_SEND_PBIT; - } skb = bt_skb_alloc(count, GFP_ATOMIC); if (!skb) @@ -620,9 +616,9 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control) { - if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) { + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { control |= L2CAP_SUPER_RCV_NOT_READY; - chan->conn_state |= L2CAP_CONN_RNR_SENT; + set_bit(CONN_RNR_SENT, &chan->conn_state); } else control |= L2CAP_SUPER_RCV_READY; @@ -1223,7 +1219,7 @@ static void l2cap_retrans_timeout(unsigned long arg) chan->retry_count = 1; __set_monitor_timer(chan); - chan->conn_state |= L2CAP_CONN_WAIT_F; + set_bit(CONN_WAIT_F, &chan->conn_state); l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); bh_unlock_sock(sk); @@ -1314,10 +1310,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); control &= L2CAP_CTRL_SAR; - if (chan->conn_state & L2CAP_CONN_SEND_FBIT) { + if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control |= L2CAP_CTRL_FINAL; - chan->conn_state &= ~L2CAP_CONN_SEND_FBIT; - } control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); @@ -1356,10 +1350,9 @@ int l2cap_ertm_send(struct l2cap_chan *chan) control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); control &= L2CAP_CTRL_SAR; - if (chan->conn_state & L2CAP_CONN_SEND_FBIT) { + if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control |= L2CAP_CTRL_FINAL; - chan->conn_state &= ~L2CAP_CONN_SEND_FBIT; - } + control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) | (chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); @@ -1411,9 +1404,9 @@ static void l2cap_send_ack(struct l2cap_chan *chan) control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) { + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { control |= L2CAP_SUPER_RCV_NOT_READY; - chan->conn_state |= L2CAP_CONN_RNR_SENT; + set_bit(CONN_RNR_SENT, &chan->conn_state); l2cap_send_sframe(chan, control); return; } @@ -1680,8 +1673,8 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) break; } - if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && - (chan->conn_state & L2CAP_CONN_WAIT_F)) { + if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && + test_bit(CONN_WAIT_F, &chan->conn_state)) { err = len; break; } @@ -3071,18 +3064,18 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) { + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { control |= L2CAP_SUPER_RCV_NOT_READY; l2cap_send_sframe(chan, control); - chan->conn_state |= L2CAP_CONN_RNR_SENT; + set_bit(CONN_RNR_SENT, &chan->conn_state); } - if (chan->conn_state & L2CAP_CONN_REMOTE_BUSY) + if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) l2cap_retransmit_frames(chan); l2cap_ertm_send(chan); - if (!(chan->conn_state & L2CAP_CONN_LOCAL_BUSY) && + if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) && chan->frames_sent == 0) { control |= L2CAP_SUPER_RCV_READY; l2cap_send_sframe(chan, control); @@ -3138,13 +3131,13 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk switch (control & L2CAP_CTRL_SAR) { case L2CAP_SDU_UNSEGMENTED: - if (chan->conn_state & L2CAP_CONN_SAR_SDU) + if (test_bit(CONN_SAR_SDU, &chan->conn_state)) goto drop; return chan->ops->recv(chan->data, skb); case L2CAP_SDU_START: - if (chan->conn_state & L2CAP_CONN_SAR_SDU) + if (test_bit(CONN_SAR_SDU, &chan->conn_state)) goto drop; chan->sdu_len = get_unaligned_le16(skb->data); @@ -3163,12 +3156,12 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - chan->conn_state |= L2CAP_CONN_SAR_SDU; + set_bit(CONN_SAR_SDU, &chan->conn_state); chan->partial_sdu_len = skb->len; break; case L2CAP_SDU_CONTINUE: - if (!(chan->conn_state & L2CAP_CONN_SAR_SDU)) + if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) goto disconnect; if (!chan->sdu) @@ -3183,13 +3176,13 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk break; case L2CAP_SDU_END: - if (!(chan->conn_state & L2CAP_CONN_SAR_SDU)) + if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) goto disconnect; if (!chan->sdu) goto disconnect; - if (!(chan->conn_state & L2CAP_CONN_SAR_RETRY)) { + if (!test_bit(CONN_SAR_RETRY, &chan->conn_state)) { chan->partial_sdu_len += skb->len; if (chan->partial_sdu_len > chan->imtu) @@ -3203,19 +3196,19 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk _skb = skb_clone(chan->sdu, GFP_ATOMIC); if (!_skb) { - chan->conn_state |= L2CAP_CONN_SAR_RETRY; + set_bit(CONN_SAR_RETRY, &chan->conn_state); return -ENOMEM; } err = chan->ops->recv(chan->data, _skb); if (err < 0) { kfree_skb(_skb); - chan->conn_state |= L2CAP_CONN_SAR_RETRY; + set_bit(CONN_SAR_RETRY, &chan->conn_state); return err; } - chan->conn_state &= ~L2CAP_CONN_SAR_RETRY; - chan->conn_state &= ~L2CAP_CONN_SAR_SDU; + clear_bit(CONN_SAR_RETRY, &chan->conn_state); + clear_bit(CONN_SAR_SDU, &chan->conn_state); kfree_skb(chan->sdu); break; @@ -3251,7 +3244,7 @@ static int l2cap_try_push_rx_skb(struct l2cap_chan *chan) chan->buffer_seq = (chan->buffer_seq + 1) % 64; } - if (!(chan->conn_state & L2CAP_CONN_RNR_SENT)) + if (!test_bit(CONN_RNR_SENT, &chan->conn_state)) goto done; control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; @@ -3262,11 +3255,11 @@ static int l2cap_try_push_rx_skb(struct l2cap_chan *chan) __clear_retrans_timer(chan); __set_monitor_timer(chan); - chan->conn_state |= L2CAP_CONN_WAIT_F; + set_bit(CONN_WAIT_F, &chan->conn_state); done: - chan->conn_state &= ~L2CAP_CONN_LOCAL_BUSY; - chan->conn_state &= ~L2CAP_CONN_RNR_SENT; + clear_bit(CONN_LOCAL_BUSY, &chan->conn_state); + clear_bit(CONN_RNR_SENT, &chan->conn_state); BT_DBG("chan %p, Exit local busy", chan); @@ -3324,7 +3317,7 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c { int sctrl, err; - if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) { + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; __skb_queue_tail(&chan->busy_q, skb); return l2cap_try_push_rx_skb(chan); @@ -3341,7 +3334,7 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c /* Busy Condition */ BT_DBG("chan %p, Enter local busy", chan); - chan->conn_state |= L2CAP_CONN_LOCAL_BUSY; + set_bit(CONN_LOCAL_BUSY, &chan->conn_state); bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; __skb_queue_tail(&chan->busy_q, skb); @@ -3349,7 +3342,7 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c sctrl |= L2CAP_SUPER_RCV_NOT_READY; l2cap_send_sframe(chan, sctrl); - chan->conn_state |= L2CAP_CONN_RNR_SENT; + set_bit(CONN_RNR_SENT, &chan->conn_state); __clear_ack_timer(chan); @@ -3370,7 +3363,7 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf switch (control & L2CAP_CTRL_SAR) { case L2CAP_SDU_UNSEGMENTED: - if (chan->conn_state & L2CAP_CONN_SAR_SDU) { + if (test_bit(CONN_SAR_SDU, &chan->conn_state)) { kfree_skb(chan->sdu); break; } @@ -3382,7 +3375,7 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf break; case L2CAP_SDU_START: - if (chan->conn_state & L2CAP_CONN_SAR_SDU) { + if (test_bit(CONN_SAR_SDU, &chan->conn_state)) { kfree_skb(chan->sdu); break; } @@ -3403,13 +3396,13 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - chan->conn_state |= L2CAP_CONN_SAR_SDU; + set_bit(CONN_SAR_SDU, &chan->conn_state); chan->partial_sdu_len = skb->len; err = 0; break; case L2CAP_SDU_CONTINUE: - if (!(chan->conn_state & L2CAP_CONN_SAR_SDU)) + if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) break; memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); @@ -3423,12 +3416,12 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf break; case L2CAP_SDU_END: - if (!(chan->conn_state & L2CAP_CONN_SAR_SDU)) + if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) break; memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - chan->conn_state &= ~L2CAP_CONN_SAR_SDU; + clear_bit(CONN_SAR_SDU, &chan->conn_state); chan->partial_sdu_len += skb->len; if (chan->partial_sdu_len > chan->imtu) @@ -3519,11 +3512,11 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont tx_seq, rx_control); if (L2CAP_CTRL_FINAL & rx_control && - chan->conn_state & L2CAP_CONN_WAIT_F) { + test_bit(CONN_WAIT_F, &chan->conn_state)) { __clear_monitor_timer(chan); if (chan->unacked_frames > 0) __set_retrans_timer(chan); - chan->conn_state &= ~L2CAP_CONN_WAIT_F; + clear_bit(CONN_WAIT_F, &chan->conn_state); } chan->expected_ack_seq = req_seq; @@ -3542,10 +3535,10 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont goto drop; } - if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) goto drop; - if (chan->conn_state & L2CAP_CONN_SREJ_SENT) { + if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { struct srej_list *first; first = list_first_entry(&chan->srej_l, @@ -3559,7 +3552,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont if (list_empty(&chan->srej_l)) { chan->buffer_seq = chan->buffer_seq_srej; - chan->conn_state &= ~L2CAP_CONN_SREJ_SENT; + clear_bit(CONN_SREJ_SENT, &chan->conn_state); l2cap_send_ack(chan); BT_DBG("chan %p, Exit SREJ_SENT", chan); } @@ -3588,7 +3581,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont if (tx_seq_offset < expected_tx_seq_offset) goto drop; - chan->conn_state |= L2CAP_CONN_SREJ_SENT; + set_bit(CONN_SREJ_SENT, &chan->conn_state); BT_DBG("chan %p, Enter SREJ", chan); @@ -3599,7 +3592,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont __skb_queue_head_init(&chan->busy_q); l2cap_add_to_srej_queue(chan, skb, tx_seq, sar); - chan->conn_state |= L2CAP_CONN_SEND_PBIT; + set_bit(CONN_SEND_PBIT, &chan->conn_state); l2cap_send_srejframe(chan, tx_seq); @@ -3610,7 +3603,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont expected: chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; - if (chan->conn_state & L2CAP_CONN_SREJ_SENT) { + if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { bt_cb(skb)->tx_seq = tx_seq; bt_cb(skb)->sar = sar; __skb_queue_tail(&chan->srej_q, skb); @@ -3622,9 +3615,7 @@ expected: return 0; if (rx_control & L2CAP_CTRL_FINAL) { - if (chan->conn_state & L2CAP_CONN_REJ_ACT) - chan->conn_state &= ~L2CAP_CONN_REJ_ACT; - else + if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) l2cap_retransmit_frames(chan); } @@ -3650,33 +3641,31 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co l2cap_drop_acked_frames(chan); if (rx_control & L2CAP_CTRL_POLL) { - chan->conn_state |= L2CAP_CONN_SEND_FBIT; - if (chan->conn_state & L2CAP_CONN_SREJ_SENT) { - if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && + set_bit(CONN_SEND_FBIT, &chan->conn_state); + if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { + if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && (chan->unacked_frames > 0)) __set_retrans_timer(chan); - chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); l2cap_send_srejtail(chan); } else { l2cap_send_i_or_rr_or_rnr(chan); } } else if (rx_control & L2CAP_CTRL_FINAL) { - chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - if (chan->conn_state & L2CAP_CONN_REJ_ACT) - chan->conn_state &= ~L2CAP_CONN_REJ_ACT; - else + if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) l2cap_retransmit_frames(chan); } else { - if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && + if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && (chan->unacked_frames > 0)) __set_retrans_timer(chan); - chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - if (chan->conn_state & L2CAP_CONN_SREJ_SENT) + clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); + if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) l2cap_send_ack(chan); else l2cap_ertm_send(chan); @@ -3689,21 +3678,19 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); - chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); chan->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(chan); if (rx_control & L2CAP_CTRL_FINAL) { - if (chan->conn_state & L2CAP_CONN_REJ_ACT) - chan->conn_state &= ~L2CAP_CONN_REJ_ACT; - else + if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) l2cap_retransmit_frames(chan); } else { l2cap_retransmit_frames(chan); - if (chan->conn_state & L2CAP_CONN_WAIT_F) - chan->conn_state |= L2CAP_CONN_REJ_ACT; + if (test_bit(CONN_WAIT_F, &chan->conn_state)) + set_bit(CONN_REJ_ACT, &chan->conn_state); } } static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control) @@ -3712,32 +3699,32 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_ BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); - chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); if (rx_control & L2CAP_CTRL_POLL) { chan->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(chan); - chan->conn_state |= L2CAP_CONN_SEND_FBIT; + set_bit(CONN_SEND_FBIT, &chan->conn_state); l2cap_retransmit_one_frame(chan, tx_seq); l2cap_ertm_send(chan); - if (chan->conn_state & L2CAP_CONN_WAIT_F) { + if (test_bit(CONN_WAIT_F, &chan->conn_state)) { chan->srej_save_reqseq = tx_seq; - chan->conn_state |= L2CAP_CONN_SREJ_ACT; + set_bit(CONN_SREJ_ACT, &chan->conn_state); } } else if (rx_control & L2CAP_CTRL_FINAL) { - if ((chan->conn_state & L2CAP_CONN_SREJ_ACT) && + if (test_bit(CONN_SREJ_ACT, &chan->conn_state) && chan->srej_save_reqseq == tx_seq) - chan->conn_state &= ~L2CAP_CONN_SREJ_ACT; + clear_bit(CONN_SREJ_ACT, &chan->conn_state); else l2cap_retransmit_one_frame(chan, tx_seq); } else { l2cap_retransmit_one_frame(chan, tx_seq); - if (chan->conn_state & L2CAP_CONN_WAIT_F) { + if (test_bit(CONN_WAIT_F, &chan->conn_state)) { chan->srej_save_reqseq = tx_seq; - chan->conn_state |= L2CAP_CONN_SREJ_ACT; + set_bit(CONN_SREJ_ACT, &chan->conn_state); } } } @@ -3748,14 +3735,14 @@ static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_c BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); - chan->conn_state |= L2CAP_CONN_REMOTE_BUSY; + set_bit(CONN_REMOTE_BUSY, &chan->conn_state); chan->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(chan); if (rx_control & L2CAP_CTRL_POLL) - chan->conn_state |= L2CAP_CONN_SEND_FBIT; + set_bit(CONN_SEND_FBIT, &chan->conn_state); - if (!(chan->conn_state & L2CAP_CONN_SREJ_SENT)) { + if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) { __clear_retrans_timer(chan); if (rx_control & L2CAP_CTRL_POLL) l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL); @@ -3773,11 +3760,11 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len); if (L2CAP_CTRL_FINAL & rx_control && - chan->conn_state & L2CAP_CONN_WAIT_F) { + test_bit(CONN_WAIT_F, &chan->conn_state)) { __clear_monitor_timer(chan); if (chan->unacked_frames > 0) __set_retrans_timer(chan); - chan->conn_state &= ~L2CAP_CONN_WAIT_F; + clear_bit(CONN_WAIT_F, &chan->conn_state); } switch (rx_control & L2CAP_CTRL_SUPERVISE) { -- cgit v1.2.3 From d8ad7d1123a960cc9f276bd499f9325c6f5e1bd1 Mon Sep 17 00:00:00 2001 From: Takao Indoh Date: Tue, 29 Mar 2011 12:35:04 -0400 Subject: generic-ipi: Fix kexec boot crash by initializing call_single_queue before enabling interrupts There is a problem that kdump(2nd kernel) sometimes hangs up due to a pending IPI from 1st kernel. Kernel panic occurs because IPI comes before call_single_queue is initialized. To fix the crash, rename init_call_single_data() to call_function_init() and call it in start_kernel() so that call_single_queue can be initialized before enabling interrupts. The details of the crash are: (1) 2nd kernel boots up (2) A pending IPI from 1st kernel comes when irqs are first enabled in start_kernel(). (3) Kernel tries to handle the interrupt, but call_single_queue is not initialized yet at this point. As a result, in the generic_smp_call_function_single_interrupt(), NULL pointer dereference occurs when list_replace_init() tries to access &q->list.next. Therefore this patch changes the name of init_call_single_data() to call_function_init() and calls it before local_irq_enable() in start_kernel(). Signed-off-by: Takao Indoh Reviewed-by: WANG Cong Acked-by: Neil Horman Acked-by: Vivek Goyal Acked-by: Peter Zijlstra Cc: Milton Miller Cc: Jens Axboe Cc: Paul E. McKenney Cc: kexec@lists.infradead.org Link: http://lkml.kernel.org/r/D6CBEE2F420741indou.takao@jp.fujitsu.com Signed-off-by: Ingo Molnar --- include/linux/smp.h | 5 ++++- init/main.c | 1 + kernel/smp.c | 5 +---- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/smp.h b/include/linux/smp.h index 7ad824d510a2..8cc38d3bab0c 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -85,12 +85,15 @@ int smp_call_function_any(const struct cpumask *mask, * Generic and arch helpers */ #ifdef CONFIG_USE_GENERIC_SMP_HELPERS +void __init call_function_init(void); void generic_smp_call_function_single_interrupt(void); void generic_smp_call_function_interrupt(void); void ipi_call_lock(void); void ipi_call_unlock(void); void ipi_call_lock_irq(void); void ipi_call_unlock_irq(void); +#else +static inline void call_function_init(void) { } #endif /* @@ -134,7 +137,7 @@ static inline void smp_send_reschedule(int cpu) { } #define smp_prepare_boot_cpu() do {} while (0) #define smp_call_function_many(mask, func, info, wait) \ (up_smp_call_function(func, info)) -static inline void init_call_single_data(void) { } +static inline void call_function_init(void) { } static inline int smp_call_function_any(const struct cpumask *mask, smp_call_func_t func, diff --git a/init/main.c b/init/main.c index cafba67c13bf..d7211faed2ad 100644 --- a/init/main.c +++ b/init/main.c @@ -542,6 +542,7 @@ asmlinkage void __init start_kernel(void) timekeeping_init(); time_init(); profile_init(); + call_function_init(); if (!irqs_disabled()) printk(KERN_CRIT "start_kernel(): bug: interrupts were " "enabled early\n"); diff --git a/kernel/smp.c b/kernel/smp.c index 73a195193558..fb67dfa8394e 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -74,7 +74,7 @@ static struct notifier_block __cpuinitdata hotplug_cfd_notifier = { .notifier_call = hotplug_cfd, }; -static int __cpuinit init_call_single_data(void) +void __init call_function_init(void) { void *cpu = (void *)(long)smp_processor_id(); int i; @@ -88,10 +88,7 @@ static int __cpuinit init_call_single_data(void) hotplug_cfd(&hotplug_cfd_notifier, CPU_UP_PREPARE, cpu); register_cpu_notifier(&hotplug_cfd_notifier); - - return 0; } -early_initcall(init_call_single_data); /* * csd_lock/csd_unlock used to serialize access to per-cpu csd resources -- cgit v1.2.3 From 879669961b11e7f40b518784863a259f735a72bf Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 17 Jun 2011 11:25:59 +0100 Subject: KEYS/DNS: Fix ____call_usermodehelper() to not lose the session keyring ____call_usermodehelper() now erases any credentials set by the subprocess_inf::init() function. The problem is that commit 17f60a7da150 ("capabilites: allow the application of capability limits to usermode helpers") creates and commits new credentials with prepare_kernel_cred() after the call to the init() function. This wipes all keyrings after umh_keys_init() is called. The best way to deal with this is to put the init() call just prior to the commit_creds() call, and pass the cred pointer to init(). That means that umh_keys_init() and suchlike can modify the credentials _before_ they are published and potentially in use by the rest of the system. This prevents request_key() from working as it is prevented from passing the session keyring it set up with the authorisation token to /sbin/request-key, and so the latter can't assume the authority to instantiate the key. This causes the in-kernel DNS resolver to fail with ENOKEY unconditionally. Signed-off-by: David Howells Acked-by: Eric Paris Tested-by: Jeff Layton Signed-off-by: Linus Torvalds --- fs/exec.c | 2 +- include/linux/kmod.h | 8 ++++---- kernel/kmod.c | 16 +++++++++------- security/keys/request_key.c | 3 +-- 4 files changed, 15 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index 97e0d52d72fd..6075a1e727ae 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1996,7 +1996,7 @@ static void wait_for_dump_helpers(struct file *file) * is a special value that we use to trap recursive * core dumps */ -static int umh_pipe_setup(struct subprocess_info *info) +static int umh_pipe_setup(struct subprocess_info *info, struct cred *new) { struct file *rp, *wp; struct fdtable *fdt; diff --git a/include/linux/kmod.h b/include/linux/kmod.h index d4a5c84c503d..0da38cf7db7b 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -45,7 +45,7 @@ static inline int request_module_nowait(const char *name, ...) { return -ENOSYS; #endif -struct key; +struct cred; struct file; enum umh_wait { @@ -62,7 +62,7 @@ struct subprocess_info { char **envp; enum umh_wait wait; int retval; - int (*init)(struct subprocess_info *info); + int (*init)(struct subprocess_info *info, struct cred *new); void (*cleanup)(struct subprocess_info *info); void *data; }; @@ -73,7 +73,7 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, /* Set various pieces of state into the subprocess_info structure */ void call_usermodehelper_setfns(struct subprocess_info *info, - int (*init)(struct subprocess_info *info), + int (*init)(struct subprocess_info *info, struct cred *new), void (*cleanup)(struct subprocess_info *info), void *data); @@ -87,7 +87,7 @@ void call_usermodehelper_freeinfo(struct subprocess_info *info); static inline int call_usermodehelper_fns(char *path, char **argv, char **envp, enum umh_wait wait, - int (*init)(struct subprocess_info *info), + int (*init)(struct subprocess_info *info, struct cred *new), void (*cleanup)(struct subprocess_info *), void *data) { struct subprocess_info *info; diff --git a/kernel/kmod.c b/kernel/kmod.c index ad6a81c58b44..47613dfb7b28 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -156,12 +156,6 @@ static int ____call_usermodehelper(void *data) */ set_user_nice(current, 0); - if (sub_info->init) { - retval = sub_info->init(sub_info); - if (retval) - goto fail; - } - retval = -ENOMEM; new = prepare_kernel_cred(current); if (!new) @@ -173,6 +167,14 @@ static int ____call_usermodehelper(void *data) new->cap_inheritable); spin_unlock(&umh_sysctl_lock); + if (sub_info->init) { + retval = sub_info->init(sub_info, new); + if (retval) { + abort_creds(new); + goto fail; + } + } + commit_creds(new); retval = kernel_execve(sub_info->path, @@ -388,7 +390,7 @@ EXPORT_SYMBOL(call_usermodehelper_setup); * context in which call_usermodehelper_exec is called. */ void call_usermodehelper_setfns(struct subprocess_info *info, - int (*init)(struct subprocess_info *info), + int (*init)(struct subprocess_info *info, struct cred *new), void (*cleanup)(struct subprocess_info *info), void *data) { diff --git a/security/keys/request_key.c b/security/keys/request_key.c index d31862e0aa1c..8e319a416eec 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -71,9 +71,8 @@ EXPORT_SYMBOL(complete_request_key); * This is called in context of freshly forked kthread before kernel_execve(), * so we can simply install the desired session_keyring at this point. */ -static int umh_keys_init(struct subprocess_info *info) +static int umh_keys_init(struct subprocess_info *info, struct cred *cred) { - struct cred *cred = (struct cred*)current_cred(); struct key *keyring = info->data; return install_session_keyring_to_cred(cred, keyring); -- cgit v1.2.3 From b856439b1b54358e580aaee5dbe683af5ada9403 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 13 Jun 2011 12:47:30 +0300 Subject: mac80211: add cancel_hw_scan() callback When suspending, __ieee80211_suspend() calls ieee80211_scan_cancel(), which will only cancel sw scan. In order to cancel hw scan, the low-level driver has to cancel it in the suspend() callback. however, this is too late, as a new scan_work will be enqueued (while the driver is going into suspend). Add a new cancel_hw_scan() callback, asking the driver to cancel an active hw scan, and call it in ieee80211_scan_cancel(). Signed-off-by: Eliad Peller Reviewed-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- include/net/mac80211.h | 10 ++++++++++ net/mac80211/driver-ops.h | 10 ++++++++++ net/mac80211/driver-trace.h | 6 ++++++ net/mac80211/scan.c | 37 +++++++++++++++++++++---------------- 4 files changed, 47 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e33fe795a3a4..120f102814b6 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1708,6 +1708,14 @@ enum ieee80211_ampdu_mlme_action { * any error unless this callback returned a negative error code. * The callback can sleep. * + * @cancel_hw_scan: Ask the low-level tp cancel the active hw scan. + * The driver should ask the hardware to cancel the scan (if possible), + * but the scan will be completed only after the driver will call + * ieee80211_scan_completed(). + * This callback is needed for wowlan, to prevent enqueueing a new + * scan_work after the low-level driver was already suspended. + * The callback can sleep. + * * @sched_scan_start: Ask the hardware to start scanning repeatedly at * specific intervals. The driver must call the * ieee80211_sched_scan_results() function whenever it finds results. @@ -1900,6 +1908,8 @@ struct ieee80211_ops { u32 iv32, u16 *phase1key); int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req); + void (*cancel_hw_scan)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); int (*sched_scan_start)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index eebf7a67daf7..0e7e4268ddf6 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -218,6 +218,16 @@ static inline int drv_hw_scan(struct ieee80211_local *local, return ret; } +static inline void drv_cancel_hw_scan(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + trace_drv_cancel_hw_scan(local, sdata); + local->ops->cancel_hw_scan(&local->hw, &sdata->vif); + trace_drv_return_void(local); +} + static inline int drv_sched_scan_start(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index ed9edcbd9aa5..3cb6795e926d 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -460,6 +460,12 @@ DEFINE_EVENT(local_sdata_evt, drv_hw_scan, TP_ARGS(local, sdata) ); +DEFINE_EVENT(local_sdata_evt, drv_cancel_hw_scan, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); + DEFINE_EVENT(local_sdata_evt, drv_sched_scan_start, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 58ffa7d069c7..1758b463c583 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -821,10 +821,8 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, */ void ieee80211_scan_cancel(struct ieee80211_local *local) { - bool abortscan; - /* - * We are only canceling software scan, or deferred scan that was not + * We are canceling software scan, or deferred scan that was not * yet really started (see __ieee80211_start_scan ). * * Regarding hardware scan: @@ -836,23 +834,30 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) * - we can not cancel scan_work since driver can schedule it * by ieee80211_scan_completed(..., true) to finish scan * - * Hence low lever driver is responsible for canceling HW scan. + * Hence we only call the cancel_hw_scan() callback, but the low-level + * driver is still responsible for calling ieee80211_scan_completed() + * after the scan was completed/aborted. */ mutex_lock(&local->mtx); - abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning); - if (abortscan) { - /* - * The scan is canceled, but stop work from being pending. - * - * If the work is currently running, it must be blocked on - * the mutex, but we'll set scan_sdata = NULL and it'll - * simply exit once it acquires the mutex. - */ - cancel_delayed_work(&local->scan_work); - /* and clean up */ - __ieee80211_scan_completed(&local->hw, true, false); + if (!local->scan_req) + goto out; + + if (test_bit(SCAN_HW_SCANNING, &local->scanning)) { + if (local->ops->cancel_hw_scan) + drv_cancel_hw_scan(local, local->scan_sdata); + goto out; } + + /* + * If the work is currently running, it must be blocked on + * the mutex, but we'll set scan_sdata = NULL and it'll + * simply exit once it acquires the mutex. + */ + cancel_delayed_work(&local->scan_work); + /* and clean up */ + __ieee80211_scan_completed(&local->hw, true, false); +out: mutex_unlock(&local->mtx); } -- cgit v1.2.3 From cca23d0b5350c9ca0473625c3f5879422ba534a6 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Sat, 18 Jun 2011 02:51:52 -0700 Subject: Input: sh_keysc - 8x8 MODE_6 fix According to the data sheet for G4, AP4 and AG5 KEYSC MODE_6 is 8x8 keys. Bump up MAXKEYS to 64 too. Signed-off-by: Magnus Damm Reviewed-by: Simon Horman Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/sh_keysc.c | 2 +- include/linux/input/sh_keysc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index 834cf98e7efb..6876700a4469 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -32,7 +32,7 @@ static const struct { [SH_KEYSC_MODE_3] = { 2, 4, 7 }, [SH_KEYSC_MODE_4] = { 3, 6, 6 }, [SH_KEYSC_MODE_5] = { 4, 6, 7 }, - [SH_KEYSC_MODE_6] = { 5, 7, 7 }, + [SH_KEYSC_MODE_6] = { 5, 8, 8 }, }; struct sh_keysc_priv { diff --git a/include/linux/input/sh_keysc.h b/include/linux/input/sh_keysc.h index 649dc7f12925..5d253cd93691 100644 --- a/include/linux/input/sh_keysc.h +++ b/include/linux/input/sh_keysc.h @@ -1,7 +1,7 @@ #ifndef __SH_KEYSC_H__ #define __SH_KEYSC_H__ -#define SH_KEYSC_MAXKEYS 49 +#define SH_KEYSC_MAXKEYS 64 struct sh_keysc_info { enum { SH_KEYSC_MODE_1, SH_KEYSC_MODE_2, SH_KEYSC_MODE_3, -- cgit v1.2.3 From be98ca652faa6468916a9b7608befff215a8ca70 Mon Sep 17 00:00:00 2001 From: Manoj Iyer Date: Thu, 26 May 2011 11:19:05 -0500 Subject: mmc: Add PCI fixup quirks for Ricoh 1180:e823 reader Signed-off-by: Manoj Iyer Cc: Signed-off-by: Chris Ball --- drivers/pci/quirks.c | 2 ++ include/linux/pci_ids.h | 1 + 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index e8a140669f90..02145e9697a9 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2761,6 +2761,8 @@ static void ricoh_mmc_fixup_r5c832(struct pci_dev *dev) } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, ricoh_mmc_fixup_r5c832); DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, ricoh_mmc_fixup_r5c832); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5CE823, ricoh_mmc_fixup_r5c832); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5CE823, ricoh_mmc_fixup_r5c832); #endif /*CONFIG_MMC_RICOH_MMC*/ #if defined(CONFIG_DMAR) || defined(CONFIG_INTR_REMAP) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index a311008af5e1..f8910e155566 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1537,6 +1537,7 @@ #define PCI_DEVICE_ID_RICOH_RL5C476 0x0476 #define PCI_DEVICE_ID_RICOH_RL5C478 0x0478 #define PCI_DEVICE_ID_RICOH_R5C822 0x0822 +#define PCI_DEVICE_ID_RICOH_R5CE823 0xe823 #define PCI_DEVICE_ID_RICOH_R5C832 0x0832 #define PCI_DEVICE_ID_RICOH_R5C843 0x0843 -- cgit v1.2.3 From 4ff75b7cf3b06efb72a50e26008d09b259b1231b Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 19 Jun 2011 03:31:39 +0000 Subject: net: correct comment on where to place transmit time stamp hook. The comment for the skb_tx_timestamp() function suggests calling it just after a buffer is released to the hardware for transmission. However, for drivers that free the buffer in an ISR, this produces a race between the time stamp code and the ISR. This commit changes the comment to advise placing the call just before handing the buffer over to the hardware. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- include/linux/skbuff.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index e8b78ce14474..f3af147c211d 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -2023,8 +2023,7 @@ static inline void sw_tx_timestamp(struct sk_buff *skb) * skb_tx_timestamp() - Driver hook for transmit timestamping * * Ethernet MAC Drivers should call this function in their hard_xmit() - * function as soon as possible after giving the sk_buff to the MAC - * hardware, but before freeing the sk_buff. + * function immediately before giving the sk_buff to the MAC hardware. * * @skb: A socket buffer. */ -- cgit v1.2.3 From 155d109b5f52ffd749219b27702462dcd9cf4f8d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 20 Jun 2011 13:23:14 +0200 Subject: block: add REQ_SECURE to REQ_COMMON_MASK Add REQ_SECURE flag to REQ_COMMON_MASK so that init_request_from_bio() can pass it to @req->cmd_flags. Signed-off-by: Namhyung Kim Acked-by: Adrian Hunter Cc: stable@kernel.org # 2.6.36 and newer Signed-off-by: Jens Axboe --- include/linux/blk_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 2a7cea53ca0d..6395692b2e7a 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -167,7 +167,7 @@ enum rq_flag_bits { (REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER) #define REQ_COMMON_MASK \ (REQ_WRITE | REQ_FAILFAST_MASK | REQ_SYNC | REQ_META | REQ_DISCARD | \ - REQ_NOIDLE | REQ_FLUSH | REQ_FUA) + REQ_NOIDLE | REQ_FLUSH | REQ_FUA | REQ_SECURE) #define REQ_CLONE_MASK REQ_COMMON_MASK #define REQ_RAHEAD (1 << __REQ_RAHEAD) -- cgit v1.2.3 From 1816315b10862277a961a70ec394b6607983041d Mon Sep 17 00:00:00 2001 From: Jonathan Neuschäfer Date: Sun, 19 Jun 2011 11:50:22 +0200 Subject: stop_machine.h: "disables preeempt" -> "disables preemption" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the noun instead of a misspelled verb. Signed-off-by: Jonathan Neuschäfer Signed-off-by: Jiri Kosina --- include/linux/stop_machine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h index 092dc9b1ce7d..2d3f0b1b054c 100644 --- a/include/linux/stop_machine.h +++ b/include/linux/stop_machine.h @@ -94,7 +94,7 @@ static inline int try_stop_cpus(const struct cpumask *cpumask, * stop_machine "Bogolock": stop the entire machine, disable * interrupts. This is a very heavy lock, which is equivalent to * grabbing every spinlock (and more). So the "read" side to such a - * lock is anything which disables preeempt. + * lock is anything which disables preemption. */ #if defined(CONFIG_STOP_MACHINE) && defined(CONFIG_SMP) -- cgit v1.2.3 From e44ba033c5654dbfda53461c9b1f7dd9bd1d198f Mon Sep 17 00:00:00 2001 From: Vitaliy Ivanov Date: Mon, 20 Jun 2011 16:08:07 +0200 Subject: treewide: remove duplicate includes Many stupid corrections of duplicated includes based on the output of scripts/checkincludes.pl. Signed-off-by: Vitaliy Ivanov Signed-off-by: Jiri Kosina --- arch/arm/mach-s3c2410/h1940-bluetooth.c | 1 - arch/mips/lantiq/devices.c | 2 -- arch/mips/lantiq/xway/devices.c | 2 -- arch/powerpc/platforms/pseries/smp.c | 1 - arch/s390/kvm/sie64a.S | 1 - arch/s390/oprofile/init.c | 2 -- arch/x86/kvm/mmu.c | 1 - arch/xtensa/include/asm/uaccess.h | 3 +-- drivers/gpio/ab8500-gpio.c | 1 - drivers/gpu/drm/nouveau/nv50_graph.c | 1 - drivers/media/rc/ite-cir.c | 1 - drivers/media/video/m5mols/m5mols_capture.c | 1 - drivers/media/video/videobuf2-memops.c | 1 - drivers/net/can/c_can/c_can.c | 1 - drivers/net/can/c_can/c_can_platform.c | 1 - drivers/net/sungem.c | 3 +-- drivers/net/wireless/ath/ath5k/ahb.c | 1 - drivers/staging/ath6kl/os/linux/include/ar6000_drv.h | 1 - drivers/staging/bcm/headers.h | 7 ++----- drivers/staging/brcm80211/brcmfmac/dhd_sdio.c | 1 - drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h | 1 - drivers/staging/brcm80211/brcmfmac/wl_iw.c | 9 ++------- drivers/staging/gma500/psb_2d.c | 1 - drivers/staging/hv/hv_mouse.c | 1 - drivers/staging/hv/tools/hv_kvp_daemon.c | 1 - drivers/staging/nvec/nvec.c | 1 - drivers/staging/rtl8712/drv_types.h | 1 - drivers/staging/rtl8712/osdep_service.h | 1 - drivers/staging/sep/sep_driver.c | 1 - drivers/staging/usbip/userspace/src/utils.h | 1 - .../westbridge/westbridge-omap3-pnand-hal/cyashalomap_kernel.h | 2 +- drivers/staging/westbridge/astoria/gadget/cyasgadget.h | 8 ++------ drivers/target/loopback/tcm_loop.c | 1 - drivers/target/tcm_fc/tfc_cmd.c | 1 - drivers/target/tcm_fc/tfc_conf.c | 1 - drivers/target/tcm_fc/tfc_io.c | 1 - drivers/target/tcm_fc/tfc_sess.c | 2 -- drivers/usb/otg/otg_fsm.c | 1 - drivers/video/udlfb.c | 1 - drivers/watchdog/intel_scu_watchdog.c | 1 - fs/nfs/idmap.c | 7 ++----- fs/ocfs2/move_extents.c | 1 - include/linux/fs.h | 1 - net/ipv4/ping.c | 1 - 44 files changed, 11 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-s3c2410/h1940-bluetooth.c b/arch/arm/mach-s3c2410/h1940-bluetooth.c index 2c126bbca08d..a5eeb62ce1c2 100644 --- a/arch/arm/mach-s3c2410/h1940-bluetooth.c +++ b/arch/arm/mach-s3c2410/h1940-bluetooth.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/arch/mips/lantiq/devices.c b/arch/mips/lantiq/devices.c index 7b82c34cb169..44a36771c819 100644 --- a/arch/mips/lantiq/devices.c +++ b/arch/mips/lantiq/devices.c @@ -15,11 +15,9 @@ #include #include #include -#include #include #include #include -#include #include #include diff --git a/arch/mips/lantiq/xway/devices.c b/arch/mips/lantiq/xway/devices.c index e09e789dfc27..d0e32ab2ea07 100644 --- a/arch/mips/lantiq/xway/devices.c +++ b/arch/mips/lantiq/xway/devices.c @@ -16,11 +16,9 @@ #include #include #include -#include #include #include #include -#include #include #include diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index fbffd7e47ab8..cd70be5ff27e 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include "plpar_wrappers.h" diff --git a/arch/s390/kvm/sie64a.S b/arch/s390/kvm/sie64a.S index 5faa1b1b23fa..cf8ec3ae2693 100644 --- a/arch/s390/kvm/sie64a.S +++ b/arch/s390/kvm/sie64a.S @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/arch/s390/oprofile/init.c b/arch/s390/oprofile/init.c index 5995e9bc72d9..0c2d94b94f0b 100644 --- a/arch/s390/oprofile/init.c +++ b/arch/s390/oprofile/init.c @@ -13,8 +13,6 @@ #include #include #include -#include -#include #include #include "../../../drivers/oprofile/oprof.h" diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index bd14bb4c8594..9b9f012c16d1 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -22,7 +22,6 @@ #include "mmu.h" #include "x86.h" #include "kvm_cache_regs.h" -#include "x86.h" #include #include diff --git a/arch/xtensa/include/asm/uaccess.h b/arch/xtensa/include/asm/uaccess.h index 5b0c18c1cce1..82d4e3815c89 100644 --- a/arch/xtensa/include/asm/uaccess.h +++ b/arch/xtensa/include/asm/uaccess.h @@ -17,6 +17,7 @@ #define _XTENSA_UACCESS_H #include +#include #define VERIFY_READ 0 #define VERIFY_WRITE 1 @@ -26,7 +27,6 @@ #include #include #include -#include /* * These assembly macros mirror the C macros that follow below. They @@ -157,7 +157,6 @@ #else /* __ASSEMBLY__ not defined */ #include -#include /* * The fs value determines whether argument validity checking should diff --git a/drivers/gpio/ab8500-gpio.c b/drivers/gpio/ab8500-gpio.c index 970053c89ff7..ed795e64eea7 100644 --- a/drivers/gpio/ab8500-gpio.c +++ b/drivers/gpio/ab8500-gpio.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index e25cbb46789a..40680f2b4231 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -31,7 +31,6 @@ #include "nouveau_grctx.h" #include "nouveau_dma.h" #include "nouveau_vm.h" -#include "nouveau_ramht.h" #include "nv50_evo.h" struct nv50_graph_engine { diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index e716b931cf7e..cd0c44e398e3 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -42,7 +42,6 @@ #include #include #include -#include #include "ite-cir.h" diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c index d71a3903b60f..e1ae5653d3b1 100644 --- a/drivers/media/video/m5mols/m5mols_capture.c +++ b/drivers/media/video/m5mols/m5mols_capture.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c index 5370a3a7ee25..b03c3aea5bea 100644 --- a/drivers/media/video/videobuf2-memops.c +++ b/drivers/media/video/videobuf2-memops.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index 7e5cc0bd913d..ff212916810b 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index cc90824f2c9c..0b071564d1dc 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index ab5930099267..70f018d37399 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -66,15 +66,14 @@ #include #include #include +#include #ifdef CONFIG_SPARC #include -#include #endif #ifdef CONFIG_PPC_PMAC #include -#include #include #include #endif diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index ea9982781559..45dd20c0dc72 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -24,7 +24,6 @@ #include "debug.h" #include "base.h" #include "reg.h" -#include "debug.h" /* return bus cachesize in 4B word units */ static void ath5k_ahb_read_cachesize(struct ath_common *common, int *csz) diff --git a/drivers/staging/ath6kl/os/linux/include/ar6000_drv.h b/drivers/staging/ath6kl/os/linux/include/ar6000_drv.h index 22453b0873e4..11dc39c6987a 100644 --- a/drivers/staging/ath6kl/os/linux/include/ar6000_drv.h +++ b/drivers/staging/ath6kl/os/linux/include/ar6000_drv.h @@ -52,7 +52,6 @@ #include "aggr_recv_api.h" #include #include -#include #include #include "ar6000_api.h" #ifdef CONFIG_HOST_TCMD_SUPPORT diff --git a/drivers/staging/bcm/headers.h b/drivers/staging/bcm/headers.h index 1148e5e22eb9..8fe8d2b1f627 100644 --- a/drivers/staging/bcm/headers.h +++ b/drivers/staging/bcm/headers.h @@ -20,25 +20,23 @@ #include #include #include -#include #include #include #include - #include #include -#include #include #include #include #include #include #include -#include #include #include #include #include +#include +#include #include "Typedefs.h" #include "Version.h" @@ -61,7 +59,6 @@ #include "Queue.h" #include "vendorspecificextn.h" - #include "InterfaceMacros.h" #include "InterfaceAdapter.h" #include "InterfaceIsr.h" diff --git a/drivers/staging/brcm80211/brcmfmac/dhd_sdio.c b/drivers/staging/brcm80211/brcmfmac/dhd_sdio.c index a71c6f8ee8a3..8cbfeae464b1 100644 --- a/drivers/staging/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/staging/brcm80211/brcmfmac/dhd_sdio.c @@ -26,7 +26,6 @@ #include BCMEMBEDIMAGE #endif /* BCMEMBEDIMAGE */ -#include #include #include diff --git a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h index 996033cf9b09..d4bcc1edddb2 100644 --- a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h @@ -17,7 +17,6 @@ #ifndef _wl_cfg80211_h_ #define _wl_cfg80211_h_ -#include #include #include #include diff --git a/drivers/staging/brcm80211/brcmfmac/wl_iw.c b/drivers/staging/brcm80211/brcmfmac/wl_iw.c index 15e1b05ca92d..ab843bc1d5d7 100644 --- a/drivers/staging/brcm80211/brcmfmac/wl_iw.c +++ b/drivers/staging/brcm80211/brcmfmac/wl_iw.c @@ -19,21 +19,16 @@ #include #include #include - #include - #include #include +#include #include #include #include -#include -typedef const struct si_pub si_t; -#include -#include -#include +typedef const struct si_pub si_t; #define WL_ERROR(fmt, args...) printk(fmt, ##args) #define WL_TRACE(fmt, args...) no_printk(fmt, ##args) diff --git a/drivers/staging/gma500/psb_2d.c b/drivers/staging/gma500/psb_2d.c index 0bd834c982d3..3aee8fc3734f 100644 --- a/drivers/staging/gma500/psb_2d.c +++ b/drivers/staging/gma500/psb_2d.c @@ -38,7 +38,6 @@ #include "psb_drv.h" #include "psb_reg.h" -#include "psb_drv.h" #include "psb_fb.h" void psb_spank(struct drm_psb_private *dev_priv) diff --git a/drivers/staging/hv/hv_mouse.c b/drivers/staging/hv/hv_mouse.c index 359e73741c48..b3324d609c8b 100644 --- a/drivers/staging/hv/hv_mouse.c +++ b/drivers/staging/hv/hv_mouse.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "hyperv.h" diff --git a/drivers/staging/hv/tools/hv_kvp_daemon.c b/drivers/staging/hv/tools/hv_kvp_daemon.c index 33f0f1c8ad73..a4a407f7052a 100644 --- a/drivers/staging/hv/tools/hv_kvp_daemon.c +++ b/drivers/staging/hv/tools/hv_kvp_daemon.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c index 1a94364c48b5..72258e8c64ca 100644 --- a/drivers/staging/nvec/nvec.c +++ b/drivers/staging/nvec/nvec.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include "nvec.h" diff --git a/drivers/staging/rtl8712/drv_types.h b/drivers/staging/rtl8712/drv_types.h index 3bb66dc2eb2c..4f380a64aa85 100644 --- a/drivers/staging/rtl8712/drv_types.h +++ b/drivers/staging/rtl8712/drv_types.h @@ -29,7 +29,6 @@ struct qos_priv { #include "rtl871x_ht.h" #include "rtl871x_cmd.h" -#include "wlan_bssdef.h" #include "rtl871x_xmit.h" #include "rtl871x_recv.h" #include "rtl871x_security.h" diff --git a/drivers/staging/rtl8712/osdep_service.h b/drivers/staging/rtl8712/osdep_service.h index 3d3f73c5cd5b..505395cff282 100644 --- a/drivers/staging/rtl8712/osdep_service.h +++ b/drivers/staging/rtl8712/osdep_service.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/staging/sep/sep_driver.c b/drivers/staging/sep/sep_driver.c index 52342c17eadd..848b4c57531d 100644 --- a/drivers/staging/sep/sep_driver.c +++ b/drivers/staging/sep/sep_driver.c @@ -50,7 +50,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/staging/usbip/userspace/src/utils.h b/drivers/staging/usbip/userspace/src/utils.h index 6c29ae945212..991f662720bb 100644 --- a/drivers/staging/usbip/userspace/src/utils.h +++ b/drivers/staging/usbip/userspace/src/utils.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/staging/westbridge/astoria/arch/arm/plat-omap/include/mach/westbridge/westbridge-omap3-pnand-hal/cyashalomap_kernel.h b/drivers/staging/westbridge/astoria/arch/arm/plat-omap/include/mach/westbridge/westbridge-omap3-pnand-hal/cyashalomap_kernel.h index 6426ea61f3d4..1e9212fbf1a6 100644 --- a/drivers/staging/westbridge/astoria/arch/arm/plat-omap/include/mach/westbridge/westbridge-omap3-pnand-hal/cyashalomap_kernel.h +++ b/drivers/staging/westbridge/astoria/arch/arm/plat-omap/include/mach/westbridge/westbridge-omap3-pnand-hal/cyashalomap_kernel.h @@ -315,5 +315,5 @@ void cy_as_hal_set_ep_dma_mode(uint8_t ep, bool sg_xfer_enabled); /* moved to staging location #include */ -#include "../../../../../../../include/linux/westbridge/cyas_cplus_start.h" +#include "../../../../../../../include/linux/westbridge/cyas_cplus_end.h" #endif diff --git a/drivers/staging/westbridge/astoria/gadget/cyasgadget.h b/drivers/staging/westbridge/astoria/gadget/cyasgadget.h index e01cea7eeb77..668e03f9e8ad 100644 --- a/drivers/staging/westbridge/astoria/gadget/cyasgadget.h +++ b/drivers/staging/westbridge/astoria/gadget/cyasgadget.h @@ -54,19 +54,15 @@ #include #include #include +#include +#include #include "../include/linux/westbridge/cyastoria.h" #include "../include/linux/westbridge/cyashal.h" #include "../include/linux/westbridge/cyasdevice.h" #include "cyasgadget_ioctl.h" -#include -#include - /*char driver defines, revisit*/ -#include -#include -#include #include /* everything... */ #include /* error codes */ #include /* size_t */ diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index dee2a2c909f5..ee959032788e 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index c056a1132ae1..7c22062ad621 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index 84e868c255dd..d963d9014973 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #include "tcm_fc.h" diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c index 4c3c0efbe13f..b4433bec6934 100644 --- a/drivers/target/tcm_fc/tfc_io.c +++ b/drivers/target/tcm_fc/tfc_io.c @@ -53,7 +53,6 @@ #include #include #include -#include #include #include "tcm_fc.h" diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c index a3bd57f2ea32..65d8ea09e3fe 100644 --- a/drivers/target/tcm_fc/tfc_sess.c +++ b/drivers/target/tcm_fc/tfc_sess.c @@ -46,10 +46,8 @@ #include #include #include -#include #include -#include #include "tcm_fc.h" static void ft_sess_delete_all(struct ft_tport *); diff --git a/drivers/usb/otg/otg_fsm.c b/drivers/usb/otg/otg_fsm.c index b0cc422f2ff9..09117387d2a4 100644 --- a/drivers/usb/otg/otg_fsm.c +++ b/drivers/usb/otg/otg_fsm.c @@ -28,7 +28,6 @@ #include #include #include -#include #include "otg_fsm.h" diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index 52b0f3e8ccac..14b152a99d13 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c @@ -29,7 +29,6 @@ #include #include #include -#include #include

+ MFC 5.1 MPEG Controls + + The following MPEG class controls deal with MPEG +decoding and encoding settings that are specific to the Multi Format Codec 5.1 device present +in the S5P family of SoCs by Samsung. + + + + MFC 5.1 Control IDs + + + + + + + + + + ID + Type + Description + + + + + + V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE  + integer + If the display delay is enabled then the decoder has to return a +CAPTURE buffer after processing a certain number of OUTPUT buffers. If this number is low, then it may result in +buffers not being dequeued in display order. In addition hardware may still use those buffers as reference, thus +application should not write to those buffers. This feature can be used for example for generating thumbnails of videos. +Applicable to the H264 decoder. + + + + + V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY  + integer + Display delay value for H264 decoder. +The decoder is forced to return a decoded frame after the set 'display delay' number of frames. If this number is +low it may result in frames returned out of dispaly order, in addition the hardware may still be using the returned buffer +as a reference picture for subsequent frames. + + + + + V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P  + integer + The number of reference pictures used for encoding a P picture. +Applicable to the H264 encoder. + + + + V4L2_CID_MPEG_MFC51_VIDEO_PADDING  + boolean + Padding enable in the encoder - use a color instead of repeating border pixels. +Applicable to encoders. + + + + V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV  + integer + Padding color in the encoder. Applicable to encoders. The supplied 32-bit integer is interpreted as follows (bit +0 = least significant bit): + + + + + + Bit 0:7 + V chrominance information + + + Bit 8:15 + U chrominance information + + + Bit 16:23 + Y luminance information + + + Bit 24:31 + Must be zero. + + + + + + + V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF  + integer + Reaction coefficient for MFC rate control. Applicable to encoders. +Note 1: Valid only when the frame level RC is enabled. +Note 2: For tight CBR, this field must be small (ex. 2 ~ 10). +For VBR, this field must be large (ex. 100 ~ 1000). +Note 3: It is not recommended to use the greater number than FRAME_RATE * (10^9 / BIT_RATE). + + + + + V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK  + boolean + Adaptive rate control for dark region. +Valid only when H.264 and macroblock level RC is enabled (V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE). +Applicable to the H264 encoder. + + + + V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH  + boolean + Adaptive rate control for smooth region. +Valid only when H.264 and macroblock level RC is enabled (V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE). +Applicable to the H264 encoder. + + + + V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC  + boolean + Adaptive rate control for static region. +Valid only when H.264 and macroblock level RC is enabled (V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE). +Applicable to the H264 encoder. + + + + V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY  + boolean + Adaptive rate control for activity region. +Valid only when H.264 and macroblock level RC is enabled (V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE). +Applicable to the H264 encoder. + + + + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE  + enum v4l2_mpeg_mfc51_frame_skip_mode + + +Indicates in what conditions the encoder should skip frames. If encoding a frame would cause the encoded stream to be larger then +a chosen data limit then the frame will be skipped. +Possible values are: + + + + + + V4L2_MPEG_MFC51_FRAME_SKIP_MODE_DISABLED  + Frame skip mode is disabled. + + + V4L2_MPEG_MFC51_FRAME_SKIP_MODE_LEVEL_LIMIT  + Frame skip mode enabled and buffer limit is set by the chosen level and is defined by the standard. + + + V4L2_MPEG_MFC51_FRAME_SKIP_MODE_BUF_LIMIT  + Frame skip mode enabled and buffer limit is set by the VBV (MPEG1/2/4) or CPB (H264) buffer size control. + + + + + + + V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT  + integer + Enable rate-control with fixed target bit. +If this setting is enabled, then the rate control logic of the encoder will calculate the average bitrate +for a GOP and keep it below or equal the set bitrate target. Otherwise the rate control logic calculates the +overall average bitrate for the stream and keeps it below or equal to the set bitrate. In the first case +the average bitrate for the whole stream will be smaller then the set bitrate. This is caused because the +average is calculated for smaller number of frames, on the other hand enabling this setting will ensure that +the stream will meet tight bandwidth contraints. Applicable to encoders. + + + + + V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE  + enum v4l2_mpeg_mfc51_force_frame_type + + Force a frame type for the next queued buffer. Applicable to encoders. +Possible values are: + + + + + + V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_DISABLED  + Forcing a specific frame type disabled. + + + V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_I_FRAME  + Force an I-frame. + + + V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_NOT_CODED  + Force a non-coded frame. + + + +
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index cb22a2c7a467..fca24cc50436 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1156,8 +1156,13 @@ enum v4l2_colorfx { #define V4L2_CID_ILLUMINATORS_1 (V4L2_CID_BASE+37) #define V4L2_CID_ILLUMINATORS_2 (V4L2_CID_BASE+38) +#define V4L2_CID_MIN_BUFFERS_FOR_CAPTURE (V4L2_CID_BASE+39) +#define V4L2_CID_MIN_BUFFERS_FOR_OUTPUT (V4L2_CID_BASE+40) + /* last CID + 1 */ -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+39) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+41) + +/* Minimum number of buffer neede by the device */ /* MPEG-class control IDs defined by V4L2 */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) @@ -1329,6 +1334,141 @@ enum v4l2_mpeg_video_bitrate_mode { #define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_MPEG_BASE+209) #define V4L2_CID_MPEG_VIDEO_MUTE (V4L2_CID_MPEG_BASE+210) #define V4L2_CID_MPEG_VIDEO_MUTE_YUV (V4L2_CID_MPEG_BASE+211) +#define V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE (V4L2_CID_MPEG_BASE+212) +#define V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER (V4L2_CID_MPEG_BASE+213) +#define V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (V4L2_CID_MPEG_BASE+214) +#define V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE (V4L2_CID_MPEG_BASE+215) +#define V4L2_CID_MPEG_VIDEO_HEADER_MODE (V4L2_CID_MPEG_BASE+216) +enum v4l2_mpeg_video_header_mode { + V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE = 0, + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME = 1, + +}; +#define V4L2_CID_MPEG_VIDEO_MAX_REF_PIC (V4L2_CID_MPEG_BASE+217) +#define V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE (V4L2_CID_MPEG_BASE+218) +#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES (V4L2_CID_MPEG_BASE+219) +#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB (V4L2_CID_MPEG_BASE+220) +#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE (V4L2_CID_MPEG_BASE+221) +enum v4l2_mpeg_video_multi_slice_mode { + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE = 0, + V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB = 1, + V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES = 2, +}; +#define V4L2_CID_MPEG_VIDEO_VBV_SIZE (V4L2_CID_MPEG_BASE+222) +#define V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (V4L2_CID_MPEG_BASE+300) +#define V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (V4L2_CID_MPEG_BASE+301) +#define V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP (V4L2_CID_MPEG_BASE+302) +#define V4L2_CID_MPEG_VIDEO_H263_MIN_QP (V4L2_CID_MPEG_BASE+303) +#define V4L2_CID_MPEG_VIDEO_H263_MAX_QP (V4L2_CID_MPEG_BASE+304) +#define V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP (V4L2_CID_MPEG_BASE+350) +#define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP (V4L2_CID_MPEG_BASE+351) +#define V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP (V4L2_CID_MPEG_BASE+352) +#define V4L2_CID_MPEG_VIDEO_H264_MIN_QP (V4L2_CID_MPEG_BASE+353) +#define V4L2_CID_MPEG_VIDEO_H264_MAX_QP (V4L2_CID_MPEG_BASE+354) +#define V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM (V4L2_CID_MPEG_BASE+355) +#define V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE (V4L2_CID_MPEG_BASE+356) +#define V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE (V4L2_CID_MPEG_BASE+357) +enum v4l2_mpeg_video_h264_entropy_mode { + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC = 0, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC = 1, +}; +#define V4L2_CID_MPEG_VIDEO_H264_I_PERIOD (V4L2_CID_MPEG_BASE+358) +#define V4L2_CID_MPEG_VIDEO_H264_LEVEL (V4L2_CID_MPEG_BASE+359) +enum v4l2_mpeg_video_h264_level { + V4L2_MPEG_VIDEO_H264_LEVEL_1_0 = 0, + V4L2_MPEG_VIDEO_H264_LEVEL_1B = 1, + V4L2_MPEG_VIDEO_H264_LEVEL_1_1 = 2, + V4L2_MPEG_VIDEO_H264_LEVEL_1_2 = 3, + V4L2_MPEG_VIDEO_H264_LEVEL_1_3 = 4, + V4L2_MPEG_VIDEO_H264_LEVEL_2_0 = 5, + V4L2_MPEG_VIDEO_H264_LEVEL_2_1 = 6, + V4L2_MPEG_VIDEO_H264_LEVEL_2_2 = 7, + V4L2_MPEG_VIDEO_H264_LEVEL_3_0 = 8, + V4L2_MPEG_VIDEO_H264_LEVEL_3_1 = 9, + V4L2_MPEG_VIDEO_H264_LEVEL_3_2 = 10, + V4L2_MPEG_VIDEO_H264_LEVEL_4_0 = 11, + V4L2_MPEG_VIDEO_H264_LEVEL_4_1 = 12, + V4L2_MPEG_VIDEO_H264_LEVEL_4_2 = 13, + V4L2_MPEG_VIDEO_H264_LEVEL_5_0 = 14, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1 = 15, +}; +#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA (V4L2_CID_MPEG_BASE+360) +#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA (V4L2_CID_MPEG_BASE+361) +#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE (V4L2_CID_MPEG_BASE+362) +enum v4l2_mpeg_video_h264_loop_filter_mode { + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED = 0, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED = 1, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY = 2, +}; +#define V4L2_CID_MPEG_VIDEO_H264_PROFILE (V4L2_CID_MPEG_BASE+363) +enum v4l2_mpeg_video_h264_profile { + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE = 0, + V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE = 1, + V4L2_MPEG_VIDEO_H264_PROFILE_MAIN = 2, + V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED = 3, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH = 4, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10 = 5, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422 = 6, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE = 7, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA = 8, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA = 9, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA = 10, + V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA = 11, + V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE = 12, + V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH = 13, + V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA = 14, + V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH = 15, + V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH = 16, +}; +#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT (V4L2_CID_MPEG_BASE+364) +#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH (V4L2_CID_MPEG_BASE+365) +#define V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE (V4L2_CID_MPEG_BASE+366) +#define V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC (V4L2_CID_MPEG_BASE+367) +enum v4l2_mpeg_video_h264_vui_sar_idc { + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED = 0, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1 = 1, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11 = 2, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11 = 3, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11 = 4, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33 = 5, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11 = 6, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11 = 7, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11 = 8, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33 = 9, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11 = 10, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11 = 11, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33 = 12, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99 = 13, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3 = 14, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2 = 15, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1 = 16, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED = 17, +}; +#define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_MPEG_BASE+400) +#define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_MPEG_BASE+401) +#define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_MPEG_BASE+402) +#define V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP (V4L2_CID_MPEG_BASE+403) +#define V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP (V4L2_CID_MPEG_BASE+404) +#define V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL (V4L2_CID_MPEG_BASE+405) +enum v4l2_mpeg_video_mpeg4_level { + V4L2_MPEG_VIDEO_MPEG4_LEVEL_0 = 0, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B = 1, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_1 = 2, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_2 = 3, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_3 = 4, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B = 5, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_4 = 6, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 = 7, +}; +#define V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE (V4L2_CID_MPEG_BASE+406) +enum v4l2_mpeg_video_mpeg4_profile { + V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE = 0, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE = 1, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE = 2, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE = 3, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY = 4, +}; +#define V4L2_CID_MPEG_VIDEO_MPEG4_QPEL (V4L2_CID_MPEG_BASE+407) /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) @@ -1371,6 +1511,33 @@ enum v4l2_mpeg_cx2341x_video_median_filter_type { #define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+10) #define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS (V4L2_CID_MPEG_CX2341X_BASE+11) +/* MPEG-class control IDs specific to the Samsung MFC 5.1 driver as defined by V4L2 */ +#define V4L2_CID_MPEG_MFC51_BASE (V4L2_CTRL_CLASS_MPEG | 0x1100) + +#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY (V4L2_CID_MPEG_MFC51_BASE+0) +#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE (V4L2_CID_MPEG_MFC51_BASE+1) +#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE (V4L2_CID_MPEG_MFC51_BASE+2) +enum v4l2_mpeg_mfc51_video_frame_skip_mode { + V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED = 0, + V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT = 1, + V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT = 2, +}; +#define V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE (V4L2_CID_MPEG_MFC51_BASE+3) +enum v4l2_mpeg_mfc51_video_force_frame_type { + V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED = 0, + V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_I_FRAME = 1, + V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED = 2, +}; +#define V4L2_CID_MPEG_MFC51_VIDEO_PADDING (V4L2_CID_MPEG_MFC51_BASE+4) +#define V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV (V4L2_CID_MPEG_MFC51_BASE+5) +#define V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT (V4L2_CID_MPEG_MFC51_BASE+6) +#define V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF (V4L2_CID_MPEG_MFC51_BASE+7) +#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY (V4L2_CID_MPEG_MFC51_BASE+50) +#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK (V4L2_CID_MPEG_MFC51_BASE+51) +#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH (V4L2_CID_MPEG_MFC51_BASE+52) +#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC (V4L2_CID_MPEG_MFC51_BASE+53) +#define V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P (V4L2_CID_MPEG_MFC51_BASE+54) + /* Camera class control IDs */ #define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900) #define V4L2_CID_CAMERA_CLASS (V4L2_CTRL_CLASS_CAMERA | 1) -- cgit v1.2.3 From f5f2cc646af13b0cf74b9d676408473123c9ea76 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Wed, 13 Jul 2011 18:09:48 -0300 Subject: [media] rc-core support for Microsoft IR keyboard/mouse This is a custom IR protocol decoder, for the RC-6-ish protocol used by the Microsoft Remote Keyboard, apparently developed internally at Microsoft, and officially dubbed MCIR-2, per their March 2011 remote and transceiver requirements and specifications document, which also touches on this IR keyboard/mouse device. Its a standard keyboard with embedded thumb stick mouse pointer and mouse buttons, along with a number of media keys. The media keys are standard RC-6, identical to the signals from the stock MCE remotes, and will be handled as such. The keyboard and mouse signals will be decoded and delivered to the system by an input device registered specifically by this driver. Successfully tested with multiple mceusb-driven transceivers, as well as with fintek-cir and redrat3 hardware. Essentially, any raw IR hardware with enough sampling resolution should be able to use this decoder, nothing about it is at all receiver-hardware-specific. This work is inspired by lirc_mod_mce: The documentation there and code aided in understanding and decoding the protocol, but the bulk of the code is actually borrowed more from the existing in-kernel decoders than anything. I did recycle the keyboard keycode table, a few defines, and some of the keyboard and mouse data parsing bits from lirc_mod_mce though. Special thanks to James Meyer for providing the hardware, and being patient with me as I took forever to get around to writing this. callback routine to ensure we don't get any stuck keys, and used symbolic names for the keytable. Also cc'ing Florian this time, who I believe is the original mod-mce author... CC: Florian Demski Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/Kconfig | 11 + drivers/media/rc/Makefile | 1 + drivers/media/rc/ir-mce_kbd-decoder.c | 448 ++++++++++++++++++++++++++++++++++ drivers/media/rc/ir-raw.c | 1 + drivers/media/rc/rc-core-priv.h | 18 ++ drivers/media/rc/rc-main.c | 1 + include/media/rc-map.h | 3 +- 7 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 drivers/media/rc/ir-mce_kbd-decoder.c (limited to 'include') diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 7d4bbc226d06..899f783d92fb 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -87,6 +87,17 @@ config IR_RC5_SZ_DECODER uses an IR protocol that is almost standard RC-5, but not quite, as it uses an additional bit). +config IR_MCE_KBD_DECODER + tristate "Enable IR raw decoder for the MCE keyboard/mouse protocol" + depends on RC_CORE + select BITREVERSE + default y + + ---help--- + Enable this option if you have a Microsoft Remote Keyboard for + Windows Media Center Edition, which you would like to use with + a raw IR receiver in your system. + config IR_LIRC_CODEC tristate "Enable IR to LIRC bridge" depends on RC_CORE diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 52830e5f4eaa..f224db027c41 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o obj-$(CONFIG_IR_RC5_SZ_DECODER) += ir-rc5-sz-decoder.o +obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o # stand-alone IR receivers/transmitters diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c new file mode 100644 index 000000000000..fe96e541839c --- /dev/null +++ b/drivers/media/rc/ir-mce_kbd-decoder.c @@ -0,0 +1,448 @@ +/* ir-mce_kbd-decoder.c - A decoder for the RC6-ish keyboard/mouse IR protocol + * used by the Microsoft Remote Keyboard for Windows Media Center Edition, + * referred to by Microsoft's Windows Media Center remote specification docs + * as "an internal protocol called MCIR-2". + * + * Copyright (C) 2011 by Jarod Wilson + * + * 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 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "rc-core-priv.h" + +/* + * This decoder currently supports: + * - MCIR-2 29-bit IR signals used for mouse movement and buttons + * - MCIR-2 32-bit IR signals used for standard keyboard keys + * + * The media keys on the keyboard send RC-6 signals that are inditinguishable + * from the keys of the same name on the stock MCE remote, and will be handled + * by the standard RC-6 decoder, and be made available to the system via the + * input device for the remote, rather than the keyboard/mouse one. + */ + +#define MCIR2_UNIT 333333 /* ns */ +#define MCIR2_HEADER_NBITS 5 +#define MCIR2_MOUSE_NBITS 29 +#define MCIR2_KEYBOARD_NBITS 32 +#define MCIR2_PREFIX_PULSE (8 * MCIR2_UNIT) +#define MCIR2_PREFIX_SPACE (1 * MCIR2_UNIT) +#define MCIR2_MAX_LEN (3 * MCIR2_UNIT) +#define MCIR2_BIT_START (1 * MCIR2_UNIT) +#define MCIR2_BIT_END (1 * MCIR2_UNIT) +#define MCIR2_BIT_0 (1 * MCIR2_UNIT) +#define MCIR2_BIT_SET (2 * MCIR2_UNIT) +#define MCIR2_MODE_MASK 0xf /* for the header bits */ +#define MCIR2_KEYBOARD_HEADER 0x4 +#define MCIR2_MOUSE_HEADER 0x1 +#define MCIR2_MASK_KEYS_START 0xe0 + +enum mce_kbd_mode { + MCIR2_MODE_KEYBOARD, + MCIR2_MODE_MOUSE, + MCIR2_MODE_UNKNOWN, +}; + +enum mce_kbd_state { + STATE_INACTIVE, + STATE_HEADER_BIT_START, + STATE_HEADER_BIT_END, + STATE_BODY_BIT_START, + STATE_BODY_BIT_END, + STATE_FINISHED, +}; + +static unsigned char kbd_keycodes[256] = { + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_A, + KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, + KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, + KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, + KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, + KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, + KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, + KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, + KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, KEY_SPACE, + KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, + KEY_RESERVED, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, + KEY_DOT, KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2, + KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, + KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, + KEY_SYSRQ, KEY_SCROLLLOCK, KEY_PAUSE, KEY_INSERT, KEY_HOME, + KEY_PAGEUP, KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT, + KEY_LEFT, KEY_DOWN, KEY_UP, KEY_NUMLOCK, KEY_KPSLASH, + KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS, KEY_KPENTER, KEY_KP1, + KEY_KP2, KEY_KP3, KEY_KP4, KEY_KP5, KEY_KP6, + KEY_KP7, KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT, + KEY_102ND, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, KEY_F13, + KEY_F14, KEY_F15, KEY_F16, KEY_F17, KEY_F18, + KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, + KEY_F24, KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT, + KEY_STOP, KEY_AGAIN, KEY_UNDO, KEY_CUT, KEY_COPY, + KEY_PASTE, KEY_FIND, KEY_MUTE, KEY_VOLUMEUP, KEY_VOLUMEDOWN, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_KPCOMMA, KEY_RESERVED, + KEY_RO, KEY_KATAKANAHIRAGANA, KEY_YEN, KEY_HENKAN, KEY_MUHENKAN, + KEY_KPJPCOMMA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_HANGUEL, + KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA, KEY_ZENKAKUHANKAKU, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTCTRL, + KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTMETA, KEY_RIGHTCTRL, KEY_RIGHTSHIFT, + KEY_RIGHTALT, KEY_RIGHTMETA, KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG, + KEY_NEXTSONG, KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, + KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, KEY_FIND, + KEY_SCROLLUP, KEY_SCROLLDOWN, KEY_EDIT, KEY_SLEEP, KEY_COFFEE, + KEY_REFRESH, KEY_CALC, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED +}; + +static void mce_kbd_rx_timeout(unsigned long data) +{ + struct mce_kbd_dec *mce_kbd = (struct mce_kbd_dec *)data; + int i; + unsigned char maskcode; + + IR_dprintk(2, "timer callback clearing all keys\n"); + + for (i = 0; i < 7; i++) { + maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i]; + input_report_key(mce_kbd->idev, maskcode, 0); + } + + for (i = 0; i < MCIR2_MASK_KEYS_START; i++) + input_report_key(mce_kbd->idev, kbd_keycodes[i], 0); +} + +static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data) +{ + switch (data->header & MCIR2_MODE_MASK) { + case MCIR2_KEYBOARD_HEADER: + return MCIR2_MODE_KEYBOARD; + case MCIR2_MOUSE_HEADER: + return MCIR2_MODE_MOUSE; + default: + return MCIR2_MODE_UNKNOWN; + } +} + +static void ir_mce_kbd_process_keyboard_data(struct input_dev *idev, + u32 scancode) +{ + u8 keydata = (scancode >> 8) & 0xff; + u8 shiftmask = scancode & 0xff; + unsigned char keycode, maskcode; + int i, keystate; + + IR_dprintk(1, "keyboard: keydata = 0x%02x, shiftmask = 0x%02x\n", + keydata, shiftmask); + + for (i = 0; i < 7; i++) { + maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i]; + if (shiftmask & (1 << i)) + keystate = 1; + else + keystate = 0; + input_report_key(idev, maskcode, keystate); + } + + if (keydata) { + keycode = kbd_keycodes[keydata]; + input_report_key(idev, keycode, 1); + } else { + for (i = 0; i < MCIR2_MASK_KEYS_START; i++) + input_report_key(idev, kbd_keycodes[i], 0); + } +} + +static void ir_mce_kbd_process_mouse_data(struct input_dev *idev, u32 scancode) +{ + /* raw mouse coordinates */ + u8 xdata = (scancode >> 7) & 0x7f; + u8 ydata = (scancode >> 14) & 0x7f; + int x, y; + /* mouse buttons */ + bool right = scancode & 0x40; + bool left = scancode & 0x20; + + if (xdata & 0x40) + x = -((~xdata & 0x7f) + 1); + else + x = xdata; + + if (ydata & 0x40) + y = -((~ydata & 0x7f) + 1); + else + y = ydata; + + IR_dprintk(1, "mouse: x = %d, y = %d, btns = %s%s\n", + x, y, left ? "L" : "", right ? "R" : ""); + + input_report_rel(idev, REL_X, x); + input_report_rel(idev, REL_Y, y); + + input_report_key(idev, BTN_LEFT, left); + input_report_key(idev, BTN_RIGHT, right); +} + +/** + * ir_mce_kbd_decode() - Decode one mce_kbd pulse or space + * @dev: the struct rc_dev descriptor of the device + * @ev: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev) +{ + struct mce_kbd_dec *data = &dev->raw->mce_kbd; + u32 scancode; + unsigned long delay; + + if (!(dev->raw->enabled_protocols & RC_TYPE_MCE_KBD)) + return 0; + + if (!is_timing_event(ev)) { + if (ev.reset) + data->state = STATE_INACTIVE; + return 0; + } + + if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2)) + goto out; + +again: + IR_dprintk(2, "started at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + + if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2)) + return 0; + + switch (data->state) { + + case STATE_INACTIVE: + if (!ev.pulse) + break; + + /* Note: larger margin on first pulse since each MCIR2_UNIT + is quite short and some hardware takes some time to + adjust to the signal */ + if (!eq_margin(ev.duration, MCIR2_PREFIX_PULSE, MCIR2_UNIT)) + break; + + data->state = STATE_HEADER_BIT_START; + data->count = 0; + data->header = 0; + return 0; + + case STATE_HEADER_BIT_START: + if (geq_margin(ev.duration, MCIR2_MAX_LEN, MCIR2_UNIT / 2)) + break; + + data->header <<= 1; + if (ev.pulse) + data->header |= 1; + data->count++; + data->state = STATE_HEADER_BIT_END; + return 0; + + case STATE_HEADER_BIT_END: + if (!is_transition(&ev, &dev->raw->prev_ev)) + break; + + decrease_duration(&ev, MCIR2_BIT_END); + + if (data->count != MCIR2_HEADER_NBITS) { + data->state = STATE_HEADER_BIT_START; + goto again; + } + + switch (mce_kbd_mode(data)) { + case MCIR2_MODE_KEYBOARD: + data->wanted_bits = MCIR2_KEYBOARD_NBITS; + break; + case MCIR2_MODE_MOUSE: + data->wanted_bits = MCIR2_MOUSE_NBITS; + break; + default: + IR_dprintk(1, "not keyboard or mouse data\n"); + goto out; + } + + data->count = 0; + data->body = 0; + data->state = STATE_BODY_BIT_START; + goto again; + + case STATE_BODY_BIT_START: + if (geq_margin(ev.duration, MCIR2_MAX_LEN, MCIR2_UNIT / 2)) + break; + + data->body <<= 1; + if (ev.pulse) + data->body |= 1; + data->count++; + data->state = STATE_BODY_BIT_END; + return 0; + + case STATE_BODY_BIT_END: + if (!is_transition(&ev, &dev->raw->prev_ev)) + break; + + if (data->count == data->wanted_bits) + data->state = STATE_FINISHED; + else + data->state = STATE_BODY_BIT_START; + + decrease_duration(&ev, MCIR2_BIT_END); + goto again; + + case STATE_FINISHED: + if (ev.pulse) + break; + + switch (data->wanted_bits) { + case MCIR2_KEYBOARD_NBITS: + scancode = data->body & 0xffff; + IR_dprintk(1, "keyboard data 0x%08x\n", data->body); + if (dev->timeout) + delay = usecs_to_jiffies(dev->timeout / 1000); + else + delay = msecs_to_jiffies(100); + mod_timer(&data->rx_timeout, jiffies + delay); + /* Pass data to keyboard buffer parser */ + ir_mce_kbd_process_keyboard_data(data->idev, scancode); + break; + case MCIR2_MOUSE_NBITS: + scancode = data->body & 0x1fffff; + IR_dprintk(1, "mouse data 0x%06x\n", scancode); + /* Pass data to mouse buffer parser */ + ir_mce_kbd_process_mouse_data(data->idev, scancode); + break; + default: + IR_dprintk(1, "not keyboard or mouse data\n"); + goto out; + } + + data->state = STATE_INACTIVE; + input_sync(data->idev); + return 0; + } + +out: + IR_dprintk(1, "failed at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state = STATE_INACTIVE; + input_sync(data->idev); + return -EINVAL; +} + +static int ir_mce_kbd_register(struct rc_dev *dev) +{ + struct mce_kbd_dec *mce_kbd = &dev->raw->mce_kbd; + struct input_dev *idev; + int i, ret; + + idev = input_allocate_device(); + if (!idev) + return -ENOMEM; + + snprintf(mce_kbd->name, sizeof(mce_kbd->name), + "MCE IR Keyboard/Mouse (%s)", dev->driver_name); + strlcat(mce_kbd->phys, "/input0", sizeof(mce_kbd->phys)); + + idev->name = mce_kbd->name; + idev->phys = mce_kbd->phys; + + /* Keyboard bits */ + set_bit(EV_KEY, idev->evbit); + set_bit(EV_REP, idev->evbit); + for (i = 0; i < sizeof(kbd_keycodes); i++) + set_bit(kbd_keycodes[i], idev->keybit); + + /* Mouse bits */ + set_bit(EV_REL, idev->evbit); + set_bit(REL_X, idev->relbit); + set_bit(REL_Y, idev->relbit); + set_bit(BTN_LEFT, idev->keybit); + set_bit(BTN_RIGHT, idev->keybit); + + /* Report scancodes too */ + set_bit(EV_MSC, idev->evbit); + set_bit(MSC_SCAN, idev->mscbit); + + setup_timer(&mce_kbd->rx_timeout, mce_kbd_rx_timeout, + (unsigned long)mce_kbd); + + input_set_drvdata(idev, mce_kbd); + +#if 0 + /* Adding this reference means two input devices are associated with + * this rc-core device, which ir-keytable doesn't cope with yet */ + idev->dev.parent = &dev->dev; +#endif + + ret = input_register_device(idev); + if (ret < 0) { + input_free_device(idev); + return -EIO; + } + + mce_kbd->idev = idev; + + return 0; +} + +static int ir_mce_kbd_unregister(struct rc_dev *dev) +{ + struct mce_kbd_dec *mce_kbd = &dev->raw->mce_kbd; + struct input_dev *idev = mce_kbd->idev; + + del_timer_sync(&mce_kbd->rx_timeout); + input_unregister_device(idev); + + return 0; +} + +static struct ir_raw_handler mce_kbd_handler = { + .protocols = RC_TYPE_MCE_KBD, + .decode = ir_mce_kbd_decode, + .raw_register = ir_mce_kbd_register, + .raw_unregister = ir_mce_kbd_unregister, +}; + +static int __init ir_mce_kbd_decode_init(void) +{ + ir_raw_handler_register(&mce_kbd_handler); + + printk(KERN_INFO "IR MCE Keyboard/mouse protocol handler initialized\n"); + return 0; +} + +static void __exit ir_mce_kbd_decode_exit(void) +{ + ir_raw_handler_unregister(&mce_kbd_handler); +} + +module_init(ir_mce_kbd_decode_init); +module_exit(ir_mce_kbd_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jarod Wilson "); +MODULE_DESCRIPTION("MCE Keyboard/mouse IR protocol decoder"); diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index 423ed45d6c55..27808bb59eba 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -355,6 +355,7 @@ static void init_decoders(struct work_struct *work) load_rc6_decode(); load_jvc_decode(); load_sony_decode(); + load_mce_kbd_decode(); load_lirc_codec(); /* If needed, we may later add some init code. In this case, diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 873b38789751..04c2c722b6ec 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -84,6 +84,17 @@ struct ir_raw_event_ctrl { unsigned count; unsigned wanted_bits; } rc5_sz; + struct mce_kbd_dec { + struct input_dev *idev; + struct timer_list rx_timeout; + char name[64]; + char phys[64]; + int state; + u8 header; + u32 body; + unsigned count; + unsigned wanted_bits; + } mce_kbd; struct lirc_codec { struct rc_dev *dev; struct lirc_driver *drv; @@ -182,6 +193,13 @@ void ir_raw_init(void); #define load_sony_decode() 0 #endif +/* from ir-mce_kbd-decoder.c */ +#ifdef CONFIG_IR_MCE_KBD_DECODER_MODULE +#define load_mce_kbd_decode() request_module("ir-mce_kbd-decoder") +#else +#define load_mce_kbd_decode() 0 +#endif + /* from ir-lirc-codec.c */ #ifdef CONFIG_IR_LIRC_CODEC_MODULE #define load_lirc_codec() request_module("ir-lirc-codec") diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 30634ab5c226..51a23f48bc7d 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -735,6 +735,7 @@ static struct { { RC_TYPE_JVC, "jvc" }, { RC_TYPE_SONY, "sony" }, { RC_TYPE_RC5_SZ, "rc-5-sz" }, + { RC_TYPE_MCE_KBD, "mce_kbd" }, { RC_TYPE_LIRC, "lirc" }, { RC_TYPE_OTHER, "other" }, }; diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 4e1409ec2613..17c9759ae77b 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -18,12 +18,13 @@ #define RC_TYPE_JVC (1 << 3) /* JVC protocol */ #define RC_TYPE_SONY (1 << 4) /* Sony12/15/20 protocol */ #define RC_TYPE_RC5_SZ (1 << 5) /* RC5 variant used by Streamzap */ +#define RC_TYPE_MCE_KBD (1 << 29) /* RC6-ish MCE keyboard/mouse */ #define RC_TYPE_LIRC (1 << 30) /* Pass raw IR to lirc userspace */ #define RC_TYPE_OTHER (1u << 31) #define RC_TYPE_ALL (RC_TYPE_RC5 | RC_TYPE_NEC | RC_TYPE_RC6 | \ RC_TYPE_JVC | RC_TYPE_SONY | RC_TYPE_LIRC | \ - RC_TYPE_RC5_SZ | RC_TYPE_OTHER) + RC_TYPE_RC5_SZ | RC_TYPE_MCE_KBD | RC_TYPE_OTHER) struct rc_map_table { u32 scancode; -- cgit v1.2.3 From 839cf81346b5145f0959b28652612eabaed48d47 Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Wed, 2 Mar 2011 13:16:37 -0300 Subject: [media] v4l: add g_tvnorms_output callback to V4L2 subdev Callback is used to acquire TV norms supported by a subdev. It is used to avoid having standards in top-level driver. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index b05579b4adb7..44a5ddbc9e0f 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -229,6 +229,9 @@ struct v4l2_subdev_audio_ops { s_std_output: set v4l2_std_id for video OUTPUT devices. This is ignored by video input devices. + g_tvnorms_output: get v4l2_std_id with all standards supported by video + OUTPUT device. This is ignored by video input devices. + s_crystal_freq: sets the frequency of the crystal used to generate the clocks in Hz. An extra flags field allows device specific configuration regarding clock frequency dividers, etc. If not used, then set flags @@ -265,6 +268,7 @@ struct v4l2_subdev_video_ops { int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags); int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std); int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std); + int (*g_tvnorms_output)(struct v4l2_subdev *sd, v4l2_std_id *std); int (*g_input_status)(struct v4l2_subdev *sd, u32 *status); int (*s_stream)(struct v4l2_subdev *sd, int enable); int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cc); -- cgit v1.2.3 From 8b32dd0d3f9221719507700fa396d280143b4da1 Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Wed, 29 Jun 2011 07:01:28 -0300 Subject: [media] v4l: add g_dv_preset callback to V4L2 subdev Callback is used to acquire current digital video preset from a subdev. It is used to avoid keeping dv preset in top-level driver. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 44a5ddbc9e0f..3bc7f928c61a 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -246,6 +246,8 @@ struct v4l2_subdev_audio_ops { s_dv_preset: set dv (Digital Video) preset in the sub device. Similar to s_std() + g_dv_preset: get current dv (Digital Video) preset in the sub device. + query_dv_preset: query dv preset in the sub device. This is similar to querystd() @@ -286,6 +288,8 @@ struct v4l2_subdev_video_ops { struct v4l2_dv_enum_preset *preset); int (*s_dv_preset)(struct v4l2_subdev *sd, struct v4l2_dv_preset *preset); + int (*g_dv_preset)(struct v4l2_subdev *sd, + struct v4l2_dv_preset *preset); int (*query_dv_preset)(struct v4l2_subdev *sd, struct v4l2_dv_preset *preset); int (*s_dv_timings)(struct v4l2_subdev *sd, -- cgit v1.2.3 From 5930ab2ba089e05c8de594c36836724f8df7725e Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Wed, 29 Jun 2011 07:16:15 -0300 Subject: [media] v4l: add g_std_output callback to V4L2 subdev Callback is used to acquire current analog TV standard from a subdev. It is used to avoid keeping current standard in top-level driver. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 3bc7f928c61a..9ff0ab9069d7 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -229,6 +229,9 @@ struct v4l2_subdev_audio_ops { s_std_output: set v4l2_std_id for video OUTPUT devices. This is ignored by video input devices. + g_std_output: get current standard for video OUTPUT devices. This is ignored + by video input devices. + g_tvnorms_output: get v4l2_std_id with all standards supported by video OUTPUT device. This is ignored by video input devices. @@ -269,6 +272,7 @@ struct v4l2_subdev_video_ops { int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config); int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags); int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std); + int (*g_std_output)(struct v4l2_subdev *sd, v4l2_std_id *std); int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std); int (*g_tvnorms_output)(struct v4l2_subdev *sd, v4l2_std_id *std); int (*g_input_status)(struct v4l2_subdev *sd, u32 *status); -- cgit v1.2.3 From 033d7463f816315308ffec6f69576d820bfbab0c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 29 Jun 2011 07:43:48 -0300 Subject: [media] V4L: soc-camera: remove now unused soc-camera specific PM hooks soc-camera host drivers shall be implementing their PM, using standard kernel methods, soc-camera specific hooks can die. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera.c | 26 -------------------------- include/media/soc_camera.h | 2 -- 2 files changed, 28 deletions(-) (limited to 'include') diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 136326e8c4b0..5084e72d4965 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -1209,36 +1209,10 @@ static int soc_camera_remove(struct device *dev) return 0; } -static int soc_camera_suspend(struct device *dev, pm_message_t state) -{ - struct soc_camera_device *icd = to_soc_camera_dev(dev); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - int ret = 0; - - if (ici->ops->suspend) - ret = ici->ops->suspend(icd, state); - - return ret; -} - -static int soc_camera_resume(struct device *dev) -{ - struct soc_camera_device *icd = to_soc_camera_dev(dev); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - int ret = 0; - - if (ici->ops->resume) - ret = ici->ops->resume(icd); - - return ret; -} - struct bus_type soc_camera_bus_type = { .name = "soc-camera", .probe = soc_camera_probe, .remove = soc_camera_remove, - .suspend = soc_camera_suspend, - .resume = soc_camera_resume, }; EXPORT_SYMBOL_GPL(soc_camera_bus_type); diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 21dd8a41f4c1..70c4ea5ecc31 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -66,8 +66,6 @@ struct soc_camera_host_ops { struct module *owner; int (*add)(struct soc_camera_device *); void (*remove)(struct soc_camera_device *); - int (*suspend)(struct soc_camera_device *, pm_message_t); - int (*resume)(struct soc_camera_device *); /* * .get_formats() is called for each client device format, but * .put_formats() is only called once. Further, if any of the calls to -- cgit v1.2.3 From 91c7953005247c9563408bbdbf35e27de33e6ec3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 1 Jul 2011 14:31:17 -0300 Subject: [media] V4L: add media bus configuration subdev operations Add media bus configuration types and two subdev operations to get supported mediabus configurations and to set a specific configuration. Subdevs can support several configurations, e.g., they can send video data on 1 or several lanes, can be configured to use a specific CSI-2 channel, in such cases subdevice drivers return bitmasks with all respective bits set. When a set-configuration operation is called, it has to specify a non-ambiguous configuration. Signed-off-by: Stanimir Varbanov Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-mediabus.h | 63 +++++++++++++++++++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 10 +++++++ 2 files changed, 73 insertions(+) (limited to 'include') diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h index 971c7fa29614..6114007c8c74 100644 --- a/include/media/v4l2-mediabus.h +++ b/include/media/v4l2-mediabus.h @@ -13,6 +13,69 @@ #include +/* Parallel flags */ +/* + * Can the client run in master or in slave mode. By "Master mode" an operation + * mode is meant, when the client (e.g., a camera sensor) is producing + * horizontal and vertical synchronisation. In "Slave mode" the host is + * providing these signals to the slave. + */ +#define V4L2_MBUS_MASTER (1 << 0) +#define V4L2_MBUS_SLAVE (1 << 1) +/* Which signal polarities it supports */ +/* Note: in BT.656 mode HSYNC and VSYNC are unused */ +#define V4L2_MBUS_HSYNC_ACTIVE_HIGH (1 << 2) +#define V4L2_MBUS_HSYNC_ACTIVE_LOW (1 << 3) +#define V4L2_MBUS_VSYNC_ACTIVE_HIGH (1 << 4) +#define V4L2_MBUS_VSYNC_ACTIVE_LOW (1 << 5) +#define V4L2_MBUS_PCLK_SAMPLE_RISING (1 << 6) +#define V4L2_MBUS_PCLK_SAMPLE_FALLING (1 << 7) +#define V4L2_MBUS_DATA_ACTIVE_HIGH (1 << 8) +#define V4L2_MBUS_DATA_ACTIVE_LOW (1 << 9) + +/* Serial flags */ +/* How many lanes the client can use */ +#define V4L2_MBUS_CSI2_1_LANE (1 << 0) +#define V4L2_MBUS_CSI2_2_LANE (1 << 1) +#define V4L2_MBUS_CSI2_3_LANE (1 << 2) +#define V4L2_MBUS_CSI2_4_LANE (1 << 3) +/* On which channels it can send video data */ +#define V4L2_MBUS_CSI2_CHANNEL_0 (1 << 4) +#define V4L2_MBUS_CSI2_CHANNEL_1 (1 << 5) +#define V4L2_MBUS_CSI2_CHANNEL_2 (1 << 6) +#define V4L2_MBUS_CSI2_CHANNEL_3 (1 << 7) +/* Does it support only continuous or also non-continuous clock mode */ +#define V4L2_MBUS_CSI2_CONTINUOUS_CLOCK (1 << 8) +#define V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK (1 << 9) + +#define V4L2_MBUS_CSI2_LANES (V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_2_LANE | \ + V4L2_MBUS_CSI2_3_LANE | V4L2_MBUS_CSI2_4_LANE) +#define V4L2_MBUS_CSI2_CHANNELS (V4L2_MBUS_CSI2_CHANNEL_0 | V4L2_MBUS_CSI2_CHANNEL_1 | \ + V4L2_MBUS_CSI2_CHANNEL_2 | V4L2_MBUS_CSI2_CHANNEL_3) + +/** + * v4l2_mbus_type - media bus type + * @V4L2_MBUS_PARALLEL: parallel interface with hsync and vsync + * @V4L2_MBUS_BT656: parallel interface with embedded synchronisation, can + * also be used for BT.1120 + * @V4L2_MBUS_CSI2: MIPI CSI-2 serial interface + */ +enum v4l2_mbus_type { + V4L2_MBUS_PARALLEL, + V4L2_MBUS_BT656, + V4L2_MBUS_CSI2, +}; + +/** + * v4l2_mbus_config - media bus configuration + * @type: in: interface type + * @flags: in / out: configuration flags, depending on @type + */ +struct v4l2_mbus_config { + enum v4l2_mbus_type type; + unsigned int flags; +}; + static inline void v4l2_fill_pix_format(struct v4l2_pix_format *pix_fmt, const struct v4l2_mbus_framefmt *mbus_fmt) { diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 9ff0ab9069d7..257da1a30f66 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -267,6 +267,12 @@ struct v4l2_subdev_audio_ops { try_mbus_fmt: try to set a pixel format on a video data source s_mbus_fmt: set a pixel format on a video data source + + g_mbus_config: get supported mediabus configurations + + s_mbus_config: set a certain mediabus configuration. This operation is added + for compatibility with soc-camera drivers and should not be used by new + software. */ struct v4l2_subdev_video_ops { int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config); @@ -310,6 +316,10 @@ struct v4l2_subdev_video_ops { struct v4l2_mbus_framefmt *fmt); int (*s_mbus_fmt)(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt); + int (*g_mbus_config)(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg); + int (*s_mbus_config)(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg); }; /* -- cgit v1.2.3 From 6b526fed0e688e3926bd43a09f2f36cba3983ce2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 1 Jul 2011 11:19:58 -0300 Subject: [media] V4L: sh_mobile_csi2: switch away from using the soc-camera bus notifier This moves us one more step closer to eliminating the soc-camera bus and devices on it. Besides, as a side effect, CSI-2 runtime PM on sh-mobile secomes finer grained now: we only have to power on the interface, when the device nodes are open. Signed-off-by: Guennadi Liakhovetski Acked-by: Paul Mundt Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-shmobile/board-ap4evb.c | 12 +-- drivers/media/video/sh_mobile_ceu_camera.c | 117 +++++++++++++++++++------ drivers/media/video/sh_mobile_csi2.c | 135 ++++++++++++++++------------- include/media/sh_mobile_ceu.h | 10 ++- include/media/sh_mobile_csi2.h | 8 +- 5 files changed, 180 insertions(+), 102 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c index 803bc6edfca4..67b3d0b752b7 100644 --- a/arch/arm/mach-shmobile/board-ap4evb.c +++ b/arch/arm/mach-shmobile/board-ap4evb.c @@ -957,19 +957,16 @@ static struct resource csi2_resources[] = { }, }; -static struct platform_device csi2_device = { - .name = "sh-mobile-csi2", - .id = 0, +static struct sh_mobile_ceu_companion csi2 = { + .id = 0, .num_resources = ARRAY_SIZE(csi2_resources), .resource = csi2_resources, - .dev = { - .platform_data = &csi2_info, - }, + .platform_data = &csi2_info, }; static struct sh_mobile_ceu_info sh_mobile_ceu_info = { .flags = SH_CEU_FLAG_USE_8BIT_BUS, - .csi2_dev = &csi2_device.dev, + .csi2 = &csi2, }; static struct resource ceu_resources[] = { @@ -1013,7 +1010,6 @@ static struct platform_device *ap4evb_devices[] __initdata = { &lcdc1_device, &lcdc_device, &hdmi_device, - &csi2_device, &ceu_device, &ap4evb_camera, &meram_device, diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 33e126e43b1f..152a65993286 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,7 @@ struct sh_mobile_ceu_buffer { struct sh_mobile_ceu_dev { struct soc_camera_host ici; struct soc_camera_device *icd; + struct platform_device *csi2_pdev; unsigned int irq; void __iomem *base; @@ -498,11 +500,26 @@ out: return IRQ_HANDLED; } +static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev) +{ + struct v4l2_subdev *sd; + + if (!pcdev->csi2_pdev) + return NULL; + + v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev) + if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd)) + return sd; + + return NULL; +} + /* Called with .video_lock held */ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; + struct v4l2_subdev *csi2_sd; int ret; if (pcdev->icd) @@ -515,8 +532,16 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) pm_runtime_get_sync(ici->v4l2_dev.dev); ret = sh_mobile_ceu_soft_reset(pcdev); - if (!ret) + + csi2_sd = find_csi2(pcdev); + + ret = v4l2_subdev_call(csi2_sd, core, s_power, 1); + if (ret != -ENODEV && ret != -ENOIOCTLCMD && ret < 0) { + pm_runtime_put_sync(ici->v4l2_dev.dev); + } else { pcdev->icd = icd; + ret = 0; + } return ret; } @@ -526,9 +551,11 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; + struct v4l2_subdev *csi2_sd = find_csi2(pcdev); BUG_ON(icd != pcdev->icd); + v4l2_subdev_call(csi2_sd, core, s_power, 0); /* disable capture, disable interrupts */ ceu_write(pcdev, CEIER, 0); sh_mobile_ceu_soft_reset(pcdev); @@ -640,7 +667,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) } /* CSI2 special configuration */ - if (pcdev->pdata->csi2_dev) { + if (pcdev->pdata->csi2) { in_width = ((in_width - 2) * 2); left_offset *= 2; } @@ -782,7 +809,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, value |= pcdev->is_16bit ? 1 << 12 : 0; /* CSI2 mode */ - if (pcdev->pdata->csi2_dev) + if (pcdev->pdata->csi2) value |= 3 << 12; ceu_write(pcdev, CAMCR, value); @@ -920,7 +947,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int return 0; } - if (!pcdev->pdata->csi2_dev) { + if (!pcdev->pdata->csi2) { ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); if (ret < 0) return 0; @@ -1943,7 +1970,7 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion), .notifier.notifier_call = bus_notify, }; - struct device *csi2; + struct sh_mobile_ceu_companion *csi2; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); @@ -2016,26 +2043,61 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->ici.drv_name = dev_name(&pdev->dev); pcdev->ici.ops = &sh_mobile_ceu_host_ops; + pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(pcdev->alloc_ctx)) { + err = PTR_ERR(pcdev->alloc_ctx); + goto exit_free_clk; + } + + err = soc_camera_host_register(&pcdev->ici); + if (err) + goto exit_free_ctx; + /* CSI2 interfacing */ - csi2 = pcdev->pdata->csi2_dev; + csi2 = pcdev->pdata->csi2; if (csi2) { - wait.dev = csi2; + struct platform_device *csi2_pdev = + platform_device_alloc("sh-mobile-csi2", csi2->id); + struct sh_csi2_pdata *csi2_pdata = csi2->platform_data; + + if (!csi2_pdev) { + err = -ENOMEM; + goto exit_host_unregister; + } + + pcdev->csi2_pdev = csi2_pdev; + + err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata)); + if (err < 0) + goto exit_pdev_put; + + csi2_pdata = csi2_pdev->dev.platform_data; + csi2_pdata->v4l2_dev = &pcdev->ici.v4l2_dev; + + csi2_pdev->resource = csi2->resource; + csi2_pdev->num_resources = csi2->num_resources; + + err = platform_device_add(csi2_pdev); + if (err < 0) + goto exit_pdev_put; + + wait.dev = &csi2_pdev->dev; err = bus_register_notifier(&platform_bus_type, &wait.notifier); if (err < 0) - goto exit_free_clk; + goto exit_pdev_unregister; /* * From this point the driver module will not unload, until * we complete the completion. */ - if (!csi2->driver) { + if (!csi2_pdev->dev.driver) { complete(&wait.completion); /* Either too late, or probing failed */ bus_unregister_notifier(&platform_bus_type, &wait.notifier); err = -ENXIO; - goto exit_free_clk; + goto exit_pdev_unregister; } /* @@ -2044,34 +2106,28 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) * the "owner" is safe! */ - err = try_module_get(csi2->driver->owner); + err = try_module_get(csi2_pdev->dev.driver->owner); /* Let notifier complete, if it has been locked */ complete(&wait.completion); bus_unregister_notifier(&platform_bus_type, &wait.notifier); if (!err) { err = -ENODEV; - goto exit_free_clk; + goto exit_pdev_unregister; } } - pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(pcdev->alloc_ctx)) { - err = PTR_ERR(pcdev->alloc_ctx); - goto exit_module_put; - } - - err = soc_camera_host_register(&pcdev->ici); - if (err) - goto exit_free_ctx; - return 0; +exit_pdev_unregister: + platform_device_del(pcdev->csi2_pdev); +exit_pdev_put: + pcdev->csi2_pdev->resource = NULL; + platform_device_put(pcdev->csi2_pdev); +exit_host_unregister: + soc_camera_host_unregister(&pcdev->ici); exit_free_ctx: vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); -exit_module_put: - if (csi2 && csi2->driver) - module_put(csi2->driver->owner); exit_free_clk: pm_runtime_disable(&pdev->dev); free_irq(pcdev->irq, pcdev); @@ -2091,7 +2147,7 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); struct sh_mobile_ceu_dev *pcdev = container_of(soc_host, struct sh_mobile_ceu_dev, ici); - struct device *csi2 = pcdev->pdata->csi2_dev; + struct platform_device *csi2_pdev = pcdev->csi2_pdev; soc_camera_host_unregister(soc_host); pm_runtime_disable(&pdev->dev); @@ -2100,8 +2156,13 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) dma_release_declared_memory(&pdev->dev); iounmap(pcdev->base); vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); - if (csi2 && csi2->driver) - module_put(csi2->driver->owner); + if (csi2_pdev && csi2_pdev->dev.driver) { + struct module *csi2_drv = csi2_pdev->dev.driver->owner; + platform_device_del(csi2_pdev); + csi2_pdev->resource = NULL; + platform_device_put(csi2_pdev); + module_put(csi2_drv); + } kfree(pcdev); return 0; diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/video/sh_mobile_csi2.c index 98b87481fa94..2893a0134c7e 100644 --- a/drivers/media/video/sh_mobile_csi2.c +++ b/drivers/media/video/sh_mobile_csi2.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -33,7 +34,6 @@ struct sh_csi2 { struct v4l2_subdev subdev; struct list_head list; - struct notifier_block notifier; unsigned int irq; void __iomem *base; struct platform_device *pdev; @@ -132,13 +132,6 @@ static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = { .try_mbus_fmt = sh_csi2_try_fmt, }; -static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops; - -static struct v4l2_subdev_ops sh_csi2_subdev_ops = { - .core = &sh_csi2_subdev_core_ops, - .video = &sh_csi2_subdev_video_ops, -}; - static void sh_csi2_hwinit(struct sh_csi2 *priv) { struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; @@ -186,65 +179,84 @@ static unsigned long sh_csi2_query_bus_param(struct soc_camera_device *icd) return soc_camera_apply_sensor_flags(icl, flags); } -static int sh_csi2_notify(struct notifier_block *nb, - unsigned long action, void *data) +static int sh_csi2_client_connect(struct sh_csi2 *priv) { - struct device *dev = data; - struct soc_camera_device *icd = to_soc_camera_dev(dev); - struct v4l2_device *v4l2_dev = dev_get_drvdata(dev->parent); - struct sh_csi2 *priv = - container_of(nb, struct sh_csi2, notifier); struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; - int ret, i; + struct v4l2_subdev *sd, *csi2_sd = &priv->subdev; + struct soc_camera_device *icd = NULL; + struct device *dev = v4l2_get_subdevdata(&priv->subdev); + int i; + + v4l2_device_for_each_subdev(sd, csi2_sd->v4l2_dev) + if (sd->grp_id) { + icd = (struct soc_camera_device *)sd->grp_id; + break; + } + + if (!icd) + return -EINVAL; for (i = 0; i < pdata->num_clients; i++) if (&pdata->clients[i].pdev->dev == icd->pdev) break; - dev_dbg(dev, "%s(%p): action = %lu, found #%d\n", __func__, dev, action, i); + dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i); if (i == pdata->num_clients) - return NOTIFY_DONE; + return -ENODEV; - switch (action) { - case BUS_NOTIFY_BOUND_DRIVER: - snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s%s", - dev_name(v4l2_dev->dev), ".mipi-csi"); - priv->subdev.grp_id = (long)icd; - ret = v4l2_device_register_subdev(v4l2_dev, &priv->subdev); - dev_dbg(dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret); - if (ret < 0) - return NOTIFY_DONE; + priv->client = pdata->clients + i; - priv->client = pdata->clients + i; + priv->set_bus_param = icd->ops->set_bus_param; + priv->query_bus_param = icd->ops->query_bus_param; + icd->ops->set_bus_param = sh_csi2_set_bus_param; + icd->ops->query_bus_param = sh_csi2_query_bus_param; - priv->set_bus_param = icd->ops->set_bus_param; - priv->query_bus_param = icd->ops->query_bus_param; - icd->ops->set_bus_param = sh_csi2_set_bus_param; - icd->ops->query_bus_param = sh_csi2_query_bus_param; + csi2_sd->grp_id = (long)icd; - pm_runtime_get_sync(v4l2_get_subdevdata(&priv->subdev)); + pm_runtime_get_sync(dev); - sh_csi2_hwinit(priv); - break; - case BUS_NOTIFY_UNBIND_DRIVER: - priv->client = NULL; + sh_csi2_hwinit(priv); - /* Driver is about to be unbound */ - icd->ops->set_bus_param = priv->set_bus_param; - icd->ops->query_bus_param = priv->query_bus_param; - priv->set_bus_param = NULL; - priv->query_bus_param = NULL; + return 0; +} - v4l2_device_unregister_subdev(&priv->subdev); +static void sh_csi2_client_disconnect(struct sh_csi2 *priv) +{ + struct soc_camera_device *icd = (struct soc_camera_device *)priv->subdev.grp_id; - pm_runtime_put(v4l2_get_subdevdata(&priv->subdev)); - break; - } + priv->client = NULL; + priv->subdev.grp_id = 0; - return NOTIFY_OK; + /* Driver is about to be unbound */ + icd->ops->set_bus_param = priv->set_bus_param; + icd->ops->query_bus_param = priv->query_bus_param; + priv->set_bus_param = NULL; + priv->query_bus_param = NULL; + + pm_runtime_put(v4l2_get_subdevdata(&priv->subdev)); } +static int sh_csi2_s_power(struct v4l2_subdev *sd, int on) +{ + struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); + + if (on) + return sh_csi2_client_connect(priv); + + sh_csi2_client_disconnect(priv); + return 0; +} + +static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops = { + .s_power = sh_csi2_s_power, +}; + +static struct v4l2_subdev_ops sh_csi2_subdev_ops = { + .core = &sh_csi2_subdev_core_ops, + .video = &sh_csi2_subdev_video_ops, +}; + static __devinit int sh_csi2_probe(struct platform_device *pdev) { struct resource *res; @@ -274,14 +286,6 @@ static __devinit int sh_csi2_probe(struct platform_device *pdev) return -ENOMEM; priv->irq = irq; - priv->notifier.notifier_call = sh_csi2_notify; - - /* We MUST attach after the MIPI sensor */ - ret = bus_register_notifier(&soc_camera_bus_type, &priv->notifier); - if (ret < 0) { - dev_err(&pdev->dev, "CSI2 cannot register notifier\n"); - goto ernotify; - } if (!request_mem_region(res->start, resource_size(res), pdev->name)) { dev_err(&pdev->dev, "CSI2 register region already claimed\n"); @@ -297,11 +301,17 @@ static __devinit int sh_csi2_probe(struct platform_device *pdev) } priv->pdev = pdev; + platform_set_drvdata(pdev, priv); v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops); v4l2_set_subdevdata(&priv->subdev, &pdev->dev); - platform_set_drvdata(pdev, priv); + snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi", + dev_name(pdata->v4l2_dev->dev)); + ret = v4l2_device_register_subdev(pdata->v4l2_dev, &priv->subdev); + dev_dbg(&pdev->dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret); + if (ret < 0) + goto esdreg; pm_runtime_enable(&pdev->dev); @@ -309,11 +319,11 @@ static __devinit int sh_csi2_probe(struct platform_device *pdev) return 0; +esdreg: + iounmap(priv->base); eremap: release_mem_region(res->start, resource_size(res)); ereqreg: - bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier); -ernotify: kfree(priv); return ret; @@ -324,7 +334,7 @@ static __devexit int sh_csi2_remove(struct platform_device *pdev) struct sh_csi2 *priv = platform_get_drvdata(pdev); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier); + v4l2_device_unregister_subdev(&priv->subdev); pm_runtime_disable(&pdev->dev); iounmap(priv->base); release_mem_region(res->start, resource_size(res)); @@ -335,8 +345,9 @@ static __devexit int sh_csi2_remove(struct platform_device *pdev) } static struct platform_driver __refdata sh_csi2_pdrv = { - .remove = __devexit_p(sh_csi2_remove), - .driver = { + .remove = __devexit_p(sh_csi2_remove), + .probe = sh_csi2_probe, + .driver = { .name = "sh-mobile-csi2", .owner = THIS_MODULE, }, @@ -344,7 +355,7 @@ static struct platform_driver __refdata sh_csi2_pdrv = { static int __init sh_csi2_init(void) { - return platform_driver_probe(&sh_csi2_pdrv, sh_csi2_probe); + return platform_driver_register(&sh_csi2_pdrv); } static void __exit sh_csi2_exit(void) diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h index 80346a6d28a9..48413b410f15 100644 --- a/include/media/sh_mobile_ceu.h +++ b/include/media/sh_mobile_ceu.h @@ -7,10 +7,18 @@ #define SH_CEU_FLAG_VSYNC_LOW (1 << 3) /* default High if possible */ struct device; +struct resource; + +struct sh_mobile_ceu_companion { + u32 num_resources; + struct resource *resource; + int id; + void *platform_data; +}; struct sh_mobile_ceu_info { unsigned long flags; - struct device *csi2_dev; + struct sh_mobile_ceu_companion *csi2; }; #endif /* __ASM_SH_MOBILE_CEU_H__ */ diff --git a/include/media/sh_mobile_csi2.h b/include/media/sh_mobile_csi2.h index 4d2615174461..c586c4f7f16b 100644 --- a/include/media/sh_mobile_csi2.h +++ b/include/media/sh_mobile_csi2.h @@ -11,6 +11,8 @@ #ifndef SH_MIPI_CSI #define SH_MIPI_CSI +#include + enum sh_csi2_phy { SH_CSI2_PHY_MAIN, SH_CSI2_PHY_SUB, @@ -33,14 +35,14 @@ struct sh_csi2_client_config { struct platform_device *pdev; /* client platform device */ }; +struct v4l2_device; + struct sh_csi2_pdata { enum sh_csi2_type type; unsigned int flags; struct sh_csi2_client_config *clients; int num_clients; + struct v4l2_device *v4l2_dev; }; -struct device; -struct v4l2_device; - #endif -- cgit v1.2.3 From d33b290a149dafe2e3cc2901ec726bea09a2c0f4 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 1 Jul 2011 11:31:35 -0300 Subject: [media] V4L: soc-camera: un-export the soc-camera bus The soc-camera bus is now completely local again. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera.c | 3 +-- include/media/soc_camera.h | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 96bed292c548..0df31b54e906 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -1207,12 +1207,11 @@ static int soc_camera_remove(struct device *dev) return 0; } -struct bus_type soc_camera_bus_type = { +static struct bus_type soc_camera_bus_type = { .name = "soc-camera", .probe = soc_camera_probe, .remove = soc_camera_remove, }; -EXPORT_SYMBOL_GPL(soc_camera_bus_type); static struct device_driver ic_drv = { .name = "camera", diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 70c4ea5ecc31..c31d55be3e17 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -20,12 +20,10 @@ #include #include -extern struct bus_type soc_camera_bus_type; - struct file; struct soc_camera_device { - struct list_head list; + struct list_head list; /* list of all registered devices */ struct device dev; struct device *pdev; /* Platform device */ s32 user_width; @@ -126,8 +124,8 @@ struct soc_camera_link { int num_regulators; /* - * For non-I2C devices platform platform has to provide methods to - * add a device to the system and to remove + * For non-I2C devices platform has to provide methods to add a device + * to the system and to remove it */ int (*add_device)(struct soc_camera_link *, struct device *); void (*del_device)(struct soc_camera_link *); -- cgit v1.2.3 From 7dfff95366f48bf66f77c17cdc9ebd8be696ac5d Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 15 Jul 2011 20:03:38 -0300 Subject: [media] V4L: soc-camera: remove soc-camera bus and devices on it Now that v4l2 subdevices have got their own device objects, having one more device in soc-camera clients became redundant and confusing. This patch removes those devices and the soc-camera bus, they used to reside on. Signed-off-by: Guennadi Liakhovetski Acked-by: Paul Mundt Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-shmobile/board-mackerel.c | 13 +- arch/sh/boards/mach-ap325rxa/setup.c | 15 +- drivers/media/video/atmel-isi.c | 64 ++++---- drivers/media/video/mt9m001.c | 14 +- drivers/media/video/mt9m111.c | 10 +- drivers/media/video/mt9t031.c | 3 +- drivers/media/video/mt9t112.c | 10 +- drivers/media/video/mt9v022.c | 10 +- drivers/media/video/mx1_camera.c | 42 +++--- drivers/media/video/mx2_camera.c | 46 +++--- drivers/media/video/mx3_camera.c | 56 +++---- drivers/media/video/omap1_camera.c | 52 +++---- drivers/media/video/ov2640.c | 13 +- drivers/media/video/ov772x.c | 10 +- drivers/media/video/ov9640.c | 13 +- drivers/media/video/ov9740.c | 13 +- drivers/media/video/pxa_camera.c | 46 +++--- drivers/media/video/rj54n1cb0c.c | 7 +- drivers/media/video/sh_mobile_ceu_camera.c | 74 ++++----- drivers/media/video/soc_camera.c | 234 ++++++++++------------------- drivers/media/video/soc_camera_platform.c | 10 +- drivers/media/video/tw9910.c | 10 +- include/media/soc_camera.h | 19 +-- include/media/soc_camera_platform.h | 15 +- 24 files changed, 337 insertions(+), 462 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index 3802f2afabef..1610fbf1a764 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -1192,8 +1192,8 @@ static struct platform_device sh_mmcif_device = { }; -static int mackerel_camera_add(struct soc_camera_link *icl, struct device *dev); -static void mackerel_camera_del(struct soc_camera_link *icl); +static int mackerel_camera_add(struct soc_camera_device *icd); +static void mackerel_camera_del(struct soc_camera_device *icd); static int camera_set_capture(struct soc_camera_platform_info *info, int enable) @@ -1232,16 +1232,15 @@ static void mackerel_camera_release(struct device *dev) soc_camera_platform_release(&camera_device); } -static int mackerel_camera_add(struct soc_camera_link *icl, - struct device *dev) +static int mackerel_camera_add(struct soc_camera_device *icd) { - return soc_camera_platform_add(icl, dev, &camera_device, &camera_link, + return soc_camera_platform_add(icd, &camera_device, &camera_link, mackerel_camera_release, 0); } -static void mackerel_camera_del(struct soc_camera_link *icl) +static void mackerel_camera_del(struct soc_camera_device *icd) { - soc_camera_platform_del(icl, camera_device, &camera_link); + soc_camera_platform_del(icd, camera_device, &camera_link); } static struct sh_mobile_ceu_info sh_mobile_ceu_info = { diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c index 969421f64a15..e57b1df6da6a 100644 --- a/arch/sh/boards/mach-ap325rxa/setup.c +++ b/arch/sh/boards/mach-ap325rxa/setup.c @@ -332,8 +332,8 @@ static int camera_set_capture(struct soc_camera_platform_info *info, return ret; } -static int ap325rxa_camera_add(struct soc_camera_link *icl, struct device *dev); -static void ap325rxa_camera_del(struct soc_camera_link *icl); +static int ap325rxa_camera_add(struct soc_camera_device *icd); +static void ap325rxa_camera_del(struct soc_camera_device *icd); static struct soc_camera_platform_info camera_info = { .format_name = "UYVY", @@ -366,24 +366,23 @@ static void ap325rxa_camera_release(struct device *dev) soc_camera_platform_release(&camera_device); } -static int ap325rxa_camera_add(struct soc_camera_link *icl, - struct device *dev) +static int ap325rxa_camera_add(struct soc_camera_device *icd) { - int ret = soc_camera_platform_add(icl, dev, &camera_device, &camera_link, + int ret = soc_camera_platform_add(icd, &camera_device, &camera_link, ap325rxa_camera_release, 0); if (ret < 0) return ret; ret = camera_probe(); if (ret < 0) - soc_camera_platform_del(icl, camera_device, &camera_link); + soc_camera_platform_del(icd, camera_device, &camera_link); return ret; } -static void ap325rxa_camera_del(struct soc_camera_link *icl) +static void ap325rxa_camera_del(struct soc_camera_device *icd) { - soc_camera_platform_del(icl, camera_device, &camera_link); + soc_camera_platform_del(icd, camera_device, &camera_link); } #endif /* CONFIG_I2C */ diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c index 4742c2837eb4..7b89f00501b8 100644 --- a/drivers/media/video/atmel-isi.c +++ b/drivers/media/video/atmel-isi.c @@ -253,7 +253,7 @@ static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, void *alloc_ctxs[]) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; unsigned long size; int ret, bytes_per_line; @@ -261,7 +261,7 @@ static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, /* Reset ISI */ ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); if (ret < 0) { - dev_err(icd->dev.parent, "Reset ISI timed out\n"); + dev_err(icd->parent, "Reset ISI timed out\n"); return ret; } /* Disable all interrupts */ @@ -288,7 +288,7 @@ static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, isi->sequence = 0; isi->active = NULL; - dev_dbg(icd->dev.parent, "%s, count=%d, size=%ld\n", __func__, + dev_dbg(icd->parent, "%s, count=%d, size=%ld\n", __func__, *nbuffers, size); return 0; @@ -308,7 +308,7 @@ static int buffer_prepare(struct vb2_buffer *vb) { struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; unsigned long size; struct isi_dma_desc *desc; @@ -321,7 +321,7 @@ static int buffer_prepare(struct vb2_buffer *vb) size = bytes_per_line * icd->user_height; if (vb2_plane_size(vb, 0) < size) { - dev_err(icd->dev.parent, "%s data will not fit into plane (%lu < %lu)\n", + dev_err(icd->parent, "%s data will not fit into plane (%lu < %lu)\n", __func__, vb2_plane_size(vb, 0), size); return -EINVAL; } @@ -330,7 +330,7 @@ static int buffer_prepare(struct vb2_buffer *vb) if (!buf->p_dma_desc) { if (list_empty(&isi->dma_desc_head)) { - dev_err(icd->dev.parent, "Not enough dma descriptors.\n"); + dev_err(icd->parent, "Not enough dma descriptors.\n"); return -EINVAL; } else { /* Get an available descriptor */ @@ -354,7 +354,7 @@ static int buffer_prepare(struct vb2_buffer *vb) static void buffer_cleanup(struct vb2_buffer *vb) { struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb); @@ -374,7 +374,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) /* Check if already in a frame */ if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) { - dev_err(isi->icd->dev.parent, "Already in frame handling.\n"); + dev_err(isi->icd->parent, "Already in frame handling.\n"); return; } @@ -394,7 +394,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) static void buffer_queue(struct vb2_buffer *vb) { struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb); unsigned long flags = 0; @@ -412,7 +412,7 @@ static void buffer_queue(struct vb2_buffer *vb) static int start_streaming(struct vb2_queue *vq) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; u32 sr = 0; @@ -427,7 +427,7 @@ static int start_streaming(struct vb2_queue *vq) isi_writel(isi, ISI_CTRL, ISI_CTRL_EN); spin_unlock_irq(&isi->lock); - dev_dbg(icd->dev.parent, "Waiting for SOF\n"); + dev_dbg(icd->parent, "Waiting for SOF\n"); ret = wait_event_interruptible(isi->vsync_wq, isi->state != ISI_STATE_IDLE); if (ret) @@ -448,7 +448,7 @@ static int start_streaming(struct vb2_queue *vq) static int stop_streaming(struct vb2_queue *vq) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; struct frame_buffer *buf, *node; int ret = 0; @@ -470,7 +470,7 @@ static int stop_streaming(struct vb2_queue *vq) msleep(1); if (time_after(jiffies, timeout)) { - dev_err(icd->dev.parent, + dev_err(icd->parent, "Timeout waiting for finishing codec request\n"); return -ETIMEDOUT; } @@ -482,7 +482,7 @@ static int stop_streaming(struct vb2_queue *vq) /* Disable ISI and wait for it is done */ ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE); if (ret < 0) - dev_err(icd->dev.parent, "Disable ISI timed out\n"); + dev_err(icd->parent, "Disable ISI timed out\n"); return ret; } @@ -518,7 +518,7 @@ static int isi_camera_init_videobuf(struct vb2_queue *q, static int isi_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; @@ -528,12 +528,12 @@ static int isi_camera_set_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(icd->dev.parent, "Format %x not found\n", + dev_warn(icd->parent, "Format %x not found\n", pix->pixelformat); return -EINVAL; } - dev_dbg(icd->dev.parent, "Plan to set format %dx%d\n", + dev_dbg(icd->parent, "Plan to set format %dx%d\n", pix->width, pix->height); mf.width = pix->width; @@ -559,7 +559,7 @@ static int isi_camera_set_fmt(struct soc_camera_device *icd, pix->colorspace = mf.colorspace; icd->current_fmt = xlate; - dev_dbg(icd->dev.parent, "Finally set format %dx%d\n", + dev_dbg(icd->parent, "Finally set format %dx%d\n", pix->width, pix->height); return ret; @@ -577,7 +577,7 @@ static int isi_camera_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (pixfmt && !xlate) { - dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); + dev_warn(icd->parent, "Format %x not found\n", pixfmt); return -EINVAL; } @@ -609,7 +609,7 @@ static int isi_camera_try_fmt(struct soc_camera_device *icd, case V4L2_FIELD_NONE: break; default: - dev_err(icd->dev.parent, "Field type %d unsupported.\n", + dev_err(icd->parent, "Field type %d unsupported.\n", mf.field); ret = -EINVAL; } @@ -670,7 +670,7 @@ static unsigned long make_bus_param(struct atmel_isi *isi) static int isi_camera_try_bus_param(struct soc_camera_device *icd, unsigned char buswidth) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; unsigned long camera_flags; int ret; @@ -702,7 +702,7 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, fmt = soc_mbus_get_fmtdesc(code); if (!fmt) { - dev_err(icd->dev.parent, + dev_err(icd->parent, "Invalid format code #%u: %d\n", idx, code); return 0; } @@ -710,7 +710,7 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, /* This also checks support for the requested bits-per-sample */ ret = isi_camera_try_bus_param(icd, fmt->bits_per_sample); if (ret < 0) { - dev_err(icd->dev.parent, + dev_err(icd->parent, "Fail to try the bus parameters.\n"); return 0; } @@ -725,7 +725,7 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, xlate->host_fmt = &isi_camera_formats[0]; xlate->code = code; xlate++; - dev_dbg(icd->dev.parent, "Providing format %s using code %d\n", + dev_dbg(icd->parent, "Providing format %s using code %d\n", isi_camera_formats[0].name, code); } break; @@ -733,7 +733,7 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, if (!isi_camera_packing_supported(fmt)) return 0; if (xlate) - dev_dbg(icd->dev.parent, + dev_dbg(icd->parent, "Providing format %s in pass-through mode\n", fmt->name); } @@ -752,7 +752,7 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, /* Called with .video_lock held */ static int isi_camera_add_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; int ret; @@ -764,14 +764,14 @@ static int isi_camera_add_device(struct soc_camera_device *icd) return ret; isi->icd = icd; - dev_dbg(icd->dev.parent, "Atmel ISI Camera driver attached to camera %d\n", + dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n", icd->devnum); return 0; } /* Called with .video_lock held */ static void isi_camera_remove_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; BUG_ON(icd != isi->icd); @@ -779,7 +779,7 @@ static void isi_camera_remove_device(struct soc_camera_device *icd) clk_disable(isi->pclk); isi->icd = NULL; - dev_dbg(icd->dev.parent, "Atmel ISI Camera driver detached from camera %d\n", + dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n", icd->devnum); } @@ -802,7 +802,7 @@ static int isi_camera_querycap(struct soc_camera_host *ici, static int isi_camera_set_bus_param(struct soc_camera_device *icd, u32 pixfmt) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; unsigned long bus_flags, camera_flags, common_flags; int ret; @@ -812,7 +812,7 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd, u32 pixfmt) bus_flags = make_bus_param(isi); common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); - dev_dbg(icd->dev.parent, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n", + dev_dbg(icd->parent, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n", camera_flags, bus_flags, common_flags); if (!common_flags) return -EINVAL; @@ -844,7 +844,7 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd, u32 pixfmt) ret = icd->ops->set_bus_param(icd, common_flags); if (ret < 0) { - dev_dbg(icd->dev.parent, "Camera set_bus_param(%lx) returned %d\n", + dev_dbg(icd->parent, "Camera set_bus_param(%lx) returned %d\n", common_flags, ret); return ret; } diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index e2bbd8c35c98..4da9cca939c1 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -603,13 +603,9 @@ static int mt9m001_video_probe(struct soc_camera_device *icd, unsigned long flags; int ret; - /* - * We must have a parent by now. And it cannot be a wrong one. - * So this entire test is completely redundant. - */ - if (!icd->dev.parent || - to_soc_camera_host(icd->dev.parent)->nr != icd->iface) - return -ENODEV; + /* We must have a parent by now. And it cannot be a wrong one. */ + BUG_ON(!icd->parent || + to_soc_camera_host(icd->parent)->nr != icd->iface); /* Enable the chip */ data = reg_write(client, MT9M001_CHIP_ENABLE, 1); @@ -675,8 +671,8 @@ static void mt9m001_video_remove(struct soc_camera_device *icd) { struct soc_camera_link *icl = to_soc_camera_link(icd); - dev_dbg(&icd->dev, "Video removed: %p, %p\n", - icd->dev.parent, icd->vdev); + dev_dbg(icd->pdev, "Video removed: %p, %p\n", + icd->parent, icd->vdev); if (icl->free_bus) icl->free_bus(icl); } diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index 7962334a386b..cbccc869c7e1 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -960,13 +960,9 @@ static int mt9m111_video_probe(struct soc_camera_device *icd, s32 data; int ret; - /* - * We must have a parent by now. And it cannot be a wrong one. - * So this entire test is completely redundant. - */ - if (!icd->dev.parent || - to_soc_camera_host(icd->dev.parent)->nr != icd->iface) - return -ENODEV; + /* We must have a parent by now. And it cannot be a wrong one. */ + BUG_ON(!icd->parent || + to_soc_camera_host(icd->parent)->nr != icd->iface); mt9m111->autoexposure = 1; mt9m111->autowhitebalance = 1; diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index 7ce279c3751d..30547cc3f89b 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -700,8 +700,7 @@ static int mt9t031_runtime_suspend(struct device *dev) static int mt9t031_runtime_resume(struct device *dev) { struct video_device *vdev = to_video_device(dev); - struct soc_camera_device *icd = container_of(vdev->parent, - struct soc_camera_device, dev); + struct soc_camera_device *icd = dev_get_drvdata(vdev->parent); struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t031 *mt9t031 = to_mt9t031(client); diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c index bffa9ee10968..d2e0a50063a2 100644 --- a/drivers/media/video/mt9t112.c +++ b/drivers/media/video/mt9t112.c @@ -1057,13 +1057,9 @@ static int mt9t112_camera_probe(struct soc_camera_device *icd, const char *devname; int chipid; - /* - * We must have a parent by now. And it cannot be a wrong one. - * So this entire test is completely redundant. - */ - if (!icd->dev.parent || - to_soc_camera_host(icd->dev.parent)->nr != icd->iface) - return -ENODEV; + /* We must have a parent by now. And it cannot be a wrong one. */ + BUG_ON(!icd->parent || + to_soc_camera_host(icd->parent)->nr != icd->iface); /* * check and show chip ID diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index fc76ed1c08e5..51b0fccbfe70 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -728,9 +728,9 @@ static int mt9v022_video_probe(struct soc_camera_device *icd, int ret; unsigned long flags; - if (!icd->dev.parent || - to_soc_camera_host(icd->dev.parent)->nr != icd->iface) - return -ENODEV; + /* We must have a parent by now. And it cannot be a wrong one. */ + BUG_ON(!icd->parent || + to_soc_camera_host(icd->parent)->nr != icd->iface); /* Read out the chip version register */ data = reg_read(client, MT9V022_CHIP_VERSION); @@ -809,8 +809,8 @@ static void mt9v022_video_remove(struct soc_camera_device *icd) { struct soc_camera_link *icl = to_soc_camera_link(icd); - dev_dbg(&icd->dev, "Video removed: %p, %p\n", - icd->dev.parent, icd->vdev); + dev_dbg(icd->pdev, "Video removed: %p, %p\n", + icd->parent, icd->vdev); if (icl->free_bus) icl->free_bus(icl); } diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c index 30a1364675c2..087db12a3a67 100644 --- a/drivers/media/video/mx1_camera.c +++ b/drivers/media/video/mx1_camera.c @@ -141,7 +141,7 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; - dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size); return 0; } @@ -153,7 +153,7 @@ static void free_buffer(struct videobuf_queue *vq, struct mx1_buffer *buf) BUG_ON(in_interrupt()); - dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); /* @@ -178,7 +178,7 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq, if (bytes_per_line < 0) return bytes_per_line; - dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); /* Added list head initialization on alloc */ @@ -231,7 +231,7 @@ out: static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) { struct videobuf_buffer *vbuf = &pcdev->active->vb; - struct device *dev = pcdev->icd->dev.parent; + struct device *dev = pcdev->icd->parent; int ret; if (unlikely(!pcdev->active)) { @@ -255,11 +255,11 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx1_camera_dev *pcdev = ici->priv; struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); - dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); list_add_tail(&vb->queue, &pcdev->capture); @@ -286,7 +286,7 @@ static void mx1_videobuf_release(struct videobuf_queue *vq, struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); #ifdef DEBUG struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->dev.parent; + struct device *dev = icd->parent; dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); @@ -342,7 +342,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev, static void mx1_camera_dma_irq(int channel, void *data) { struct mx1_camera_dev *pcdev = data; - struct device *dev = pcdev->icd->dev.parent; + struct device *dev = pcdev->icd->parent; struct mx1_buffer *buf; struct videobuf_buffer *vb; unsigned long flags; @@ -377,10 +377,10 @@ static struct videobuf_queue_ops mx1_videobuf_ops = { static void mx1_camera_init_videobuf(struct videobuf_queue *q, struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx1_camera_dev *pcdev = ici->priv; - videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->dev.parent, + videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, sizeof(struct mx1_buffer), icd, &icd->video_lock); @@ -400,7 +400,7 @@ static int mclk_get_divisor(struct mx1_camera_dev *pcdev) */ div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1; - dev_dbg(pcdev->icd->dev.parent, + dev_dbg(pcdev->icd->parent, "System clock %lukHz, target freq %dkHz, divisor %lu\n", lcdclk / 1000, mclk / 1000, div); @@ -411,7 +411,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev) { unsigned int csicr1 = CSICR1_EN; - dev_dbg(pcdev->icd->dev.parent, "Activate device\n"); + dev_dbg(pcdev->icd->parent, "Activate device\n"); clk_enable(pcdev->clk); @@ -427,7 +427,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev) static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev) { - dev_dbg(pcdev->icd->dev.parent, "Deactivate device\n"); + dev_dbg(pcdev->icd->parent, "Deactivate device\n"); /* Disable all CSI interface */ __raw_writel(0x00, pcdev->base + CSICR1); @@ -441,13 +441,13 @@ static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev) */ static int mx1_camera_add_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx1_camera_dev *pcdev = ici->priv; if (pcdev->icd) return -EBUSY; - dev_info(icd->dev.parent, "MX1 Camera driver attached to camera %d\n", + dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n", icd->devnum); mx1_camera_activate(pcdev); @@ -459,7 +459,7 @@ static int mx1_camera_add_device(struct soc_camera_device *icd) static void mx1_camera_remove_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx1_camera_dev *pcdev = ici->priv; unsigned int csicr1; @@ -472,7 +472,7 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd) /* Stop DMA engine */ imx_dma_disable(pcdev->dma_chan); - dev_info(icd->dev.parent, "MX1 Camera driver detached from camera %d\n", + dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n", icd->devnum); mx1_camera_deactivate(pcdev); @@ -490,7 +490,7 @@ static int mx1_camera_set_crop(struct soc_camera_device *icd, static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx1_camera_dev *pcdev = ici->priv; unsigned long camera_flags, common_flags; unsigned int csicr1; @@ -561,14 +561,14 @@ static int mx1_camera_set_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(icd->dev.parent, "Format %x not found\n", + dev_warn(icd->parent, "Format %x not found\n", pix->pixelformat); return -EINVAL; } buswidth = xlate->host_fmt->bits_per_sample; if (buswidth > 8) { - dev_warn(icd->dev.parent, + dev_warn(icd->parent, "bits-per-sample %d for format %x unsupported\n", buswidth, pix->pixelformat); return -EINVAL; @@ -608,7 +608,7 @@ static int mx1_camera_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(icd->dev.parent, "Format %x not found\n", + dev_warn(icd->parent, "Format %x not found\n", pix->pixelformat); return -EINVAL; } diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index 55d48442c2eb..ec2410c0c806 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -277,7 +277,7 @@ static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) */ static int mx2_camera_add_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; int ret; u32 csicr1; @@ -302,7 +302,7 @@ static int mx2_camera_add_device(struct soc_camera_device *icd) pcdev->icd = icd; - dev_info(icd->dev.parent, "Camera driver attached to camera %d\n", + dev_info(icd->parent, "Camera driver attached to camera %d\n", icd->devnum); return 0; @@ -310,12 +310,12 @@ static int mx2_camera_add_device(struct soc_camera_device *icd) static void mx2_camera_remove_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; BUG_ON(icd != pcdev->icd); - dev_info(icd->dev.parent, "Camera driver detached from camera %d\n", + dev_info(icd->parent, "Camera driver detached from camera %d\n", icd->devnum); mx2_camera_deactivate(pcdev); @@ -436,7 +436,7 @@ static int mx2_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); - dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size); if (bytes_per_line < 0) return bytes_per_line; @@ -456,7 +456,7 @@ static void free_buffer(struct videobuf_queue *vq, struct mx2_buffer *buf) struct soc_camera_device *icd = vq->priv_data; struct videobuf_buffer *vb = &buf->vb; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); /* @@ -466,7 +466,7 @@ static void free_buffer(struct videobuf_queue *vq, struct mx2_buffer *buf) videobuf_waiton(vq, vb, 0, 0); videobuf_dma_contig_free(vq, vb); - dev_dbg(&icd->dev, "%s freed\n", __func__); + dev_dbg(icd->parent, "%s freed\n", __func__); vb->state = VIDEOBUF_NEEDS_INIT; } @@ -480,7 +480,7 @@ static int mx2_videobuf_prepare(struct videobuf_queue *vq, icd->current_fmt->host_fmt); int ret = 0; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); if (bytes_per_line < 0) @@ -532,12 +532,12 @@ static void mx2_videobuf_queue(struct videobuf_queue *vq, { struct soc_camera_device *icd = vq->priv_data; struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); unsigned long flags; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); spin_lock_irqsave(&pcdev->lock, flags); @@ -610,27 +610,27 @@ static void mx2_videobuf_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); unsigned long flags; #ifdef DEBUG - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); switch (vb->state) { case VIDEOBUF_ACTIVE: - dev_info(&icd->dev, "%s (active)\n", __func__); + dev_info(icd->parent, "%s (active)\n", __func__); break; case VIDEOBUF_QUEUED: - dev_info(&icd->dev, "%s (queued)\n", __func__); + dev_info(icd->parent, "%s (queued)\n", __func__); break; case VIDEOBUF_PREPARED: - dev_info(&icd->dev, "%s (prepared)\n", __func__); + dev_info(icd->parent, "%s (prepared)\n", __func__); break; default: - dev_info(&icd->dev, "%s (unknown) %d\n", __func__, + dev_info(icd->parent, "%s (unknown) %d\n", __func__, vb->state); break; } @@ -677,7 +677,7 @@ static struct videobuf_queue_ops mx2_videobuf_ops = { static void mx2_camera_init_videobuf(struct videobuf_queue *q, struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; videobuf_queue_dma_contig_init(q, &mx2_videobuf_ops, pcdev->dev, @@ -718,7 +718,7 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, int bytesperline) { struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; writel(pcdev->discard_buffer_dma, @@ -771,7 +771,7 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; unsigned long camera_flags, common_flags; int ret = 0; @@ -890,7 +890,7 @@ static int mx2_camera_set_crop(struct soc_camera_device *icd, if (ret < 0) return ret; - dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n", + dev_dbg(icd->parent, "Sensor cropped %dx%d\n", mf.width, mf.height); icd->user_width = mf.width; @@ -910,7 +910,7 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(icd->dev.parent, "Format %x not found\n", + dev_warn(icd->parent, "Format %x not found\n", pix->pixelformat); return -EINVAL; } @@ -950,7 +950,7 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (pixfmt && !xlate) { - dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); + dev_warn(icd->parent, "Format %x not found\n", pixfmt); return -EINVAL; } @@ -1000,7 +1000,7 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, if (mf.field == V4L2_FIELD_ANY) mf.field = V4L2_FIELD_NONE; if (mf.field != V4L2_FIELD_NONE) { - dev_err(icd->dev.parent, "Field type %d unsupported.\n", + dev_err(icd->parent, "Field type %d unsupported.\n", mf.field); return -EINVAL; } diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index cdb7dad05c1e..c045b47803ad 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -194,7 +194,7 @@ static int mx3_videobuf_setup(struct vb2_queue *vq, unsigned long sizes[], void *alloc_ctxs[]) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); @@ -223,7 +223,7 @@ static int mx3_videobuf_setup(struct vb2_queue *vq, static int mx3_videobuf_prepare(struct vb2_buffer *vb) { struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; struct scatterlist *sg; @@ -241,7 +241,7 @@ static int mx3_videobuf_prepare(struct vb2_buffer *vb) new_size = bytes_per_line * icd->user_height; if (vb2_plane_size(vb, 0) < new_size) { - dev_err(icd->dev.parent, "Buffer too small (%lu < %zu)\n", + dev_err(icd->parent, "Buffer too small (%lu < %zu)\n", vb2_plane_size(vb, 0), new_size); return -ENOBUFS; } @@ -283,7 +283,7 @@ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) static void mx3_videobuf_queue(struct vb2_buffer *vb) { struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct mx3_camera_buffer *buf = to_mx3_vb(vb); struct dma_async_tx_descriptor *txd = buf->txd; @@ -336,7 +336,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb) spin_unlock_irq(&mx3_cam->lock); cookie = txd->tx_submit(txd); - dev_dbg(icd->dev.parent, "Submitted cookie %d DMA 0x%08x\n", + dev_dbg(icd->parent, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg)); if (cookie >= 0) @@ -357,13 +357,13 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb) static void mx3_videobuf_release(struct vb2_buffer *vb) { struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct mx3_camera_buffer *buf = to_mx3_vb(vb); struct dma_async_tx_descriptor *txd = buf->txd; unsigned long flags; - dev_dbg(icd->dev.parent, + dev_dbg(icd->parent, "Release%s DMA 0x%08x, queue %sempty\n", mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg), list_empty(&buf->queue) ? "" : "not "); @@ -402,7 +402,7 @@ static int mx3_videobuf_init(struct vb2_buffer *vb) static int mx3_stop_streaming(struct vb2_queue *q) { struct soc_camera_device *icd = soc_camera_from_vb2q(q); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; struct dma_chan *chan; @@ -498,7 +498,7 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, clk_enable(mx3_cam->clk); rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk); - dev_dbg(icd->dev.parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate); + dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate); if (rate) clk_set_rate(mx3_cam->clk, rate); } @@ -506,7 +506,7 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, /* Called with .video_lock held */ static int mx3_camera_add_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; if (mx3_cam->icd) @@ -516,7 +516,7 @@ static int mx3_camera_add_device(struct soc_camera_device *icd) mx3_cam->icd = icd; - dev_info(icd->dev.parent, "MX3 Camera driver attached to camera %d\n", + dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n", icd->devnum); return 0; @@ -525,7 +525,7 @@ static int mx3_camera_add_device(struct soc_camera_device *icd) /* Called with .video_lock held */ static void mx3_camera_remove_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; @@ -540,7 +540,7 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd) mx3_cam->icd = NULL; - dev_info(icd->dev.parent, "MX3 Camera driver detached from camera %d\n", + dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n", icd->devnum); } @@ -607,12 +607,12 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam, static int mx3_camera_try_bus_param(struct soc_camera_device *icd, const unsigned int depth) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; unsigned long bus_flags, camera_flags; int ret = test_platform_param(mx3_cam, depth, &bus_flags); - dev_dbg(icd->dev.parent, "request bus width %d bit: %d\n", depth, ret); + dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret); if (ret < 0) return ret; @@ -621,7 +621,7 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd, ret = soc_camera_bus_param_compatible(camera_flags, bus_flags); if (ret < 0) - dev_warn(icd->dev.parent, + dev_warn(icd->parent, "Flags incompatible: camera %lx, host %lx\n", camera_flags, bus_flags); @@ -675,7 +675,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id struct soc_camera_format_xlate *xlate) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->dev.parent; + struct device *dev = icd->parent; int formats = 0, ret; enum v4l2_mbus_pixelcode code; const struct soc_mbus_pixelfmt *fmt; @@ -687,7 +687,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id fmt = soc_mbus_get_fmtdesc(code); if (!fmt) { - dev_warn(icd->dev.parent, + dev_warn(icd->parent, "Unsupported format code #%u: %d\n", idx, code); return 0; } @@ -815,7 +815,7 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd, struct v4l2_crop *a) { struct v4l2_rect *rect = &a->c; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct v4l2_mbus_framefmt mf; @@ -848,7 +848,7 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd, configure_geometry(mx3_cam, mf.width, mf.height, icd->current_fmt->host_fmt); - dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n", + dev_dbg(icd->parent, "Sensor cropped %dx%d\n", mf.width, mf.height); icd->user_width = mf.width; @@ -860,7 +860,7 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd, static int mx3_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; @@ -870,13 +870,13 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(icd->dev.parent, "Format %x not found\n", + dev_warn(icd->parent, "Format %x not found\n", pix->pixelformat); return -EINVAL; } stride_align(&pix->width); - dev_dbg(icd->dev.parent, "Set format %dx%d\n", pix->width, pix->height); + dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height); /* * Might have to perform a complete interface initialisation like in @@ -912,7 +912,7 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, pix->colorspace = mf.colorspace; icd->current_fmt = xlate; - dev_dbg(icd->dev.parent, "Sensor set %dx%d\n", pix->width, pix->height); + dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height); return ret; } @@ -929,7 +929,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (pixfmt && !xlate) { - dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); + dev_warn(icd->parent, "Format %x not found\n", pixfmt); return -EINVAL; } @@ -961,7 +961,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd, case V4L2_FIELD_NONE: break; default: - dev_err(icd->dev.parent, "Field type %d unsupported.\n", + dev_err(icd->parent, "Field type %d unsupported.\n", mf.field); ret = -EINVAL; } @@ -994,7 +994,7 @@ static int mx3_camera_querycap(struct soc_camera_host *ici, static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; unsigned long bus_flags, camera_flags, common_flags; u32 dw, sens_conf; @@ -1002,7 +1002,7 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) int buswidth; int ret; const struct soc_camera_format_xlate *xlate; - struct device *dev = icd->dev.parent; + struct device *dev = icd->parent; fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code); if (!fmt) diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c index 9bfe4c121a5c..8a947e603aca 100644 --- a/drivers/media/video/omap1_camera.c +++ b/drivers/media/video/omap1_camera.c @@ -207,7 +207,7 @@ static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, struct soc_camera_device *icd = vq->priv_data; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; if (bytes_per_line < 0) @@ -221,7 +221,7 @@ static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; - dev_dbg(icd->dev.parent, + dev_dbg(icd->parent, "%s: count=%d, size=%d\n", __func__, *count, *size); return 0; @@ -240,7 +240,7 @@ static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf, videobuf_dma_contig_free(vq, vb); } else { struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->dev.parent; + struct device *dev = icd->parent; struct videobuf_dmabuf *dma = videobuf_to_dma(vb); videobuf_dma_unmap(dev, dma); @@ -257,7 +257,7 @@ static int omap1_videobuf_prepare(struct videobuf_queue *vq, struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb); int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; int ret; @@ -489,7 +489,7 @@ static void omap1_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; struct omap1_cam_buf *buf; u32 mode; @@ -518,7 +518,7 @@ static void omap1_videobuf_queue(struct videobuf_queue *vq, pcdev->active = buf; pcdev->ready = NULL; - dev_dbg(icd->dev.parent, + dev_dbg(icd->parent, "%s: capture not active, setup FIFO, start DMA\n", __func__); mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK; mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT; @@ -542,8 +542,8 @@ static void omap1_videobuf_release(struct videobuf_queue *vq, struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb); struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->dev.parent; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->parent; + struct soc_camera_host *ici = to_soc_camera_host(dev); struct omap1_cam_dev *pcdev = ici->priv; switch (vb->state) { @@ -572,7 +572,7 @@ static void videobuf_done(struct omap1_cam_dev *pcdev, { struct omap1_cam_buf *buf = pcdev->active; struct videobuf_buffer *vb; - struct device *dev = pcdev->icd->dev.parent; + struct device *dev = pcdev->icd->parent; if (WARN_ON(!buf)) { suspend_capture(pcdev); @@ -798,7 +798,7 @@ out: static irqreturn_t cam_isr(int irq, void *data) { struct omap1_cam_dev *pcdev = data; - struct device *dev = pcdev->icd->dev.parent; + struct device *dev = pcdev->icd->parent; struct omap1_cam_buf *buf = pcdev->active; u32 it_status; unsigned long flags; @@ -908,7 +908,7 @@ static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset) */ static int omap1_cam_add_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; u32 ctrlclock; @@ -951,14 +951,14 @@ static int omap1_cam_add_device(struct soc_camera_device *icd) pcdev->icd = icd; - dev_dbg(icd->dev.parent, "OMAP1 Camera driver attached to camera %d\n", + dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n", icd->devnum); return 0; } static void omap1_cam_remove_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; u32 ctrlclock; @@ -984,7 +984,7 @@ static void omap1_cam_remove_device(struct soc_camera_device *icd) pcdev->icd = NULL; - dev_dbg(icd->dev.parent, + dev_dbg(icd->parent, "OMAP1 Camera driver detached from camera %d\n", icd->devnum); } @@ -1069,7 +1069,7 @@ static int omap1_cam_get_formats(struct soc_camera_device *icd, unsigned int idx, struct soc_camera_format_xlate *xlate) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->dev.parent; + struct device *dev = icd->parent; int formats = 0, ret; enum v4l2_mbus_pixelcode code; const struct soc_mbus_pixelfmt *fmt; @@ -1221,9 +1221,9 @@ static int omap1_cam_set_crop(struct soc_camera_device *icd, struct v4l2_rect *rect = &crop->c; const struct soc_camera_format_xlate *xlate = icd->current_fmt; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->parent; + struct soc_camera_host *ici = to_soc_camera_host(dev); struct omap1_cam_dev *pcdev = ici->priv; - struct device *dev = icd->dev.parent; struct v4l2_mbus_framefmt mf; int ret; @@ -1269,8 +1269,8 @@ static int omap1_cam_set_fmt(struct soc_camera_device *icd, { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; - struct device *dev = icd->dev.parent; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->parent; + struct soc_camera_host *ici = to_soc_camera_host(dev); struct omap1_cam_dev *pcdev = ici->priv; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_mbus_framefmt mf; @@ -1325,7 +1325,7 @@ static int omap1_cam_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(icd->dev.parent, "Format %#x not found\n", + dev_warn(icd->parent, "Format %#x not found\n", pix->pixelformat); return -EINVAL; } @@ -1361,7 +1361,7 @@ static int omap1_cam_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma) { struct soc_camera_device *icd = q->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; int ret; @@ -1376,17 +1376,17 @@ static int omap1_cam_mmap_mapper(struct videobuf_queue *q, static void omap1_cam_init_videobuf(struct videobuf_queue *q, struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; if (!sg_mode) videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops, - icd->dev.parent, &pcdev->lock, + icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, sizeof(struct omap1_cam_buf), icd, &icd->video_lock); else videobuf_queue_sg_init(q, &omap1_videobuf_ops, - icd->dev.parent, &pcdev->lock, + icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, sizeof(struct omap1_cam_buf), icd, &icd->video_lock); @@ -1438,9 +1438,9 @@ static int omap1_cam_querycap(struct soc_camera_host *ici, static int omap1_cam_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->parent; + struct soc_camera_host *ici = to_soc_camera_host(dev); struct omap1_cam_dev *pcdev = ici->priv; - struct device *dev = icd->dev.parent; const struct soc_camera_format_xlate *xlate; const struct soc_mbus_pixelfmt *fmt; unsigned long camera_flags, common_flags; diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c index 0cea0cf36679..9ce2fa037b94 100644 --- a/drivers/media/video/ov2640.c +++ b/drivers/media/video/ov2640.c @@ -1031,16 +1031,9 @@ static int ov2640_video_probe(struct soc_camera_device *icd, const char *devname; int ret; - /* - * we must have a parent by now. And it cannot be a wrong one. - * So this entire test is completely redundant. - */ - if (!icd->dev.parent || - to_soc_camera_host(icd->dev.parent)->nr != icd->iface) { - dev_err(&client->dev, "Parent missing or invalid!\n"); - ret = -ENODEV; - goto err; - } + /* We must have a parent by now. And it cannot be a wrong one. */ + BUG_ON(!icd->parent || + to_soc_camera_host(icd->parent)->nr != icd->iface); /* * check and show product ID and manufacturer ID diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 48895ef863ff..397870f076c1 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -1032,13 +1032,9 @@ static int ov772x_video_probe(struct soc_camera_device *icd, u8 pid, ver; const char *devname; - /* - * We must have a parent by now. And it cannot be a wrong one. - * So this entire test is completely redundant. - */ - if (!icd->dev.parent || - to_soc_camera_host(icd->dev.parent)->nr != icd->iface) - return -ENODEV; + /* We must have a parent by now. And it cannot be a wrong one. */ + BUG_ON(!icd->parent || + to_soc_camera_host(icd->parent)->nr != icd->iface); /* * check and show product ID and manufacturer ID diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c index 5173ac449dd8..3681a6ff0815 100644 --- a/drivers/media/video/ov9640.c +++ b/drivers/media/video/ov9640.c @@ -657,16 +657,9 @@ static int ov9640_video_probe(struct soc_camera_device *icd, const char *devname; int ret = 0; - /* - * We must have a parent by now. And it cannot be a wrong one. - * So this entire test is completely redundant. - */ - if (!icd->dev.parent || - to_soc_camera_host(icd->dev.parent)->nr != icd->iface) { - dev_err(&client->dev, "Parent missing or invalid!\n"); - ret = -ENODEV; - goto err; - } + /* We must have a parent by now. And it cannot be a wrong one. */ + BUG_ON(!icd->parent || + to_soc_camera_host(icd->parent)->nr != icd->iface); /* * check and show product ID and manufacturer ID diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c index ede48f2ed70c..edd1ffcca30b 100644 --- a/drivers/media/video/ov9740.c +++ b/drivers/media/video/ov9740.c @@ -898,16 +898,9 @@ static int ov9740_video_probe(struct soc_camera_device *icd, u8 modelhi, modello; int ret; - /* - * We must have a parent by now. And it cannot be a wrong one. - * So this entire test is completely redundant. - */ - if (!icd->dev.parent || - to_soc_camera_host(icd->dev.parent)->nr != icd->iface) { - dev_err(&client->dev, "Parent missing or invalid!\n"); - ret = -ENODEV; - goto err; - } + /* We must have a parent by now. And it cannot be a wrong one. */ + BUG_ON(!icd->parent || + to_soc_camera_host(icd->parent)->nr != icd->iface); /* * check and show product ID and manufacturer ID diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 020de529b955..d07df22a5ec6 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -246,7 +246,7 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, if (bytes_per_line < 0) return bytes_per_line; - dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size); *size = bytes_per_line * icd->user_height; @@ -261,13 +261,13 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) { struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); int i; BUG_ON(in_interrupt()); - dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, &buf->vb, buf->vb.baddr, buf->vb.bsize); /* @@ -428,7 +428,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field) { struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct pxa_camera_dev *pcdev = ici->priv; struct device *dev = pcdev->soc_host.v4l2_dev.dev; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); @@ -635,11 +635,11 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n", + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n", __func__, vb, vb->baddr, vb->bsize, pcdev->active); list_add_tail(&vb->queue, &pcdev->capture); @@ -657,7 +657,7 @@ static void pxa_videobuf_release(struct videobuf_queue *vq, struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); #ifdef DEBUG struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->dev.parent; + struct device *dev = icd->parent; dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); @@ -842,7 +842,7 @@ static struct videobuf_queue_ops pxa_videobuf_ops = { static void pxa_camera_init_videobuf(struct videobuf_queue *q, struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct pxa_camera_dev *pcdev = ici->priv; /* @@ -971,7 +971,7 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) */ static int pxa_camera_add_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct pxa_camera_dev *pcdev = ici->priv; if (pcdev->icd) @@ -981,7 +981,7 @@ static int pxa_camera_add_device(struct soc_camera_device *icd) pcdev->icd = icd; - dev_info(icd->dev.parent, "PXA Camera driver attached to camera %d\n", + dev_info(icd->parent, "PXA Camera driver attached to camera %d\n", icd->devnum); return 0; @@ -990,12 +990,12 @@ static int pxa_camera_add_device(struct soc_camera_device *icd) /* Called with .video_lock held */ static void pxa_camera_remove_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct pxa_camera_dev *pcdev = ici->priv; BUG_ON(icd != pcdev->icd); - dev_info(icd->dev.parent, "PXA Camera driver detached from camera %d\n", + dev_info(icd->parent, "PXA Camera driver detached from camera %d\n", icd->devnum); /* disable capture, disable interrupts */ @@ -1056,7 +1056,7 @@ static int test_platform_param(struct pxa_camera_dev *pcdev, static void pxa_camera_setup_cicr(struct soc_camera_device *icd, unsigned long flags, __u32 pixfmt) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct pxa_camera_dev *pcdev = ici->priv; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); unsigned long dw, bpp; @@ -1151,7 +1151,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct pxa_camera_dev *pcdev = ici->priv; unsigned long bus_flags, camera_flags, common_flags; int ret; @@ -1209,7 +1209,7 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) static int pxa_camera_try_bus_param(struct soc_camera_device *icd, unsigned char buswidth) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct pxa_camera_dev *pcdev = ici->priv; unsigned long bus_flags, camera_flags; int ret = test_platform_param(pcdev, buswidth, &bus_flags); @@ -1246,7 +1246,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id struct soc_camera_format_xlate *xlate) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->dev.parent; + struct device *dev = icd->parent; int formats = 0, ret; struct pxa_cam *cam; enum v4l2_mbus_pixelcode code; @@ -1334,9 +1334,9 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd, struct v4l2_crop *a) { struct v4l2_rect *rect = &a->c; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->parent; + struct soc_camera_host *ici = to_soc_camera_host(dev); struct pxa_camera_dev *pcdev = ici->priv; - struct device *dev = icd->dev.parent; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_sense sense = { .master_clock = pcdev->mclk, @@ -1378,7 +1378,7 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd, return ret; if (pxa_camera_check_frame(mf.width, mf.height)) { - dev_warn(icd->dev.parent, + dev_warn(icd->parent, "Inconsistent state. Use S_FMT to repair\n"); return -EINVAL; } @@ -1405,9 +1405,9 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd, static int pxa_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->parent; + struct soc_camera_host *ici = to_soc_camera_host(dev); struct pxa_camera_dev *pcdev = ici->priv; - struct device *dev = icd->dev.parent; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate = NULL; struct soc_camera_sense sense = { @@ -1484,7 +1484,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); + dev_warn(icd->parent, "Format %x not found\n", pixfmt); return -EINVAL; } @@ -1521,7 +1521,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, break; default: /* TODO: support interlaced at least in pass-through mode */ - dev_err(icd->dev.parent, "Field type %d unsupported.\n", + dev_err(icd->parent, "Field type %d unsupported.\n", mf.field); return -EINVAL; } diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c index 57e11b6f19fb..847ccc067e87 100644 --- a/drivers/media/video/rj54n1cb0c.c +++ b/drivers/media/video/rj54n1cb0c.c @@ -1364,10 +1364,9 @@ static int rj54n1_video_probe(struct soc_camera_device *icd, int data1, data2; int ret; - /* This could be a BUG_ON() or a WARN_ON(), or remove it completely */ - if (!icd->dev.parent || - to_soc_camera_host(icd->dev.parent)->nr != icd->iface) - return -ENODEV; + /* We must have a parent by now. And it cannot be a wrong one. */ + BUG_ON(!icd->parent || + to_soc_camera_host(icd->parent)->nr != icd->iface); /* Read out the chip version register */ data1 = reg_read(client, RJ54N1_DEV_CODE); diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 152a65993286..f86550703f8e 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -206,7 +206,7 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) if (2 != success) { - dev_warn(&icd->dev, "soft reset time out\n"); + dev_warn(icd->pdev, "soft reset time out\n"); return -EIO; } @@ -221,7 +221,7 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, unsigned long sizes[], void *alloc_ctxs[]) { struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); @@ -243,7 +243,7 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, *count = pcdev->video_limit / PAGE_ALIGN(sizes[0]); } - dev_dbg(icd->dev.parent, "count=%d, size=%lu\n", *count, sizes[0]); + dev_dbg(icd->parent, "count=%d, size=%lu\n", *count, sizes[0]); return 0; } @@ -352,7 +352,7 @@ static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb) buf = to_ceu_vb(vb); - dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); /* Added list head initialization on alloc */ @@ -372,7 +372,7 @@ static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb) size = icd->user_height * bytes_per_line; if (vb2_plane_size(vb, 0) < size) { - dev_err(icd->dev.parent, "Buffer too small (%lu < %lu)\n", + dev_err(icd->parent, "Buffer too small (%lu < %lu)\n", vb2_plane_size(vb, 0), size); return -ENOBUFS; } @@ -385,11 +385,11 @@ static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb) static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) { struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); - dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); spin_lock_irq(&pcdev->lock); @@ -410,7 +410,7 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) { struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); struct sh_mobile_ceu_dev *pcdev = ici->priv; @@ -438,7 +438,7 @@ static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q) { struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; struct list_head *buf_head, *tmp; @@ -517,7 +517,7 @@ static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev) /* Called with .video_lock held */ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; struct v4l2_subdev *csi2_sd; int ret; @@ -525,7 +525,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) if (pcdev->icd) return -EBUSY; - dev_info(icd->dev.parent, + dev_info(icd->parent, "SuperH Mobile CEU driver attached to camera %d\n", icd->devnum); @@ -549,7 +549,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) /* Called with .video_lock held */ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; struct v4l2_subdev *csi2_sd = find_csi2(pcdev); @@ -571,7 +571,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) pm_runtime_put_sync(ici->v4l2_dev.dev); - dev_info(icd->dev.parent, + dev_info(icd->parent, "SuperH Mobile CEU driver detached from camera %d\n", icd->devnum); @@ -611,14 +611,14 @@ static u16 calc_scale(unsigned int src, unsigned int *dst) /* rect is guaranteed to not exceed the scaled camera rectangle */ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_cam *cam = icd->host_priv; struct sh_mobile_ceu_dev *pcdev = ici->priv; unsigned int height, width, cdwdr_width, in_width, in_height; unsigned int left_offset, top_offset; u32 camor; - dev_geo(icd->dev.parent, "Crop %ux%u@%u:%u\n", + dev_geo(icd->parent, "Crop %ux%u@%u:%u\n", icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top); left_offset = cam->ceu_left; @@ -675,7 +675,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ camor = left_offset | (top_offset << 16); - dev_geo(icd->dev.parent, + dev_geo(icd->parent, "CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor, (in_height << 16) | in_width, (height << 16) | width, cdwdr_width); @@ -723,7 +723,7 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr) static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; int ret; unsigned long camera_flags, common_flags, value; @@ -832,7 +832,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, sh_mobile_ceu_set_rect(icd); mdelay(1); - dev_geo(icd->dev.parent, "CFLCR 0x%x\n", pcdev->cflcr); + dev_geo(icd->parent, "CFLCR 0x%x\n", pcdev->cflcr); ceu_write(pcdev, CFLCR, pcdev->cflcr); /* @@ -855,7 +855,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, ceu_write(pcdev, CDOCR, value); ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ - dev_dbg(icd->dev.parent, "S_FMT successful for %c%c%c%c %ux%u\n", + dev_dbg(icd->parent, "S_FMT successful for %c%c%c%c %ux%u\n", pixfmt & 0xff, (pixfmt >> 8) & 0xff, (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff, icd->user_width, icd->user_height); @@ -869,7 +869,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, unsigned char buswidth) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; unsigned long camera_flags, common_flags; @@ -927,7 +927,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int struct soc_camera_format_xlate *xlate) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->dev.parent; + struct device *dev = icd->parent; struct soc_camera_host *ici = to_soc_camera_host(dev); struct sh_mobile_ceu_dev *pcdev = ici->priv; int ret, k, n; @@ -1270,7 +1270,7 @@ static int client_s_fmt(struct soc_camera_device *icd, { struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->dev.parent; + struct device *dev = icd->parent; unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; unsigned int max_width, max_height; struct v4l2_cropcap cap; @@ -1339,7 +1339,7 @@ static int client_scale(struct soc_camera_device *icd, bool ceu_can_scale) { struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct device *dev = icd->dev.parent; + struct device *dev = icd->parent; struct v4l2_mbus_framefmt mf_tmp = *mf; unsigned int scale_h, scale_v; int ret; @@ -1389,13 +1389,13 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, struct v4l2_crop *a) { struct v4l2_rect *rect = &a->c; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->parent; + struct soc_camera_host *ici = to_soc_camera_host(dev); struct sh_mobile_ceu_dev *pcdev = ici->priv; struct v4l2_crop cam_crop; struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_rect *cam_rect = &cam_crop.c; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->dev.parent; struct v4l2_mbus_framefmt mf; unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v, out_width, out_height; @@ -1537,7 +1537,7 @@ static void calculate_client_output(struct soc_camera_device *icd, struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf) { struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct device *dev = icd->dev.parent; + struct device *dev = icd->parent; struct v4l2_rect *cam_subrect = &cam->subrect; unsigned int scale_v, scale_h; @@ -1581,12 +1581,12 @@ static void calculate_client_output(struct soc_camera_device *icd, static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->parent; + struct soc_camera_host *ici = to_soc_camera_host(dev); struct sh_mobile_ceu_dev *pcdev = ici->priv; struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_mbus_framefmt mf; - struct device *dev = icd->dev.parent; __u32 pixfmt = pix->pixelformat; const struct soc_camera_format_xlate *xlate; /* Keep Compiler Happy */ @@ -1710,12 +1710,12 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, int width, height; int ret; - dev_geo(icd->dev.parent, "TRY_FMT(pix=0x%x, %ux%u)\n", + dev_geo(icd->parent, "TRY_FMT(pix=0x%x, %ux%u)\n", pixfmt, pix->width, pix->height); xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); + dev_warn(icd->parent, "Format %x not found\n", pixfmt); return -EINVAL; } @@ -1762,7 +1762,7 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, try_mbus_fmt, &mf); if (ret < 0) { /* Shouldn't actually happen... */ - dev_err(icd->dev.parent, + dev_err(icd->parent, "FIXME: client try_fmt() = %d\n", ret); return ret; } @@ -1774,7 +1774,7 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, pix->height = height; } - dev_geo(icd->dev.parent, "%s(): return %d, fmt 0x%x, %ux%u\n", + dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n", __func__, ret, pix->pixelformat, pix->width, pix->height); return ret; @@ -1784,7 +1784,7 @@ static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd, struct v4l2_crop *a) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; u32 out_width = icd->user_width, out_height = icd->user_height; int ret; @@ -1796,13 +1796,13 @@ static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd, /* Stop the client */ ret = v4l2_subdev_call(sd, video, s_stream, 0); if (ret < 0) - dev_warn(icd->dev.parent, + dev_warn(icd->parent, "Client failed to stop the stream: %d\n", ret); else /* Do the crop, if it fails, there's nothing more we can do */ sh_mobile_ceu_set_crop(icd, a); - dev_geo(icd->dev.parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height); + dev_geo(icd->parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height); if (icd->user_width != out_width || icd->user_height != out_height) { struct v4l2_format f = { @@ -1868,7 +1868,7 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd, struct v4l2_control *ctrl) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; u32 val; @@ -1884,7 +1884,7 @@ static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd, static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd, struct v4l2_control *ctrl) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; switch (ctrl->id) { diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 0df31b54e906..5bdfe7e16bc1 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -60,14 +60,14 @@ static int soc_camera_power_set(struct soc_camera_device *icd, ret = regulator_bulk_enable(icl->num_regulators, icl->regulators); if (ret < 0) { - dev_err(&icd->dev, "Cannot enable regulators\n"); + dev_err(icd->pdev, "Cannot enable regulators\n"); return ret; } if (icl->power) ret = icl->power(icd->pdev, power_on); if (ret < 0) { - dev_err(&icd->dev, + dev_err(icd->pdev, "Platform failed to power-on the camera.\n"); regulator_bulk_disable(icl->num_regulators, @@ -79,7 +79,7 @@ static int soc_camera_power_set(struct soc_camera_device *icd, if (icl->power) ret = icl->power(icd->pdev, 0); if (ret < 0) { - dev_err(&icd->dev, + dev_err(icd->pdev, "Platform failed to power-off the camera.\n"); return ret; } @@ -87,7 +87,7 @@ static int soc_camera_power_set(struct soc_camera_device *icd, ret = regulator_bulk_disable(icl->num_regulators, icl->regulators); if (ret < 0) { - dev_err(&icd->dev, "Cannot disable regulators\n"); + dev_err(icd->pdev, "Cannot disable regulators\n"); return ret; } } @@ -147,11 +147,11 @@ EXPORT_SYMBOL(soc_camera_apply_sensor_flags); static int soc_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct v4l2_pix_format *pix = &f->fmt.pix; int ret; - dev_dbg(&icd->dev, "TRY_FMT(%c%c%c%c, %ux%u)\n", + dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n", pixfmtstr(pix->pixelformat), pix->width, pix->height); pix->bytesperline = 0; @@ -237,7 +237,7 @@ static int soc_camera_enum_fsizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); return ici->ops->enum_fsizes(icd, fsize); } @@ -247,7 +247,7 @@ static int soc_camera_reqbufs(struct file *file, void *priv, { int ret; struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); WARN_ON(priv != file->private_data); @@ -274,7 +274,7 @@ static int soc_camera_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); WARN_ON(priv != file->private_data); @@ -288,7 +288,7 @@ static int soc_camera_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); WARN_ON(priv != file->private_data); @@ -305,7 +305,7 @@ static int soc_camera_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); WARN_ON(priv != file->private_data); @@ -322,7 +322,7 @@ static int soc_camera_dqbuf(struct file *file, void *priv, static int soc_camera_init_user_formats(struct soc_camera_device *icd) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); unsigned int i, fmts = 0, raw_fmts = 0; int ret; enum v4l2_mbus_pixelcode code; @@ -356,7 +356,7 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd) if (!icd->user_formats) return -ENOMEM; - dev_dbg(&icd->dev, "Found %d supported formats.\n", fmts); + dev_dbg(icd->pdev, "Found %d supported formats.\n", fmts); /* Second pass - actually fill data formats */ fmts = 0; @@ -388,7 +388,7 @@ egfmt: /* Always entered with .video_lock held */ static void soc_camera_free_user_formats(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); if (ici->ops->put_formats) ici->ops->put_formats(icd); @@ -402,11 +402,11 @@ static void soc_camera_free_user_formats(struct soc_camera_device *icd) static int soc_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct v4l2_pix_format *pix = &f->fmt.pix; int ret; - dev_dbg(&icd->dev, "S_FMT(%c%c%c%c, %ux%u)\n", + dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n", pixfmtstr(pix->pixelformat), pix->width, pix->height); /* We always call try_fmt() before set_fmt() or set_crop() */ @@ -419,7 +419,7 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd, return ret; } else if (!icd->current_fmt || icd->current_fmt->host_fmt->fourcc != pix->pixelformat) { - dev_err(&icd->dev, + dev_err(icd->pdev, "Host driver hasn't set up current format correctly!\n"); return -EINVAL; } @@ -433,7 +433,7 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd, if (ici->ops->init_videobuf) icd->vb_vidq.field = pix->field; - dev_dbg(&icd->dev, "set width: %d height: %d\n", + dev_dbg(icd->pdev, "set width: %d height: %d\n", icd->user_width, icd->user_height); /* set physical bus parameters */ @@ -443,9 +443,7 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd, static int soc_camera_open(struct file *file) { struct video_device *vdev = video_devdata(file); - struct soc_camera_device *icd = container_of(vdev->parent, - struct soc_camera_device, - dev); + struct soc_camera_device *icd = dev_get_drvdata(vdev->parent); struct soc_camera_link *icl = to_soc_camera_link(icd); struct soc_camera_host *ici; int ret; @@ -454,10 +452,10 @@ static int soc_camera_open(struct file *file) /* No device driver attached */ return -ENODEV; - ici = to_soc_camera_host(icd->dev.parent); + ici = to_soc_camera_host(icd->parent); if (!try_module_get(ici->ops->owner)) { - dev_err(&icd->dev, "Couldn't lock capture bus driver.\n"); + dev_err(icd->pdev, "Couldn't lock capture bus driver.\n"); return -EINVAL; } @@ -488,7 +486,7 @@ static int soc_camera_open(struct file *file) ret = ici->ops->add(icd); if (ret < 0) { - dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); + dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); goto eiciadd; } @@ -517,7 +515,7 @@ static int soc_camera_open(struct file *file) } file->private_data = icd; - dev_dbg(&icd->dev, "camera device open\n"); + dev_dbg(icd->pdev, "camera device open\n"); return 0; @@ -542,7 +540,7 @@ epower: static int soc_camera_close(struct file *file) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); icd->use_count--; if (!icd->use_count) { @@ -563,7 +561,7 @@ static int soc_camera_close(struct file *file) module_put(ici->ops->owner); - dev_dbg(&icd->dev, "camera device close\n"); + dev_dbg(icd->pdev, "camera device close\n"); return 0; } @@ -574,7 +572,7 @@ static ssize_t soc_camera_read(struct file *file, char __user *buf, struct soc_camera_device *icd = file->private_data; int err = -EINVAL; - dev_err(&icd->dev, "camera device read not implemented\n"); + dev_err(icd->pdev, "camera device read not implemented\n"); return err; } @@ -582,10 +580,10 @@ static ssize_t soc_camera_read(struct file *file, char __user *buf, static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); int err; - dev_dbg(&icd->dev, "mmap called, vma=0x%08lx\n", (unsigned long)vma); + dev_dbg(icd->pdev, "mmap called, vma=0x%08lx\n", (unsigned long)vma); if (icd->streamer != file) return -EBUSY; @@ -595,7 +593,7 @@ static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) else err = vb2_mmap(&icd->vb2_vidq, vma); - dev_dbg(&icd->dev, "vma start=0x%08lx, size=%ld, ret=%d\n", + dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, err); @@ -606,13 +604,13 @@ static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) static unsigned int soc_camera_poll(struct file *file, poll_table *pt) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); if (icd->streamer != file) return -EBUSY; if (ici->ops->init_videobuf && list_empty(&icd->vb_vidq.stream)) { - dev_err(&icd->dev, "Trying to poll with no queued buffers!\n"); + dev_err(icd->pdev, "Trying to poll with no queued buffers!\n"); return POLLERR; } @@ -652,15 +650,15 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, WARN_ON(priv != file->private_data); if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_warn(&icd->dev, "Wrong buf-type %d\n", f->type); + dev_warn(icd->pdev, "Wrong buf-type %d\n", f->type); return -EINVAL; } if (icd->streamer && icd->streamer != file) return -EBUSY; - if (is_streaming(to_soc_camera_host(icd->dev.parent), icd)) { - dev_err(&icd->dev, "S_FMT denied: queue initialised\n"); + if (is_streaming(to_soc_camera_host(icd->parent), icd)) { + dev_err(icd->pdev, "S_FMT denied: queue initialised\n"); return -EBUSY; } @@ -709,7 +707,7 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, pix->field = icd->field; pix->pixelformat = icd->current_fmt->host_fmt->fourcc; pix->colorspace = icd->colorspace; - dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n", + dev_dbg(icd->pdev, "current_fmt->fourcc: 0x%08x\n", icd->current_fmt->host_fmt->fourcc); return 0; } @@ -718,7 +716,7 @@ static int soc_camera_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); WARN_ON(priv != file->private_data); @@ -730,7 +728,7 @@ static int soc_camera_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct v4l2_subdev *sd = soc_camera_to_subdev(icd); int ret; @@ -759,7 +757,7 @@ static int soc_camera_streamoff(struct file *file, void *priv, { struct soc_camera_device *icd = file->private_data; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); WARN_ON(priv != file->private_data); @@ -787,7 +785,7 @@ static int soc_camera_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); int i; WARN_ON(priv != file->private_data); @@ -818,7 +816,7 @@ static int soc_camera_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct v4l2_subdev *sd = soc_camera_to_subdev(icd); int ret; @@ -837,7 +835,7 @@ static int soc_camera_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct v4l2_subdev *sd = soc_camera_to_subdev(icd); int ret; @@ -856,7 +854,7 @@ static int soc_camera_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); return ici->ops->cropcap(icd, a); } @@ -865,7 +863,7 @@ static int soc_camera_g_crop(struct file *file, void *fh, struct v4l2_crop *a) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); int ret; ret = ici->ops->get_crop(icd, a); @@ -882,7 +880,7 @@ static int soc_camera_s_crop(struct file *file, void *fh, struct v4l2_crop *a) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct v4l2_rect *rect = &a->c; struct v4l2_crop current_crop; int ret; @@ -890,7 +888,7 @@ static int soc_camera_s_crop(struct file *file, void *fh, if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - dev_dbg(&icd->dev, "S_CROP(%ux%u@%u:%u)\n", + dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, rect->left, rect->top); /* If get_crop fails, we'll let host and / or client drivers decide */ @@ -898,7 +896,7 @@ static int soc_camera_s_crop(struct file *file, void *fh, /* Prohibit window size change with initialised buffers */ if (ret < 0) { - dev_err(&icd->dev, + dev_err(icd->pdev, "S_CROP denied: getting current crop failed\n"); } else if ((a->c.width == current_crop.c.width && a->c.height == current_crop.c.height) || @@ -908,7 +906,7 @@ static int soc_camera_s_crop(struct file *file, void *fh, } else if (ici->ops->set_livecrop) { ret = ici->ops->set_livecrop(icd, a); } else { - dev_err(&icd->dev, + dev_err(icd->pdev, "S_CROP denied: queue initialised and sizes differ\n"); ret = -EBUSY; } @@ -920,7 +918,7 @@ static int soc_camera_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); if (ici->ops->get_parm) return ici->ops->get_parm(icd, a); @@ -932,7 +930,7 @@ static int soc_camera_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); if (ici->ops->set_parm) return ici->ops->set_parm(icd, a); @@ -969,6 +967,8 @@ static int soc_camera_s_register(struct file *file, void *fh, } #endif +static int soc_camera_probe(struct soc_camera_device *icd); + /* So far this function cannot fail */ static void scan_add_host(struct soc_camera_host *ici) { @@ -979,15 +979,9 @@ static void scan_add_host(struct soc_camera_host *ici) list_for_each_entry(icd, &devices, list) { if (icd->iface == ici->nr) { int ret; - icd->dev.parent = ici->v4l2_dev.dev; - dev_set_name(&icd->dev, "%u-%u", icd->iface, - icd->devnum); - ret = device_register(&icd->dev); - if (ret < 0) { - icd->dev.parent = NULL; - dev_err(&icd->dev, - "Cannot register device: %d\n", ret); - } + + icd->parent = ici->v4l2_dev.dev; + ret = soc_camera_probe(icd); } } @@ -999,12 +993,12 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd, struct soc_camera_link *icl) { struct i2c_client *client; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id); struct v4l2_subdev *subdev; if (!adap) { - dev_err(&icd->dev, "Cannot get I2C adapter #%d. No driver?\n", + dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n", icl->i2c_adapter_id); goto ei2cga; } @@ -1019,7 +1013,7 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd, client = v4l2_get_subdevdata(subdev); /* Use to_i2c_client(dev) to recover the i2c client */ - dev_set_drvdata(&icd->dev, &client->dev); + icd->control = &client->dev; return 0; ei2cnd: @@ -1033,7 +1027,8 @@ static void soc_camera_free_i2c(struct soc_camera_device *icd) struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); struct i2c_adapter *adap = client->adapter; - dev_set_drvdata(&icd->dev, NULL); + + icd->control = NULL; v4l2_device_unregister_subdev(i2c_get_clientdata(client)); i2c_unregister_device(client); i2c_put_adapter(adap); @@ -1046,17 +1041,16 @@ static void soc_camera_free_i2c(struct soc_camera_device *icd) static int soc_camera_video_start(struct soc_camera_device *icd); static int video_dev_create(struct soc_camera_device *icd); /* Called during host-driver probe */ -static int soc_camera_probe(struct device *dev) +static int soc_camera_probe(struct soc_camera_device *icd) { - struct soc_camera_device *icd = to_soc_camera_dev(dev); - struct soc_camera_host *ici = to_soc_camera_host(dev->parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct soc_camera_link *icl = to_soc_camera_link(icd); struct device *control = NULL; struct v4l2_subdev *sd; struct v4l2_mbus_framefmt mf; int ret; - dev_info(dev, "Probing %s\n", dev_name(dev)); + dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev)); ret = regulator_bulk_get(icd->pdev, icl->num_regulators, icl->regulators); @@ -1092,7 +1086,7 @@ static int soc_camera_probe(struct device *dev) if (icl->module_name) ret = request_module(icl->module_name); - ret = icl->add_device(icl, &icd->dev); + ret = icl->add_device(icd); if (ret < 0) goto eadddev; @@ -1103,7 +1097,7 @@ static int soc_camera_probe(struct device *dev) control = to_soc_camera_control(icd); if (!control || !control->driver || !dev_get_drvdata(control) || !try_module_get(control->driver->owner)) { - icl->del_device(icl); + icl->del_device(icd); goto enodrv; } } @@ -1137,11 +1131,6 @@ static int soc_camera_probe(struct device *dev) icd->field = mf.field; } - /* Do we have to sysfs_remove_link() before device_unregister()? */ - if (sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj, - "control")) - dev_warn(&icd->dev, "Failed creating the control symlink\n"); - ici->ops->remove(icd); soc_camera_power_set(icd, icl, 0); @@ -1157,7 +1146,7 @@ eiufmt: if (icl->board_info) { soc_camera_free_i2c(icd); } else { - icl->del_device(icl); + icl->del_device(icd); module_put(control->driver->owner); } enodrv: @@ -1177,13 +1166,12 @@ ereg: * This is called on device_unregister, which only means we have to disconnect * from the host, but not remove ourselves from the device list */ -static int soc_camera_remove(struct device *dev) +static int soc_camera_remove(struct soc_camera_device *icd) { - struct soc_camera_device *icd = to_soc_camera_dev(dev); struct soc_camera_link *icl = to_soc_camera_link(icd); struct video_device *vdev = icd->vdev; - BUG_ON(!dev->parent); + BUG_ON(!icd->parent); if (vdev) { video_unregister_device(vdev); @@ -1193,10 +1181,9 @@ static int soc_camera_remove(struct device *dev) if (icl->board_info) { soc_camera_free_i2c(icd); } else { - struct device_driver *drv = to_soc_camera_control(icd) ? - to_soc_camera_control(icd)->driver : NULL; + struct device_driver *drv = to_soc_camera_control(icd)->driver; if (drv) { - icl->del_device(icl); + icl->del_device(icd); module_put(drv->owner); } } @@ -1207,22 +1194,6 @@ static int soc_camera_remove(struct device *dev) return 0; } -static struct bus_type soc_camera_bus_type = { - .name = "soc-camera", - .probe = soc_camera_probe, - .remove = soc_camera_remove, -}; - -static struct device_driver ic_drv = { - .name = "camera", - .bus = &soc_camera_bus_type, - .owner = THIS_MODULE, -}; - -static void dummy_release(struct device *dev) -{ -} - static int default_cropcap(struct soc_camera_device *icd, struct v4l2_cropcap *a) { @@ -1281,13 +1252,6 @@ static int default_enum_fsizes(struct soc_camera_device *icd, return 0; } -static void soc_camera_device_init(struct device *dev, void *pdata) -{ - dev->platform_data = pdata; - dev->bus = &soc_camera_bus_type; - dev->release = dummy_release; -} - int soc_camera_host_register(struct soc_camera_host *ici) { struct soc_camera_host *ix; @@ -1353,24 +1317,9 @@ void soc_camera_host_unregister(struct soc_camera_host *ici) mutex_lock(&list_lock); list_del(&ici->list); - - list_for_each_entry(icd, &devices, list) { - if (icd->iface == ici->nr) { - void *pdata = icd->dev.platform_data; - /* The bus->remove will be called */ - device_unregister(&icd->dev); - /* - * Not before device_unregister(), .remove - * needs parent to call ici->ops->remove(). - * If the host module is loaded again, device_register() - * would complain "already initialised," since 2.6.32 - * this is also needed to prevent use-after-free of the - * device private data. - */ - memset(&icd->dev, 0, sizeof(icd->dev)); - soc_camera_device_init(&icd->dev, pdata); - } - } + list_for_each_entry(icd, &devices, list) + if (icd->iface == ici->nr && to_soc_camera_control(icd)) + soc_camera_remove(icd); mutex_unlock(&list_lock); @@ -1412,11 +1361,6 @@ static int soc_camera_device_register(struct soc_camera_device *icd) return 0; } -static void soc_camera_device_unregister(struct soc_camera_device *icd) -{ - list_del(&icd->list); -} - static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_querycap = soc_camera_querycap, .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap, @@ -1451,7 +1395,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { static int video_dev_create(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct video_device *vdev = video_device_alloc(); if (!vdev) @@ -1459,7 +1403,7 @@ static int video_dev_create(struct soc_camera_device *icd) strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name)); - vdev->parent = &icd->dev; + vdev->parent = icd->pdev; vdev->current_norm = V4L2_STD_UNKNOWN; vdev->fops = &soc_camera_fops; vdev->ioctl_ops = &soc_camera_ioctl_ops; @@ -1480,7 +1424,7 @@ static int soc_camera_video_start(struct soc_camera_device *icd) const struct device_type *type = icd->vdev->dev.type; int ret; - if (!icd->dev.parent) + if (!icd->parent) return -ENODEV; if (!icd->ops || @@ -1490,7 +1434,7 @@ static int soc_camera_video_start(struct soc_camera_device *icd) ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { - dev_err(&icd->dev, "video_register_device failed: %d\n", ret); + dev_err(icd->pdev, "video_register_device failed: %d\n", ret); return ret; } @@ -1514,6 +1458,7 @@ static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev) return -ENOMEM; icd->iface = icl->bus_id; + icd->link = icl; icd->pdev = &pdev->dev; platform_set_drvdata(pdev, icd); @@ -1521,8 +1466,6 @@ static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev) if (ret < 0) goto escdevreg; - soc_camera_device_init(&icd->dev, icl); - icd->user_width = DEFAULT_WIDTH; icd->user_height = DEFAULT_HEIGHT; @@ -1546,7 +1489,7 @@ static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev) if (!icd) return -EINVAL; - soc_camera_device_unregister(icd); + list_del(&icd->list); kfree(icd); @@ -1563,31 +1506,12 @@ static struct platform_driver __refdata soc_camera_pdrv = { static int __init soc_camera_init(void) { - int ret = bus_register(&soc_camera_bus_type); - if (ret) - return ret; - ret = driver_register(&ic_drv); - if (ret) - goto edrvr; - - ret = platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe); - if (ret) - goto epdr; - - return 0; - -epdr: - driver_unregister(&ic_drv); -edrvr: - bus_unregister(&soc_camera_bus_type); - return ret; + return platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe); } static void __exit soc_camera_exit(void) { platform_driver_unregister(&soc_camera_pdrv); - driver_unregister(&ic_drv); - bus_unregister(&soc_camera_bus_type); } module_init(soc_camera_init); diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c index bf406e89c992..8069cd6bc5e8 100644 --- a/drivers/media/video/soc_camera_platform.c +++ b/drivers/media/video/soc_camera_platform.c @@ -146,7 +146,7 @@ static int soc_camera_platform_probe(struct platform_device *pdev) if (!p) return -EINVAL; - if (!p->dev) { + if (!p->icd) { dev_err(&pdev->dev, "Platform has not set soc_camera_device pointer!\n"); return -EINVAL; @@ -156,16 +156,16 @@ static int soc_camera_platform_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - icd = to_soc_camera_dev(p->dev); + icd = p->icd; /* soc-camera convention: control's drvdata points to the subdev */ platform_set_drvdata(pdev, &priv->subdev); /* Set the control device reference */ - dev_set_drvdata(&icd->dev, &pdev->dev); + icd->control = &pdev->dev; icd->ops = &soc_camera_platform_ops; - ici = to_soc_camera_host(icd->dev.parent); + ici = to_soc_camera_host(icd->parent); v4l2_subdev_init(&priv->subdev, &platform_subdev_ops); v4l2_set_subdevdata(&priv->subdev, p); @@ -188,7 +188,7 @@ static int soc_camera_platform_remove(struct platform_device *pdev) { struct soc_camera_platform_priv *priv = get_priv(pdev); struct soc_camera_platform_info *p = pdev->dev.platform_data; - struct soc_camera_device *icd = to_soc_camera_dev(p->dev); + struct soc_camera_device *icd = p->icd; v4l2_device_unregister_subdev(&priv->subdev); icd->ops = NULL; diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index a722f66c3f23..742482e30011 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -836,13 +836,9 @@ static int tw9910_video_probe(struct soc_camera_device *icd, struct tw9910_priv *priv = to_tw9910(client); s32 id; - /* - * We must have a parent by now. And it cannot be a wrong one. - * So this entire test is completely redundant. - */ - if (!icd->dev.parent || - to_soc_camera_host(icd->dev.parent)->nr != icd->iface) - return -ENODEV; + /* We must have a parent by now. And it cannot be a wrong one. */ + BUG_ON(!icd->parent || + to_soc_camera_host(icd->parent)->nr != icd->iface); /* * tw9910 only use 8 or 16 bit bus width diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index c31d55be3e17..7582952dceae 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -21,11 +21,14 @@ #include struct file; +struct soc_camera_link; struct soc_camera_device { struct list_head list; /* list of all registered devices */ - struct device dev; + struct soc_camera_link *link; struct device *pdev; /* Platform device */ + struct device *parent; /* Camera host device */ + struct device *control; /* E.g., the i2c client */ s32 user_width; s32 user_height; u32 bytesperline; /* for padding, zero if unused */ @@ -127,8 +130,8 @@ struct soc_camera_link { * For non-I2C devices platform has to provide methods to add a device * to the system and to remove it */ - int (*add_device)(struct soc_camera_link *, struct device *); - void (*del_device)(struct soc_camera_link *); + int (*add_device)(struct soc_camera_device *); + void (*del_device)(struct soc_camera_device *); /* Optional callbacks to power on or off and reset the sensor */ int (*power)(struct device *, int); int (*reset)(struct device *); @@ -142,12 +145,6 @@ struct soc_camera_link { void (*free_bus)(struct soc_camera_link *); }; -static inline struct soc_camera_device *to_soc_camera_dev( - const struct device *dev) -{ - return container_of(dev, struct soc_camera_device, dev); -} - static inline struct soc_camera_host *to_soc_camera_host( const struct device *dev) { @@ -159,13 +156,13 @@ static inline struct soc_camera_host *to_soc_camera_host( static inline struct soc_camera_link *to_soc_camera_link( const struct soc_camera_device *icd) { - return icd->dev.platform_data; + return icd->link; } static inline struct device *to_soc_camera_control( const struct soc_camera_device *icd) { - return dev_get_drvdata(&icd->dev); + return icd->control; } static inline struct v4l2_subdev *soc_camera_to_subdev( diff --git a/include/media/soc_camera_platform.h b/include/media/soc_camera_platform.h index 6d7a4fd00fc0..74f0fa15ca47 100644 --- a/include/media/soc_camera_platform.h +++ b/include/media/soc_camera_platform.h @@ -21,7 +21,7 @@ struct soc_camera_platform_info { unsigned long format_depth; struct v4l2_mbus_framefmt format; unsigned long bus_param; - struct device *dev; + struct soc_camera_device *icd; int (*set_capture)(struct soc_camera_platform_info *info, int enable); }; @@ -30,8 +30,7 @@ static inline void soc_camera_platform_release(struct platform_device **pdev) *pdev = NULL; } -static inline int soc_camera_platform_add(const struct soc_camera_link *icl, - struct device *dev, +static inline int soc_camera_platform_add(struct soc_camera_device *icd, struct platform_device **pdev, struct soc_camera_link *plink, void (*release)(struct device *dev), @@ -40,7 +39,7 @@ static inline int soc_camera_platform_add(const struct soc_camera_link *icl, struct soc_camera_platform_info *info = plink->priv; int ret; - if (icl != plink) + if (icd->link != plink) return -ENODEV; if (*pdev) @@ -50,7 +49,7 @@ static inline int soc_camera_platform_add(const struct soc_camera_link *icl, if (!*pdev) return -ENOMEM; - info->dev = dev; + info->icd = icd; (*pdev)->dev.platform_data = info; (*pdev)->dev.release = release; @@ -59,17 +58,17 @@ static inline int soc_camera_platform_add(const struct soc_camera_link *icl, if (ret < 0) { platform_device_put(*pdev); *pdev = NULL; - info->dev = NULL; + info->icd = NULL; } return ret; } -static inline void soc_camera_platform_del(const struct soc_camera_link *icl, +static inline void soc_camera_platform_del(const struct soc_camera_device *icd, struct platform_device *pdev, const struct soc_camera_link *plink) { - if (icl != plink || !pdev) + if (icd->link != plink || !pdev) return; platform_device_unregister(pdev); -- cgit v1.2.3 From b52a851d9182e8a20704152d537c7e19ed34cc99 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Fri, 24 Jun 2011 07:57:36 -0300 Subject: [media] V4L: initial driver for ov5642 CMOS sensor This is an initial driver release for the Omnivision 5642 CMOS sensor. Signed-off-by: Bastian Hecht Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 6 + drivers/media/video/Makefile | 1 + drivers/media/video/ov5642.c | 1011 +++++++++++++++++++++++++++++++++++++++ include/media/v4l2-chip-ident.h | 1 + 4 files changed, 1019 insertions(+) create mode 100644 drivers/media/video/ov5642.c (limited to 'include') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 176ac49e9c73..f574dc012cad 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -848,6 +848,12 @@ config SOC_CAMERA_OV2640 help This is a ov2640 camera driver +config SOC_CAMERA_OV5642 + tristate "ov5642 camera support" + depends on SOC_CAMERA && I2C + help + This is a V4L2 camera driver for the OmniVision OV5642 sensor + config SOC_CAMERA_OV6650 tristate "ov6650 sensor support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 6cca52a7e2dd..272390072aef 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -79,6 +79,7 @@ obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o +obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c new file mode 100644 index 000000000000..3cdae971dfa5 --- /dev/null +++ b/drivers/media/video/ov5642.c @@ -0,0 +1,1011 @@ +/* + * Driver for OV5642 CMOS Image Sensor from Omnivision + * + * Copyright (C) 2011, Bastian Hecht + * + * Based on Sony IMX074 Camera Driver + * Copyright (C) 2010, Guennadi Liakhovetski + * + * Based on Omnivision OV7670 Camera Driver + * Copyright (C) 2006-7 Jonathan Corbet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +/* OV5642 registers */ +#define REG_CHIP_ID_HIGH 0x300a +#define REG_CHIP_ID_LOW 0x300b + +#define REG_WINDOW_START_X_HIGH 0x3800 +#define REG_WINDOW_START_X_LOW 0x3801 +#define REG_WINDOW_START_Y_HIGH 0x3802 +#define REG_WINDOW_START_Y_LOW 0x3803 +#define REG_WINDOW_WIDTH_HIGH 0x3804 +#define REG_WINDOW_WIDTH_LOW 0x3805 +#define REG_WINDOW_HEIGHT_HIGH 0x3806 +#define REG_WINDOW_HEIGHT_LOW 0x3807 +#define REG_OUT_WIDTH_HIGH 0x3808 +#define REG_OUT_WIDTH_LOW 0x3809 +#define REG_OUT_HEIGHT_HIGH 0x380a +#define REG_OUT_HEIGHT_LOW 0x380b +#define REG_OUT_TOTAL_WIDTH_HIGH 0x380c +#define REG_OUT_TOTAL_WIDTH_LOW 0x380d +#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e +#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f + +/* + * define standard resolution. + * Works currently only for up to 720 lines + * eg. 320x240, 640x480, 800x600, 1280x720, 2048x720 + */ + +#define OV5642_WIDTH 1280 +#define OV5642_HEIGHT 720 +#define OV5642_TOTAL_WIDTH 3200 +#define OV5642_TOTAL_HEIGHT 2000 +#define OV5642_SENSOR_SIZE_X 2592 +#define OV5642_SENSOR_SIZE_Y 1944 + +struct regval_list { + u16 reg_num; + u8 value; +}; + +static struct regval_list ov5642_default_regs_init[] = { + { 0x3103, 0x93 }, + { 0x3008, 0x82 }, + { 0x3017, 0x7f }, + { 0x3018, 0xfc }, + { 0x3810, 0xc2 }, + { 0x3615, 0xf0 }, + { 0x3000, 0x0 }, + { 0x3001, 0x0 }, + { 0x3002, 0x0 }, + { 0x3003, 0x0 }, + { 0x3004, 0xff }, + { 0x3030, 0x2b }, + { 0x3011, 0x8 }, + { 0x3010, 0x10 }, + { 0x3604, 0x60 }, + { 0x3622, 0x60 }, + { 0x3621, 0x9 }, + { 0x3709, 0x0 }, + { 0x4000, 0x21 }, + { 0x401d, 0x22 }, + { 0x3600, 0x54 }, + { 0x3605, 0x4 }, + { 0x3606, 0x3f }, + { 0x3c01, 0x80 }, + { 0x300d, 0x22 }, + { 0x3623, 0x22 }, + { 0x5000, 0x4f }, + { 0x5020, 0x4 }, + { 0x5181, 0x79 }, + { 0x5182, 0x0 }, + { 0x5185, 0x22 }, + { 0x5197, 0x1 }, + { 0x5500, 0xa }, + { 0x5504, 0x0 }, + { 0x5505, 0x7f }, + { 0x5080, 0x8 }, + { 0x300e, 0x18 }, + { 0x4610, 0x0 }, + { 0x471d, 0x5 }, + { 0x4708, 0x6 }, + { 0x370c, 0xa0 }, + { 0x5687, 0x94 }, + { 0x501f, 0x0 }, + { 0x5000, 0x4f }, + { 0x5001, 0xcf }, + { 0x4300, 0x30 }, + { 0x4300, 0x30 }, + { 0x460b, 0x35 }, + { 0x471d, 0x0 }, + { 0x3002, 0xc }, + { 0x3002, 0x0 }, + { 0x4713, 0x3 }, + { 0x471c, 0x50 }, + { 0x4721, 0x2 }, + { 0x4402, 0x90 }, + { 0x460c, 0x22 }, + { 0x3815, 0x44 }, + { 0x3503, 0x7 }, + { 0x3501, 0x73 }, + { 0x3502, 0x80 }, + { 0x350b, 0x0 }, + { 0x3818, 0xc8 }, + { 0x3824, 0x11 }, + { 0x3a00, 0x78 }, + { 0x3a1a, 0x4 }, + { 0x3a13, 0x30 }, + { 0x3a18, 0x0 }, + { 0x3a19, 0x7c }, + { 0x3a08, 0x12 }, + { 0x3a09, 0xc0 }, + { 0x3a0a, 0xf }, + { 0x3a0b, 0xa0 }, + { 0x350c, 0x7 }, + { 0x350d, 0xd0 }, + { 0x3a0d, 0x8 }, + { 0x3a0e, 0x6 }, + { 0x3500, 0x0 }, + { 0x3501, 0x0 }, + { 0x3502, 0x0 }, + { 0x350a, 0x0 }, + { 0x350b, 0x0 }, + { 0x3503, 0x0 }, + { 0x3a0f, 0x3c }, + { 0x3a10, 0x32 }, + { 0x3a1b, 0x3c }, + { 0x3a1e, 0x32 }, + { 0x3a11, 0x80 }, + { 0x3a1f, 0x20 }, + { 0x3030, 0x2b }, + { 0x3a02, 0x0 }, + { 0x3a03, 0x7d }, + { 0x3a04, 0x0 }, + { 0x3a14, 0x0 }, + { 0x3a15, 0x7d }, + { 0x3a16, 0x0 }, + { 0x3a00, 0x78 }, + { 0x3a08, 0x9 }, + { 0x3a09, 0x60 }, + { 0x3a0a, 0x7 }, + { 0x3a0b, 0xd0 }, + { 0x3a0d, 0x10 }, + { 0x3a0e, 0xd }, + { 0x4407, 0x4 }, + { 0x5193, 0x70 }, + { 0x589b, 0x0 }, + { 0x589a, 0xc0 }, + { 0x401e, 0x20 }, + { 0x4001, 0x42 }, + { 0x401c, 0x6 }, + { 0x3825, 0xac }, + { 0x3827, 0xc }, + { 0x528a, 0x1 }, + { 0x528b, 0x4 }, + { 0x528c, 0x8 }, + { 0x528d, 0x10 }, + { 0x528e, 0x20 }, + { 0x528f, 0x28 }, + { 0x5290, 0x30 }, + { 0x5292, 0x0 }, + { 0x5293, 0x1 }, + { 0x5294, 0x0 }, + { 0x5295, 0x4 }, + { 0x5296, 0x0 }, + { 0x5297, 0x8 }, + { 0x5298, 0x0 }, + { 0x5299, 0x10 }, + { 0x529a, 0x0 }, + { 0x529b, 0x20 }, + { 0x529c, 0x0 }, + { 0x529d, 0x28 }, + { 0x529e, 0x0 }, + { 0x529f, 0x30 }, + { 0x5282, 0x0 }, + { 0x5300, 0x0 }, + { 0x5301, 0x20 }, + { 0x5302, 0x0 }, + { 0x5303, 0x7c }, + { 0x530c, 0x0 }, + { 0x530d, 0xc }, + { 0x530e, 0x20 }, + { 0x530f, 0x80 }, + { 0x5310, 0x20 }, + { 0x5311, 0x80 }, + { 0x5308, 0x20 }, + { 0x5309, 0x40 }, + { 0x5304, 0x0 }, + { 0x5305, 0x30 }, + { 0x5306, 0x0 }, + { 0x5307, 0x80 }, + { 0x5314, 0x8 }, + { 0x5315, 0x20 }, + { 0x5319, 0x30 }, + { 0x5316, 0x10 }, + { 0x5317, 0x0 }, + { 0x5318, 0x2 }, + { 0x5380, 0x1 }, + { 0x5381, 0x0 }, + { 0x5382, 0x0 }, + { 0x5383, 0x4e }, + { 0x5384, 0x0 }, + { 0x5385, 0xf }, + { 0x5386, 0x0 }, + { 0x5387, 0x0 }, + { 0x5388, 0x1 }, + { 0x5389, 0x15 }, + { 0x538a, 0x0 }, + { 0x538b, 0x31 }, + { 0x538c, 0x0 }, + { 0x538d, 0x0 }, + { 0x538e, 0x0 }, + { 0x538f, 0xf }, + { 0x5390, 0x0 }, + { 0x5391, 0xab }, + { 0x5392, 0x0 }, + { 0x5393, 0xa2 }, + { 0x5394, 0x8 }, + { 0x5480, 0x14 }, + { 0x5481, 0x21 }, + { 0x5482, 0x36 }, + { 0x5483, 0x57 }, + { 0x5484, 0x65 }, + { 0x5485, 0x71 }, + { 0x5486, 0x7d }, + { 0x5487, 0x87 }, + { 0x5488, 0x91 }, + { 0x5489, 0x9a }, + { 0x548a, 0xaa }, + { 0x548b, 0xb8 }, + { 0x548c, 0xcd }, + { 0x548d, 0xdd }, + { 0x548e, 0xea }, + { 0x548f, 0x1d }, + { 0x5490, 0x5 }, + { 0x5491, 0x0 }, + { 0x5492, 0x4 }, + { 0x5493, 0x20 }, + { 0x5494, 0x3 }, + { 0x5495, 0x60 }, + { 0x5496, 0x2 }, + { 0x5497, 0xb8 }, + { 0x5498, 0x2 }, + { 0x5499, 0x86 }, + { 0x549a, 0x2 }, + { 0x549b, 0x5b }, + { 0x549c, 0x2 }, + { 0x549d, 0x3b }, + { 0x549e, 0x2 }, + { 0x549f, 0x1c }, + { 0x54a0, 0x2 }, + { 0x54a1, 0x4 }, + { 0x54a2, 0x1 }, + { 0x54a3, 0xed }, + { 0x54a4, 0x1 }, + { 0x54a5, 0xc5 }, + { 0x54a6, 0x1 }, + { 0x54a7, 0xa5 }, + { 0x54a8, 0x1 }, + { 0x54a9, 0x6c }, + { 0x54aa, 0x1 }, + { 0x54ab, 0x41 }, + { 0x54ac, 0x1 }, + { 0x54ad, 0x20 }, + { 0x54ae, 0x0 }, + { 0x54af, 0x16 }, + { 0x54b0, 0x1 }, + { 0x54b1, 0x20 }, + { 0x54b2, 0x0 }, + { 0x54b3, 0x10 }, + { 0x54b4, 0x0 }, + { 0x54b5, 0xf0 }, + { 0x54b6, 0x0 }, + { 0x54b7, 0xdf }, + { 0x5402, 0x3f }, + { 0x5403, 0x0 }, + { 0x3406, 0x0 }, + { 0x5180, 0xff }, + { 0x5181, 0x52 }, + { 0x5182, 0x11 }, + { 0x5183, 0x14 }, + { 0x5184, 0x25 }, + { 0x5185, 0x24 }, + { 0x5186, 0x6 }, + { 0x5187, 0x8 }, + { 0x5188, 0x8 }, + { 0x5189, 0x7c }, + { 0x518a, 0x60 }, + { 0x518b, 0xb2 }, + { 0x518c, 0xb2 }, + { 0x518d, 0x44 }, + { 0x518e, 0x3d }, + { 0x518f, 0x58 }, + { 0x5190, 0x46 }, + { 0x5191, 0xf8 }, + { 0x5192, 0x4 }, + { 0x5193, 0x70 }, + { 0x5194, 0xf0 }, + { 0x5195, 0xf0 }, + { 0x5196, 0x3 }, + { 0x5197, 0x1 }, + { 0x5198, 0x4 }, + { 0x5199, 0x12 }, + { 0x519a, 0x4 }, + { 0x519b, 0x0 }, + { 0x519c, 0x6 }, + { 0x519d, 0x82 }, + { 0x519e, 0x0 }, + { 0x5025, 0x80 }, + { 0x3a0f, 0x38 }, + { 0x3a10, 0x30 }, + { 0x3a1b, 0x3a }, + { 0x3a1e, 0x2e }, + { 0x3a11, 0x60 }, + { 0x3a1f, 0x10 }, + { 0x5688, 0xa6 }, + { 0x5689, 0x6a }, + { 0x568a, 0xea }, + { 0x568b, 0xae }, + { 0x568c, 0xa6 }, + { 0x568d, 0x6a }, + { 0x568e, 0x62 }, + { 0x568f, 0x26 }, + { 0x5583, 0x40 }, + { 0x5584, 0x40 }, + { 0x5580, 0x2 }, + { 0x5000, 0xcf }, + { 0x5800, 0x27 }, + { 0x5801, 0x19 }, + { 0x5802, 0x12 }, + { 0x5803, 0xf }, + { 0x5804, 0x10 }, + { 0x5805, 0x15 }, + { 0x5806, 0x1e }, + { 0x5807, 0x2f }, + { 0x5808, 0x15 }, + { 0x5809, 0xd }, + { 0x580a, 0xa }, + { 0x580b, 0x9 }, + { 0x580c, 0xa }, + { 0x580d, 0xc }, + { 0x580e, 0x12 }, + { 0x580f, 0x19 }, + { 0x5810, 0xb }, + { 0x5811, 0x7 }, + { 0x5812, 0x4 }, + { 0x5813, 0x3 }, + { 0x5814, 0x3 }, + { 0x5815, 0x6 }, + { 0x5816, 0xa }, + { 0x5817, 0xf }, + { 0x5818, 0xa }, + { 0x5819, 0x5 }, + { 0x581a, 0x1 }, + { 0x581b, 0x0 }, + { 0x581c, 0x0 }, + { 0x581d, 0x3 }, + { 0x581e, 0x8 }, + { 0x581f, 0xc }, + { 0x5820, 0xa }, + { 0x5821, 0x5 }, + { 0x5822, 0x1 }, + { 0x5823, 0x0 }, + { 0x5824, 0x0 }, + { 0x5825, 0x3 }, + { 0x5826, 0x8 }, + { 0x5827, 0xc }, + { 0x5828, 0xe }, + { 0x5829, 0x8 }, + { 0x582a, 0x6 }, + { 0x582b, 0x4 }, + { 0x582c, 0x5 }, + { 0x582d, 0x7 }, + { 0x582e, 0xb }, + { 0x582f, 0x12 }, + { 0x5830, 0x18 }, + { 0x5831, 0x10 }, + { 0x5832, 0xc }, + { 0x5833, 0xa }, + { 0x5834, 0xb }, + { 0x5835, 0xe }, + { 0x5836, 0x15 }, + { 0x5837, 0x19 }, + { 0x5838, 0x32 }, + { 0x5839, 0x1f }, + { 0x583a, 0x18 }, + { 0x583b, 0x16 }, + { 0x583c, 0x17 }, + { 0x583d, 0x1e }, + { 0x583e, 0x26 }, + { 0x583f, 0x53 }, + { 0x5840, 0x10 }, + { 0x5841, 0xf }, + { 0x5842, 0xd }, + { 0x5843, 0xc }, + { 0x5844, 0xe }, + { 0x5845, 0x9 }, + { 0x5846, 0x11 }, + { 0x5847, 0x10 }, + { 0x5848, 0x10 }, + { 0x5849, 0x10 }, + { 0x584a, 0x10 }, + { 0x584b, 0xe }, + { 0x584c, 0x10 }, + { 0x584d, 0x10 }, + { 0x584e, 0x11 }, + { 0x584f, 0x10 }, + { 0x5850, 0xf }, + { 0x5851, 0xc }, + { 0x5852, 0xf }, + { 0x5853, 0x10 }, + { 0x5854, 0x10 }, + { 0x5855, 0xf }, + { 0x5856, 0xe }, + { 0x5857, 0xb }, + { 0x5858, 0x10 }, + { 0x5859, 0xd }, + { 0x585a, 0xd }, + { 0x585b, 0xc }, + { 0x585c, 0xc }, + { 0x585d, 0xc }, + { 0x585e, 0xb }, + { 0x585f, 0xc }, + { 0x5860, 0xc }, + { 0x5861, 0xc }, + { 0x5862, 0xd }, + { 0x5863, 0x8 }, + { 0x5864, 0x11 }, + { 0x5865, 0x18 }, + { 0x5866, 0x18 }, + { 0x5867, 0x19 }, + { 0x5868, 0x17 }, + { 0x5869, 0x19 }, + { 0x586a, 0x16 }, + { 0x586b, 0x13 }, + { 0x586c, 0x13 }, + { 0x586d, 0x12 }, + { 0x586e, 0x13 }, + { 0x586f, 0x16 }, + { 0x5870, 0x14 }, + { 0x5871, 0x12 }, + { 0x5872, 0x10 }, + { 0x5873, 0x11 }, + { 0x5874, 0x11 }, + { 0x5875, 0x16 }, + { 0x5876, 0x14 }, + { 0x5877, 0x11 }, + { 0x5878, 0x10 }, + { 0x5879, 0xf }, + { 0x587a, 0x10 }, + { 0x587b, 0x14 }, + { 0x587c, 0x13 }, + { 0x587d, 0x12 }, + { 0x587e, 0x11 }, + { 0x587f, 0x11 }, + { 0x5880, 0x12 }, + { 0x5881, 0x15 }, + { 0x5882, 0x14 }, + { 0x5883, 0x15 }, + { 0x5884, 0x15 }, + { 0x5885, 0x15 }, + { 0x5886, 0x13 }, + { 0x5887, 0x17 }, + { 0x3710, 0x10 }, + { 0x3632, 0x51 }, + { 0x3702, 0x10 }, + { 0x3703, 0xb2 }, + { 0x3704, 0x18 }, + { 0x370b, 0x40 }, + { 0x370d, 0x3 }, + { 0x3631, 0x1 }, + { 0x3632, 0x52 }, + { 0x3606, 0x24 }, + { 0x3620, 0x96 }, + { 0x5785, 0x7 }, + { 0x3a13, 0x30 }, + { 0x3600, 0x52 }, + { 0x3604, 0x48 }, + { 0x3606, 0x1b }, + { 0x370d, 0xb }, + { 0x370f, 0xc0 }, + { 0x3709, 0x1 }, + { 0x3823, 0x0 }, + { 0x5007, 0x0 }, + { 0x5009, 0x0 }, + { 0x5011, 0x0 }, + { 0x5013, 0x0 }, + { 0x519e, 0x0 }, + { 0x5086, 0x0 }, + { 0x5087, 0x0 }, + { 0x5088, 0x0 }, + { 0x5089, 0x0 }, + { 0x302b, 0x0 }, + { 0x3503, 0x7 }, + { 0x3011, 0x8 }, + { 0x350c, 0x2 }, + { 0x350d, 0xe4 }, + { 0x3621, 0xc9 }, + { 0x370a, 0x81 }, + { 0xffff, 0xff }, +}; + +static struct regval_list ov5642_default_regs_finalise[] = { + { 0x3810, 0xc2 }, + { 0x3818, 0xc9 }, + { 0x381c, 0x10 }, + { 0x381d, 0xa0 }, + { 0x381e, 0x5 }, + { 0x381f, 0xb0 }, + { 0x3820, 0x0 }, + { 0x3821, 0x0 }, + { 0x3824, 0x11 }, + { 0x3a08, 0x1b }, + { 0x3a09, 0xc0 }, + { 0x3a0a, 0x17 }, + { 0x3a0b, 0x20 }, + { 0x3a0d, 0x2 }, + { 0x3a0e, 0x1 }, + { 0x401c, 0x4 }, + { 0x5682, 0x5 }, + { 0x5683, 0x0 }, + { 0x5686, 0x2 }, + { 0x5687, 0xcc }, + { 0x5001, 0x4f }, + { 0x589b, 0x6 }, + { 0x589a, 0xc5 }, + { 0x3503, 0x0 }, + { 0x460c, 0x20 }, + { 0x460b, 0x37 }, + { 0x471c, 0xd0 }, + { 0x471d, 0x5 }, + { 0x3815, 0x1 }, + { 0x3818, 0xc1 }, + { 0x501f, 0x0 }, + { 0x5002, 0xe0 }, + { 0x4300, 0x32 }, /* UYVY */ + { 0x3002, 0x1c }, + { 0x4800, 0x14 }, + { 0x4801, 0xf }, + { 0x3007, 0x3b }, + { 0x300e, 0x4 }, + { 0x4803, 0x50 }, + { 0x3815, 0x1 }, + { 0x4713, 0x2 }, + { 0x4842, 0x1 }, + { 0x300f, 0xe }, + { 0x3003, 0x3 }, + { 0x3003, 0x1 }, + { 0xffff, 0xff }, +}; + +struct ov5642_datafmt { + enum v4l2_mbus_pixelcode code; + enum v4l2_colorspace colorspace; +}; + +struct ov5642 { + struct v4l2_subdev subdev; + const struct ov5642_datafmt *fmt; +}; + +static const struct ov5642_datafmt ov5642_colour_fmts[] = { + {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG}, +}; + +static struct ov5642 *to_ov5642(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct ov5642, subdev); +} + +/* Find a data format by a pixel code in an array */ +static const struct ov5642_datafmt + *ov5642_find_datafmt(enum v4l2_mbus_pixelcode code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++) + if (ov5642_colour_fmts[i].code == code) + return ov5642_colour_fmts + i; + + return NULL; +} + +static int reg_read(struct i2c_client *client, u16 reg, u8 *val) +{ + int ret; + /* We have 16-bit i2c addresses - care for endianess */ + unsigned char data[2] = { reg >> 8, reg & 0xff }; + + ret = i2c_master_send(client, data, 2); + if (ret < 2) { + dev_err(&client->dev, "%s: i2c read error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + + ret = i2c_master_recv(client, val, 1); + if (ret < 1) { + dev_err(&client->dev, "%s: i2c read error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + return 0; +} + +static int reg_write(struct i2c_client *client, u16 reg, u8 val) +{ + int ret; + unsigned char data[3] = { reg >> 8, reg & 0xff, val }; + + ret = i2c_master_send(client, data, 3); + if (ret < 3) { + dev_err(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + + return 0; +} +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + u8 val; + + if (reg->reg & ~0xffff) + return -EINVAL; + + reg->size = 1; + + ret = reg_read(client, reg->reg, &val); + if (!ret) + reg->val = (__u64)val; + + return ret; +} + +static int ov5642_set_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg & ~0xffff || reg->val & ~0xff) + return -EINVAL; + + return reg_write(client, reg->reg, reg->val); +} +#endif + +static int ov5642_write_array(struct i2c_client *client, + struct regval_list *vals) +{ + while (vals->reg_num != 0xffff || vals->value != 0xff) { + int ret = reg_write(client, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + } + dev_dbg(&client->dev, "Register list loaded\n"); + return 0; +} + +static int ov5642_set_resolution(struct i2c_client *client) +{ + int ret; + u8 start_x_high = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) >> 8; + u8 start_x_low = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) & 0xff; + u8 start_y_high = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) >> 8; + u8 start_y_low = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) & 0xff; + + u8 width_high = OV5642_WIDTH >> 8; + u8 width_low = OV5642_WIDTH & 0xff; + u8 height_high = OV5642_HEIGHT >> 8; + u8 height_low = OV5642_HEIGHT & 0xff; + + u8 total_width_high = OV5642_TOTAL_WIDTH >> 8; + u8 total_width_low = OV5642_TOTAL_WIDTH & 0xff; + u8 total_height_high = OV5642_TOTAL_HEIGHT >> 8; + u8 total_height_low = OV5642_TOTAL_HEIGHT & 0xff; + + ret = reg_write(client, REG_WINDOW_START_X_HIGH, start_x_high); + if (!ret) + ret = reg_write(client, REG_WINDOW_START_X_LOW, start_x_low); + if (!ret) + ret = reg_write(client, REG_WINDOW_START_Y_HIGH, start_y_high); + if (!ret) + ret = reg_write(client, REG_WINDOW_START_Y_LOW, start_y_low); + + if (!ret) + ret = reg_write(client, REG_WINDOW_WIDTH_HIGH, width_high); + if (!ret) + ret = reg_write(client, REG_WINDOW_WIDTH_LOW , width_low); + if (!ret) + ret = reg_write(client, REG_WINDOW_HEIGHT_HIGH, height_high); + if (!ret) + ret = reg_write(client, REG_WINDOW_HEIGHT_LOW, height_low); + + if (!ret) + ret = reg_write(client, REG_OUT_WIDTH_HIGH, width_high); + if (!ret) + ret = reg_write(client, REG_OUT_WIDTH_LOW , width_low); + if (!ret) + ret = reg_write(client, REG_OUT_HEIGHT_HIGH, height_high); + if (!ret) + ret = reg_write(client, REG_OUT_HEIGHT_LOW, height_low); + + if (!ret) + ret = reg_write(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width_high); + if (!ret) + ret = reg_write(client, REG_OUT_TOTAL_WIDTH_LOW, total_width_low); + if (!ret) + ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height_high); + if (!ret) + ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_LOW, total_height_low); + + return ret; +} + +static int ov5642_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code); + + dev_dbg(sd->v4l2_dev->dev, "%s(%u) width: %u heigth: %u\n", + __func__, mf->code, mf->width, mf->height); + + if (!fmt) { + mf->code = ov5642_colour_fmts[0].code; + mf->colorspace = ov5642_colour_fmts[0].colorspace; + } + + mf->width = OV5642_WIDTH; + mf->height = OV5642_HEIGHT; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int ov5642_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + + dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); + + /* MIPI CSI could have changed the format, double-check */ + if (!ov5642_find_datafmt(mf->code)) + return -EINVAL; + + ov5642_try_fmt(sd, mf); + + priv->fmt = ov5642_find_datafmt(mf->code); + + ov5642_write_array(client, ov5642_default_regs_init); + ov5642_set_resolution(client); + ov5642_write_array(client, ov5642_default_regs_finalise); + + return 0; +} + +static int ov5642_g_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + + const struct ov5642_datafmt *fmt = priv->fmt; + + mf->code = fmt->code; + mf->colorspace = fmt->colorspace; + mf->width = OV5642_WIDTH; + mf->height = OV5642_HEIGHT; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int ov5642_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(ov5642_colour_fmts)) + return -EINVAL; + + *code = ov5642_colour_fmts[index].code; + return 0; +} + +static int ov5642_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match.addr != client->addr) + return -ENODEV; + + id->ident = V4L2_IDENT_OV5642; + id->revision = 0; + + return 0; +} + +static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct v4l2_rect *rect = &a->c; + + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rect->top = 0; + rect->left = 0; + rect->width = OV5642_WIDTH; + rect->height = OV5642_HEIGHT; + + return 0; +} + +static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = OV5642_WIDTH; + a->bounds.height = OV5642_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { + .s_mbus_fmt = ov5642_s_fmt, + .g_mbus_fmt = ov5642_g_fmt, + .try_mbus_fmt = ov5642_try_fmt, + .enum_mbus_fmt = ov5642_enum_fmt, + .g_crop = ov5642_g_crop, + .cropcap = ov5642_cropcap, +}; + +static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { + .g_chip_ident = ov5642_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov5642_get_register, + .s_register = ov5642_set_register, +#endif +}; + +static struct v4l2_subdev_ops ov5642_subdev_ops = { + .core = &ov5642_subdev_core_ops, + .video = &ov5642_subdev_video_ops, +}; + +/* + * We have to provide soc-camera operations, but we don't have anything to say + * there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param + */ +static unsigned long soc_ov5642_query_bus_param(struct soc_camera_device *icd) +{ + return 0; +} + +static int soc_ov5642_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return -EINVAL; +} + +static struct soc_camera_ops soc_ov5642_ops = { + .query_bus_param = soc_ov5642_query_bus_param, + .set_bus_param = soc_ov5642_set_bus_param, +}; + +static int ov5642_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) +{ + int ret; + u8 id_high, id_low; + u16 id; + + /* Read sensor Model ID */ + ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high); + if (ret < 0) + return ret; + + id = id_high << 8; + + ret = reg_read(client, REG_CHIP_ID_LOW, &id_low); + if (ret < 0) + return ret; + + id |= id_low; + + dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); + + if (id != 0x5642) + return -ENODEV; + + return 0; +} + +static int ov5642_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov5642 *priv; + struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl; + int ret; + + if (!icd) { + dev_err(&client->dev, "OV5642: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); + if (!icl) { + dev_err(&client->dev, "OV5642: missing platform data!\n"); + return -EINVAL; + } + + priv = kzalloc(sizeof(struct ov5642), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops); + + icd->ops = &soc_ov5642_ops; + priv->fmt = &ov5642_colour_fmts[0]; + + ret = ov5642_video_probe(icd, client); + if (ret < 0) + goto error; + + return 0; + +error: + icd->ops = NULL; + kfree(priv); + return ret; +} + +static int ov5642_remove(struct i2c_client *client) +{ + struct ov5642 *priv = to_ov5642(client); + struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); + + icd->ops = NULL; + if (icl->free_bus) + icl->free_bus(icl); + kfree(priv); + + return 0; +} + +static const struct i2c_device_id ov5642_id[] = { + { "ov5642", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov5642_id); + +static struct i2c_driver ov5642_i2c_driver = { + .driver = { + .name = "ov5642", + }, + .probe = ov5642_probe, + .remove = ov5642_remove, + .id_table = ov5642_id, +}; + +static int __init ov5642_mod_init(void) +{ + return i2c_add_driver(&ov5642_i2c_driver); +} + +static void __exit ov5642_mod_exit(void) +{ + i2c_del_driver(&ov5642_i2c_driver); +} + +module_init(ov5642_mod_init); +module_exit(ov5642_mod_exit); + +MODULE_DESCRIPTION("Omnivision OV5642 Camera driver"); +MODULE_AUTHOR("Bastian Hecht "); +MODULE_LICENSE("GPL v2"); diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 8717045e4aec..63fd9d3db296 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -76,6 +76,7 @@ enum { V4L2_IDENT_OV6650 = 258, V4L2_IDENT_OV2640 = 259, V4L2_IDENT_OV9740 = 260, + V4L2_IDENT_OV5642 = 261, /* module saa7146: reserved range 300-309 */ V4L2_IDENT_SAA7146 = 300, -- cgit v1.2.3 From 2699b67223aca6b1450fc2f72e40fada952afc85 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 28 Jul 2011 11:31:47 +1000 Subject: md: load/store badblock list from v1.x metadata Space must have been allocated when array was created. A feature flag is set when the badblock list is non-empty, to ensure old kernels don't load and trust the whole device. We only update the on-disk badblocklist when it has changed. If the badblocklist (or other metadata) is stored on a bad block, we don't cope very well. If metadata has no room for bad block, flag bad-blocks as disabled, and do the same for 0.90 metadata. Signed-off-by: NeilBrown --- drivers/md/md.c | 108 +++++++++++++++++++++++++++++++++++++++++++--- drivers/md/md.h | 5 ++- include/linux/raid/md_p.h | 14 ++++-- 3 files changed, 116 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/md/md.c b/drivers/md/md.c index 7f5ffc51c28e..e2ec8bd0fb94 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -757,6 +757,10 @@ static void free_disk_sb(mdk_rdev_t * rdev) rdev->sb_start = 0; rdev->sectors = 0; } + if (rdev->bb_page) { + put_page(rdev->bb_page); + rdev->bb_page = NULL; + } } @@ -1395,6 +1399,8 @@ static __le32 calc_sb_1_csum(struct mdp_superblock_1 * sb) return cpu_to_le32(csum); } +static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors, + int acknowledged); static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) { struct mdp_superblock_1 *sb; @@ -1473,6 +1479,47 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) else rdev->desc_nr = le32_to_cpu(sb->dev_number); + if (!rdev->bb_page) { + rdev->bb_page = alloc_page(GFP_KERNEL); + if (!rdev->bb_page) + return -ENOMEM; + } + if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BAD_BLOCKS) && + rdev->badblocks.count == 0) { + /* need to load the bad block list. + * Currently we limit it to one page. + */ + s32 offset; + sector_t bb_sector; + u64 *bbp; + int i; + int sectors = le16_to_cpu(sb->bblog_size); + if (sectors > (PAGE_SIZE / 512)) + return -EINVAL; + offset = le32_to_cpu(sb->bblog_offset); + if (offset == 0) + return -EINVAL; + bb_sector = (long long)offset; + if (!sync_page_io(rdev, bb_sector, sectors << 9, + rdev->bb_page, READ, true)) + return -EIO; + bbp = (u64 *)page_address(rdev->bb_page); + rdev->badblocks.shift = sb->bblog_shift; + for (i = 0 ; i < (sectors << (9-3)) ; i++, bbp++) { + u64 bb = le64_to_cpu(*bbp); + int count = bb & (0x3ff); + u64 sector = bb >> 10; + sector <<= sb->bblog_shift; + count <<= sb->bblog_shift; + if (bb + 1 == 0) + break; + if (md_set_badblocks(&rdev->badblocks, + sector, count, 1) == 0) + return -EINVAL; + } + } else if (sb->bblog_offset == 0) + rdev->badblocks.shift = -1; + if (!refdev) { ret = 1; } else { @@ -1624,7 +1671,6 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->pad0 = 0; sb->recovery_offset = cpu_to_le64(0); memset(sb->pad1, 0, sizeof(sb->pad1)); - memset(sb->pad2, 0, sizeof(sb->pad2)); memset(sb->pad3, 0, sizeof(sb->pad3)); sb->utime = cpu_to_le64((__u64)mddev->utime); @@ -1664,6 +1710,40 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->new_chunk = cpu_to_le32(mddev->new_chunk_sectors); } + if (rdev->badblocks.count == 0) + /* Nothing to do for bad blocks*/ ; + else if (sb->bblog_offset == 0) + /* Cannot record bad blocks on this device */ + md_error(mddev, rdev); + else { + struct badblocks *bb = &rdev->badblocks; + u64 *bbp = (u64 *)page_address(rdev->bb_page); + u64 *p = bb->page; + sb->feature_map |= cpu_to_le32(MD_FEATURE_BAD_BLOCKS); + if (bb->changed) { + unsigned seq; + +retry: + seq = read_seqbegin(&bb->lock); + + memset(bbp, 0xff, PAGE_SIZE); + + for (i = 0 ; i < bb->count ; i++) { + u64 internal_bb = *p++; + u64 store_bb = ((BB_OFFSET(internal_bb) << 10) + | BB_LEN(internal_bb)); + *bbp++ = cpu_to_le64(store_bb); + } + if (read_seqretry(&bb->lock, seq)) + goto retry; + + bb->sector = (rdev->sb_start + + (int)le32_to_cpu(sb->bblog_offset)); + bb->size = le16_to_cpu(sb->bblog_size); + bb->changed = 0; + } + } + max_dev = 0; list_for_each_entry(rdev2, &mddev->disks, same_set) if (rdev2->desc_nr+1 > max_dev) @@ -2196,6 +2276,7 @@ static void md_update_sb(mddev_t * mddev, int force_change) mdk_rdev_t *rdev; int sync_req; int nospares = 0; + int any_badblocks_changed = 0; repeat: /* First make sure individual recovery_offsets are correct */ @@ -2267,6 +2348,11 @@ repeat: MD_BUG(); mddev->events --; } + + list_for_each_entry(rdev, &mddev->disks, same_set) + if (rdev->badblocks.changed) + any_badblocks_changed++; + sync_sbs(mddev, nospares); spin_unlock_irq(&mddev->write_lock); @@ -2292,6 +2378,13 @@ repeat: bdevname(rdev->bdev,b), (unsigned long long)rdev->sb_start); rdev->sb_events = mddev->events; + if (rdev->badblocks.size) { + md_super_write(mddev, rdev, + rdev->badblocks.sector, + rdev->badblocks.size << 9, + rdev->bb_page); + rdev->badblocks.size = 0; + } } else dprintk(")\n"); @@ -2315,6 +2408,9 @@ repeat: if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) sysfs_notify(&mddev->kobj, NULL, "sync_completed"); + if (any_badblocks_changed) + list_for_each_entry(rdev, &mddev->disks, same_set) + md_ack_all_badblocks(&rdev->badblocks); } /* words written to sysfs files may, or may not, be \n terminated. @@ -2822,6 +2918,8 @@ int md_rdev_init(mdk_rdev_t *rdev) rdev->sb_events = 0; rdev->last_read_error.tv_sec = 0; rdev->last_read_error.tv_nsec = 0; + rdev->sb_loaded = 0; + rdev->bb_page = NULL; atomic_set(&rdev->nr_pending, 0); atomic_set(&rdev->read_errors, 0); atomic_set(&rdev->corrected_errors, 0); @@ -2910,11 +3008,9 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi return rdev; abort_free: - if (rdev->sb_page) { - if (rdev->bdev) - unlock_rdev(rdev); - free_disk_sb(rdev); - } + if (rdev->bdev) + unlock_rdev(rdev); + free_disk_sb(rdev); kfree(rdev->badblocks.page); kfree(rdev); return ERR_PTR(err); diff --git a/drivers/md/md.h b/drivers/md/md.h index 85af8433f8b8..aea9e9ff8a33 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -55,7 +55,7 @@ struct mdk_rdev_s struct block_device *meta_bdev; struct block_device *bdev; /* block device handle */ - struct page *sb_page; + struct page *sb_page, *bb_page; int sb_loaded; __u64 sb_events; sector_t data_offset; /* start of data in array */ @@ -127,6 +127,9 @@ struct mdk_rdev_s u64 *page; /* badblock list */ int changed; seqlock_t lock; + + sector_t sector; + sector_t size; /* in sectors */ } badblocks; }; diff --git a/include/linux/raid/md_p.h b/include/linux/raid/md_p.h index 75cbf4f62fe8..9e65d9e20662 100644 --- a/include/linux/raid/md_p.h +++ b/include/linux/raid/md_p.h @@ -245,10 +245,16 @@ struct mdp_superblock_1 { __u8 device_uuid[16]; /* user-space setable, ignored by kernel */ __u8 devflags; /* per-device flags. Only one defined...*/ #define WriteMostly1 1 /* mask for writemostly flag in above */ - __u8 pad2[64-57]; /* set to 0 when writing */ + /* Bad block log. If there are any bad blocks the feature flag is set. + * If offset and size are non-zero, that space is reserved and available + */ + __u8 bblog_shift; /* shift from sectors to block size */ + __le16 bblog_size; /* number of sectors reserved for list */ + __le32 bblog_offset; /* sector offset from superblock to bblog, + * signed - not unsigned */ /* array state information - 64 bytes */ - __le64 utime; /* 40 bits second, 24 btes microseconds */ + __le64 utime; /* 40 bits second, 24 bits microseconds */ __le64 events; /* incremented when superblock updated */ __le64 resync_offset; /* data before this offset (from data_offset) known to be in sync */ __le32 sb_csum; /* checksum up to devs[max_dev] */ @@ -270,8 +276,8 @@ struct mdp_superblock_1 { * must be honoured */ #define MD_FEATURE_RESHAPE_ACTIVE 4 +#define MD_FEATURE_BAD_BLOCKS 8 /* badblock list is not empty */ -#define MD_FEATURE_ALL (1|2|4) +#define MD_FEATURE_ALL (1|2|4|8) #endif - -- cgit v1.2.3 From d8873315065f1f527c7c380402cf59b1e1d0ae36 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Tue, 26 Jul 2011 06:05:37 +0000 Subject: net: add IFF_SKB_TX_SHARED flag to priv_flags Pktgen attempts to transmit shared skbs to net devices, which can't be used by some drivers as they keep state information in skbs. This patch adds a flag marking drivers as being able to handle shared skbs in their tx path. Drivers are defaulted to being unable to do so, but calling ether_setup enables this flag, as 90% of the drivers calling ether_setup touch real hardware and can handle shared skbs. A subsequent patch will audit drivers to ensure that the flag is set properly Signed-off-by: Neil Horman Reported-by: Jiri Pirko CC: Robert Olsson CC: Eric Dumazet CC: Alexey Dobriyan CC: David S. Miller Signed-off-by: David S. Miller --- include/linux/if.h | 2 ++ net/core/pktgen.c | 8 +++++--- net/ethernet/eth.c | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/if.h b/include/linux/if.h index 3bc63e6a02f7..03489ca92ded 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -76,6 +76,8 @@ #define IFF_BRIDGE_PORT 0x4000 /* device used as bridge port */ #define IFF_OVS_DATAPATH 0x8000 /* device used as Open vSwitch * datapath port */ +#define IFF_TX_SKB_SHARING 0x10000 /* The interface supports sharing + * skbs on transmit */ #define IF_GET_IFACE 0x0001 /* for querying only */ #define IF_GET_PROTO 0x0002 diff --git a/net/core/pktgen.c b/net/core/pktgen.c index f76079cd750c..e35a6fbb8110 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -1070,7 +1070,9 @@ static ssize_t pktgen_if_write(struct file *file, len = num_arg(&user_buffer[i], 10, &value); if (len < 0) return len; - + if ((value > 0) && + (!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING))) + return -ENOTSUPP; i += len; pkt_dev->clone_skb = value; @@ -3555,7 +3557,6 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname) pkt_dev->min_pkt_size = ETH_ZLEN; pkt_dev->max_pkt_size = ETH_ZLEN; pkt_dev->nfrags = 0; - pkt_dev->clone_skb = pg_clone_skb_d; pkt_dev->delay = pg_delay_d; pkt_dev->count = pg_count_d; pkt_dev->sofar = 0; @@ -3563,7 +3564,6 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname) pkt_dev->udp_src_max = 9; pkt_dev->udp_dst_min = 9; pkt_dev->udp_dst_max = 9; - pkt_dev->vlan_p = 0; pkt_dev->vlan_cfi = 0; pkt_dev->vlan_id = 0xffff; @@ -3575,6 +3575,8 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname) err = pktgen_setup_dev(pkt_dev, ifname); if (err) goto out1; + if (pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING) + pkt_dev->clone_skb = pg_clone_skb_d; pkt_dev->entry = proc_create_data(ifname, 0600, pg_proc_dir, &pktgen_if_fops, pkt_dev); diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index b8453bd4b09c..27997d35ebd3 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -340,6 +340,7 @@ void ether_setup(struct net_device *dev) dev->addr_len = ETH_ALEN; dev->tx_queue_len = 1000; /* Ethernet wants good queues */ dev->flags = IFF_BROADCAST|IFF_MULTICAST; + dev->priv_flags = IFF_TX_SKB_SHARING; memset(dev->broadcast, 0xFF, ETH_ALEN); -- cgit v1.2.3 From f22aaaa70d8c24e5dc7d23a219c4beace8354b65 Mon Sep 17 00:00:00 2001 From: Donggeun Kim Date: Mon, 20 Jun 2011 16:48:19 +0900 Subject: hwmon: Driver for NTC Thermistors Add support for NTC Thermistor series. In this release, the following thermistors are supported: NCP15WB473, NCP18WB473, NCP03WB473, and NCP15WL333. This driver is based on the datasheet of MURATA. The driver in the patch does conversion from the raw ADC value (either voltage or resistence) to temperature. In order to use voltage values as input, the circuit schematics should be provided with the platform data. A compensation table for each type of thermistor is provided for the conversion. Signed-off-by: Donggeun Kim Signed-off-by: MyungJoo Ham Signed-off-by: KyungMin Park Reviewed-by: Shubhrajyoti D Signed-off-by: Guenter Roeck --- Documentation/hwmon/ntc_thermistor | 93 ++++++ drivers/hwmon/Kconfig | 14 + drivers/hwmon/Makefile | 1 + drivers/hwmon/ntc_thermistor.c | 453 +++++++++++++++++++++++++++ include/linux/platform_data/ntc_thermistor.h | 53 ++++ 5 files changed, 614 insertions(+) create mode 100644 Documentation/hwmon/ntc_thermistor create mode 100644 drivers/hwmon/ntc_thermistor.c create mode 100644 include/linux/platform_data/ntc_thermistor.h (limited to 'include') diff --git a/Documentation/hwmon/ntc_thermistor b/Documentation/hwmon/ntc_thermistor new file mode 100644 index 000000000000..3bfda94096fd --- /dev/null +++ b/Documentation/hwmon/ntc_thermistor @@ -0,0 +1,93 @@ +Kernel driver ntc_thermistor +================= + +Supported thermistors: +* Murata NTC Thermistors NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, NCP15WL333 + Prefixes: 'ncp15wb473', 'ncp18wb473', 'ncp21wb473', 'ncp03wb473', 'ncp15wl333' + Datasheet: Publicly available at Murata + +Other NTC thermistors can be supported simply by adding compensation +tables; e.g., NCP15WL333 support is added by the table ncpXXwl333. + +Authors: + MyungJoo Ham + +Description +----------- + +The NTC thermistor is a simple thermistor that requires users to provide the +resistance and lookup the corresponding compensation table to get the +temperature input. + +The NTC driver provides lookup tables with a linear approximation function +and four circuit models with an option not to use any of the four models. + +The four circuit models provided are: + + $: resister, [TH]: the thermistor + + 1. connect = NTC_CONNECTED_POSITIVE, pullup_ohm > 0 + + [pullup_uV] + | | + [TH] $ (pullup_ohm) + | | + +----+-----------------------[read_uV] + | + $ (pulldown_ohm) + | + --- (ground) + + 2. connect = NTC_CONNECTED_POSITIVE, pullup_ohm = 0 (not-connected) + + [pullup_uV] + | + [TH] + | + +----------------------------[read_uV] + | + $ (pulldown_ohm) + | + --- (ground) + + 3. connect = NTC_CONNECTED_GROUND, pulldown_ohm > 0 + + [pullup_uV] + | + $ (pullup_ohm) + | + +----+-----------------------[read_uV] + | | + [TH] $ (pulldown_ohm) + | | + -------- (ground) + + 4. connect = NTC_CONNECTED_GROUND, pulldown_ohm = 0 (not-connected) + + [pullup_uV] + | + $ (pullup_ohm) + | + +----------------------------[read_uV] + | + [TH] + | + --- (ground) + +When one of the four circuit models is used, read_uV, pullup_uV, pullup_ohm, +pulldown_ohm, and connect should be provided. When none of the four models +are suitable or the user can get the resistance directly, the user should +provide read_ohm and _not_ provide the others. + +Sysfs Interface +--------------- +name the mandatory global attribute, the thermistor name. + +temp1_type always 4 (thermistor) + RO + +temp1_input measure the temperature and provide the measured value. + (reading this file initiates the reading procedure.) + RO + +Note that each NTC thermistor has only _one_ thermistor; thus, only temp1 exists. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index dec5eee3542f..770fc8195627 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -777,6 +777,20 @@ config SENSORS_MAX6650 This driver can also be built as a module. If so, the module will be called max6650. +config SENSORS_NTC_THERMISTOR + tristate "NTC thermistor support" + depends on EXPERIMENTAL + help + This driver supports NTC thermistors sensor reading and its + interpretation. The driver can also monitor the temperature and + send notifications about the temperature. + + Currently, this driver supports + NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333. + + This driver can also be built as a module. If so, the module + will be called ntc-thermistor. + config SENSORS_PC87360 tristate "National Semiconductor PC87360 family" select HWMON_VID diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index a6d519e568c0..1b7dc770aa60 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o +obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c new file mode 100644 index 000000000000..d7926f4336b5 --- /dev/null +++ b/drivers/hwmon/ntc_thermistor.c @@ -0,0 +1,453 @@ +/* + * ntc_thermistor.c - NTC Thermistors + * + * Copyright (C) 2010 Samsung Electronics + * MyungJoo Ham + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +struct ntc_compensation { + int temp_C; + unsigned int ohm; +}; + +/* + * A compensation table should be sorted by the values of .ohm + * in descending order. + * The following compensation tables are from the specification of Murata NTC + * Thermistors Datasheet + */ +const struct ntc_compensation ncpXXwb473[] = { + { .temp_C = -40, .ohm = 1747920 }, + { .temp_C = -35, .ohm = 1245428 }, + { .temp_C = -30, .ohm = 898485 }, + { .temp_C = -25, .ohm = 655802 }, + { .temp_C = -20, .ohm = 483954 }, + { .temp_C = -15, .ohm = 360850 }, + { .temp_C = -10, .ohm = 271697 }, + { .temp_C = -5, .ohm = 206463 }, + { .temp_C = 0, .ohm = 158214 }, + { .temp_C = 5, .ohm = 122259 }, + { .temp_C = 10, .ohm = 95227 }, + { .temp_C = 15, .ohm = 74730 }, + { .temp_C = 20, .ohm = 59065 }, + { .temp_C = 25, .ohm = 47000 }, + { .temp_C = 30, .ohm = 37643 }, + { .temp_C = 35, .ohm = 30334 }, + { .temp_C = 40, .ohm = 24591 }, + { .temp_C = 45, .ohm = 20048 }, + { .temp_C = 50, .ohm = 16433 }, + { .temp_C = 55, .ohm = 13539 }, + { .temp_C = 60, .ohm = 11209 }, + { .temp_C = 65, .ohm = 9328 }, + { .temp_C = 70, .ohm = 7798 }, + { .temp_C = 75, .ohm = 6544 }, + { .temp_C = 80, .ohm = 5518 }, + { .temp_C = 85, .ohm = 4674 }, + { .temp_C = 90, .ohm = 3972 }, + { .temp_C = 95, .ohm = 3388 }, + { .temp_C = 100, .ohm = 2902 }, + { .temp_C = 105, .ohm = 2494 }, + { .temp_C = 110, .ohm = 2150 }, + { .temp_C = 115, .ohm = 1860 }, + { .temp_C = 120, .ohm = 1615 }, + { .temp_C = 125, .ohm = 1406 }, +}; +const struct ntc_compensation ncpXXwl333[] = { + { .temp_C = -40, .ohm = 1610154 }, + { .temp_C = -35, .ohm = 1130850 }, + { .temp_C = -30, .ohm = 802609 }, + { .temp_C = -25, .ohm = 575385 }, + { .temp_C = -20, .ohm = 416464 }, + { .temp_C = -15, .ohm = 304219 }, + { .temp_C = -10, .ohm = 224193 }, + { .temp_C = -5, .ohm = 166623 }, + { .temp_C = 0, .ohm = 124850 }, + { .temp_C = 5, .ohm = 94287 }, + { .temp_C = 10, .ohm = 71747 }, + { .temp_C = 15, .ohm = 54996 }, + { .temp_C = 20, .ohm = 42455 }, + { .temp_C = 25, .ohm = 33000 }, + { .temp_C = 30, .ohm = 25822 }, + { .temp_C = 35, .ohm = 20335 }, + { .temp_C = 40, .ohm = 16115 }, + { .temp_C = 45, .ohm = 12849 }, + { .temp_C = 50, .ohm = 10306 }, + { .temp_C = 55, .ohm = 8314 }, + { .temp_C = 60, .ohm = 6746 }, + { .temp_C = 65, .ohm = 5503 }, + { .temp_C = 70, .ohm = 4513 }, + { .temp_C = 75, .ohm = 3721 }, + { .temp_C = 80, .ohm = 3084 }, + { .temp_C = 85, .ohm = 2569 }, + { .temp_C = 90, .ohm = 2151 }, + { .temp_C = 95, .ohm = 1809 }, + { .temp_C = 100, .ohm = 1529 }, + { .temp_C = 105, .ohm = 1299 }, + { .temp_C = 110, .ohm = 1108 }, + { .temp_C = 115, .ohm = 949 }, + { .temp_C = 120, .ohm = 817 }, + { .temp_C = 125, .ohm = 707 }, +}; + +struct ntc_data { + struct device *hwmon_dev; + struct ntc_thermistor_platform_data *pdata; + const struct ntc_compensation *comp; + struct device *dev; + int n_comp; + char name[PLATFORM_NAME_SIZE]; +}; + +static inline u64 div64_u64_safe(u64 dividend, u64 divisor) +{ + if (divisor == 0 && dividend == 0) + return 0; + if (divisor == 0) + return UINT_MAX; + return div64_u64(dividend, divisor); +} + +static unsigned int get_ohm_of_thermistor(struct ntc_data *data, + unsigned int uV) +{ + struct ntc_thermistor_platform_data *pdata = data->pdata; + u64 mV = uV / 1000; + u64 pmV = pdata->pullup_uV / 1000; + u64 N, puO, pdO; + puO = pdata->pullup_ohm; + pdO = pdata->pulldown_ohm; + + if (mV == 0) { + if (pdata->connect == NTC_CONNECTED_POSITIVE) + return UINT_MAX; + return 0; + } + if (mV >= pmV) + return (pdata->connect == NTC_CONNECTED_POSITIVE) ? + 0 : UINT_MAX; + + if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0) + N = div64_u64_safe(pdO * (pmV - mV), mV); + else if (pdata->connect == NTC_CONNECTED_GROUND && pdO == 0) + N = div64_u64_safe(puO * mV, pmV - mV); + else if (pdata->connect == NTC_CONNECTED_POSITIVE) + N = div64_u64_safe(pdO * puO * (pmV - mV), + puO * mV - pdO * (pmV - mV)); + else + N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV); + + return (unsigned int) N; +} + +static int lookup_comp(struct ntc_data *data, + unsigned int ohm, int *i_low, int *i_high) +{ + int start, end, mid = -1; + + /* Do a binary search on compensation table */ + start = 0; + end = data->n_comp; + + while (end > start) { + mid = start + (end - start) / 2; + if (data->comp[mid].ohm < ohm) + end = mid; + else if (data->comp[mid].ohm > ohm) + start = mid + 1; + else + break; + } + + if (mid == 0) { + if (data->comp[mid].ohm > ohm) { + *i_high = mid; + *i_low = mid + 1; + return 0; + } else { + *i_low = mid; + *i_high = -1; + return -EINVAL; + } + } + if (mid == (data->n_comp - 1)) { + if (data->comp[mid].ohm <= ohm) { + *i_low = mid; + *i_high = mid - 1; + return 0; + } else { + *i_low = -1; + *i_high = mid; + return -EINVAL; + } + } + + if (data->comp[mid].ohm <= ohm) { + *i_low = mid; + *i_high = mid - 1; + } + if (data->comp[mid].ohm > ohm) { + *i_low = mid + 1; + *i_high = mid; + } + + return 0; +} + +static int get_temp_mC(struct ntc_data *data, unsigned int ohm, int *temp) +{ + int low, high; + int ret; + + ret = lookup_comp(data, ohm, &low, &high); + if (ret) { + /* Unable to use linear approximation */ + if (low != -1) + *temp = data->comp[low].temp_C * 1000; + else if (high != -1) + *temp = data->comp[high].temp_C * 1000; + else + return ret; + } else { + *temp = data->comp[low].temp_C * 1000 + + ((data->comp[high].temp_C - data->comp[low].temp_C) * + 1000 * ((int)ohm - (int)data->comp[low].ohm)) / + ((int)data->comp[high].ohm - (int)data->comp[low].ohm); + } + + return 0; +} + +static int ntc_thermistor_read(struct ntc_data *data, int *temp) +{ + int ret; + int read_ohm, read_uV; + unsigned int ohm = 0; + + if (data->pdata->read_ohm) { + read_ohm = data->pdata->read_ohm(); + if (read_ohm < 0) + return read_ohm; + ohm = (unsigned int)read_ohm; + } + + if (data->pdata->read_uV) { + read_uV = data->pdata->read_uV(); + if (read_uV < 0) + return read_uV; + ohm = get_ohm_of_thermistor(data, (unsigned int)read_uV); + } + + ret = get_temp_mC(data, ohm, temp); + if (ret) { + dev_dbg(data->dev, "Sensor reading function not available.\n"); + return ret; + } + + return 0; +} + +static ssize_t ntc_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ntc_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static ssize_t ntc_show_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "4\n"); +} + +static ssize_t ntc_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ntc_data *data = dev_get_drvdata(dev); + int temp, ret; + + ret = ntc_thermistor_read(data, &temp); + if (ret) + return ret; + return sprintf(buf, "%d\n", temp); +} + +static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0); +static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL); + +static struct attribute *ntc_attributes[] = { + &dev_attr_name.attr, + &sensor_dev_attr_temp1_type.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ntc_attr_group = { + .attrs = ntc_attributes, +}; + +static int __devinit ntc_thermistor_probe(struct platform_device *pdev) +{ + struct ntc_data *data; + struct ntc_thermistor_platform_data *pdata = pdev->dev.platform_data; + int ret = 0; + + if (!pdata) { + dev_err(&pdev->dev, "No platform init data supplied.\n"); + return -ENODEV; + } + + /* Either one of the two is required. */ + if (!pdata->read_uV && !pdata->read_ohm) { + dev_err(&pdev->dev, "Both read_uV and read_ohm missing." + "Need either one of the two.\n"); + return -EINVAL; + } + + if (pdata->read_uV && pdata->read_ohm) { + dev_warn(&pdev->dev, "Only one of read_uV and read_ohm " + "is needed; ignoring read_uV.\n"); + pdata->read_uV = NULL; + } + + if (pdata->read_uV && (pdata->pullup_uV == 0 || + (pdata->pullup_ohm == 0 && pdata->connect == + NTC_CONNECTED_GROUND) || + (pdata->pulldown_ohm == 0 && pdata->connect == + NTC_CONNECTED_POSITIVE) || + (pdata->connect != NTC_CONNECTED_POSITIVE && + pdata->connect != NTC_CONNECTED_GROUND))) { + dev_err(&pdev->dev, "Required data to use read_uV not " + "supplied.\n"); + return -EINVAL; + } + + data = kzalloc(sizeof(struct ntc_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = &pdev->dev; + data->pdata = pdata; + strncpy(data->name, pdev->id_entry->name, PLATFORM_NAME_SIZE); + + switch (pdev->id_entry->driver_data) { + case TYPE_NCPXXWB473: + data->comp = ncpXXwb473; + data->n_comp = ARRAY_SIZE(ncpXXwb473); + break; + case TYPE_NCPXXWL333: + data->comp = ncpXXwl333; + data->n_comp = ARRAY_SIZE(ncpXXwl333); + break; + default: + dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n", + pdev->id_entry->driver_data, + pdev->id_entry->name); + ret = -EINVAL; + goto err; + } + + platform_set_drvdata(pdev, data); + + ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group); + if (ret) { + dev_err(data->dev, "unable to create sysfs files\n"); + goto err; + } + + data->hwmon_dev = hwmon_device_register(data->dev); + if (IS_ERR_OR_NULL(data->hwmon_dev)) { + dev_err(data->dev, "unable to register as hwmon device.\n"); + ret = -EINVAL; + goto err_after_sysfs; + } + + dev_info(&pdev->dev, "Thermistor %s:%d (type: %s/%lu) successfully probed.\n", + pdev->name, pdev->id, pdev->id_entry->name, + pdev->id_entry->driver_data); + return 0; +err_after_sysfs: + sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); +err: + kfree(data); + return ret; +} + +static int __devexit ntc_thermistor_remove(struct platform_device *pdev) +{ + struct ntc_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); + platform_set_drvdata(pdev, NULL); + + kfree(data); + + return 0; +} + +static const struct platform_device_id ntc_thermistor_id[] = { + { "ncp15wb473", TYPE_NCPXXWB473 }, + { "ncp18wb473", TYPE_NCPXXWB473 }, + { "ncp21wb473", TYPE_NCPXXWB473 }, + { "ncp03wb473", TYPE_NCPXXWB473 }, + { "ncp15wl333", TYPE_NCPXXWL333 }, + { }, +}; + +static struct platform_driver ntc_thermistor_driver = { + .driver = { + .name = "ntc-thermistor", + .owner = THIS_MODULE, + }, + .probe = ntc_thermistor_probe, + .remove = __devexit_p(ntc_thermistor_remove), + .id_table = ntc_thermistor_id, +}; + +static int __init ntc_thermistor_init(void) +{ + return platform_driver_register(&ntc_thermistor_driver); +} + +module_init(ntc_thermistor_init); + +static void __exit ntc_thermistor_cleanup(void) +{ + platform_driver_unregister(&ntc_thermistor_driver); +} + +module_exit(ntc_thermistor_cleanup); + +MODULE_DESCRIPTION("NTC Thermistor Driver"); +MODULE_AUTHOR("MyungJoo Ham "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ntc-thermistor"); diff --git a/include/linux/platform_data/ntc_thermistor.h b/include/linux/platform_data/ntc_thermistor.h new file mode 100644 index 000000000000..abd286215279 --- /dev/null +++ b/include/linux/platform_data/ntc_thermistor.h @@ -0,0 +1,53 @@ +/* + * ntc_thermistor.h - NTC Thermistors + * + * Copyright (C) 2010 Samsung Electronics + * MyungJoo Ham + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _LINUX_NTC_H +#define _LINUX_NTC_H + +enum ntc_thermistor_type { + TYPE_NCPXXWB473, + TYPE_NCPXXWL333, +}; + +struct ntc_thermistor_platform_data { + /* + * One (not both) of read_uV and read_ohm should be provided and only + * one of the two should be provided. + * Both functions should return negative value for an error case. + * + * pullup_uV, pullup_ohm, pulldown_ohm, and connect are required to use + * read_uV() + * + * How to setup pullup_ohm, pulldown_ohm, and connect is + * described at Documentation/hwmon/ntc + * + * pullup/down_ohm: 0 for infinite / not-connected + */ + int (*read_uV)(void); + unsigned int pullup_uV; + + unsigned int pullup_ohm; + unsigned int pulldown_ohm; + enum { NTC_CONNECTED_POSITIVE, NTC_CONNECTED_GROUND } connect; + + int (*read_ohm)(void); +}; + +#endif /* _LINUX_NTC_H */ -- cgit v1.2.3 From 08a543ad33fc188650801bd36eed4ffe272643e1 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Tue, 26 Jul 2011 03:19:06 -0600 Subject: irq: add irq_domain translation infrastructure This patch adds irq_domain infrastructure for translating from hardware irq numbers to linux irqs. This is particularly important for architectures adding device tree support because the current implementation (excluding PowerPC and SPARC) cannot handle translation for more than a single interrupt controller. irq_domain supports device tree translation for any number of interrupt controllers. This patch converts x86, Microblaze, ARM and MIPS to use irq_domain for device tree irq translation. x86 is untested beyond compiling it, irq_domain is enabled for MIPS and Microblaze, but the old behaviour is preserved until the core code is modified to actually register an irq_domain yet. On ARM it works and is required for much of the new ARM device tree board support. PowerPC has /not/ been converted to use this new infrastructure. It is still missing some features before it can replace the virq infrastructure already in powerpc (see documentation on irq_domain_map/unmap for details). Followup patches will add the missing pieces and migrate PowerPC to use irq_domain. SPARC has its own method of managing interrupts from the device tree and is unaffected by this change. Acked-by: Ralf Baechle Signed-off-by: Grant Likely --- arch/arm/Kconfig | 1 + arch/arm/include/asm/prom.h | 5 -- arch/arm/kernel/devtree.c | 14 ----- include/linux/irq.h | 6 +++ include/linux/irqdomain.h | 81 +++++++++++++++++++++++++++++ include/linux/of_irq.h | 4 ++ kernel/irq/Kconfig | 4 ++ kernel/irq/Makefile | 1 + kernel/irq/irqdomain.c | 122 ++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 219 insertions(+), 19 deletions(-) create mode 100644 include/linux/irqdomain.h create mode 100644 kernel/irq/irqdomain.c (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 1478c6171b00..8ac7b996038c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1682,6 +1682,7 @@ config USE_OF bool "Flattened Device Tree support" select OF select OF_EARLY_FLATTREE + select IRQ_DOMAIN help Include support for flattened device tree machine descriptions. diff --git a/arch/arm/include/asm/prom.h b/arch/arm/include/asm/prom.h index 11b8708fc4db..6f65ca86a5ec 100644 --- a/arch/arm/include/asm/prom.h +++ b/arch/arm/include/asm/prom.h @@ -16,11 +16,6 @@ #include #include -static inline void irq_dispose_mapping(unsigned int virq) -{ - return; -} - extern struct machine_desc *setup_machine_fdt(unsigned int dt_phys); extern void arm_dt_memblock_reserve(void); diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c index 0cdd7b456cb2..1a33e9d6bb1f 100644 --- a/arch/arm/kernel/devtree.c +++ b/arch/arm/kernel/devtree.c @@ -132,17 +132,3 @@ struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) return mdesc_best; } - -/** - * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq# - * - * Currently the mapping mechanism is trivial; simple flat hwirq numbers are - * mapped 1:1 onto Linux irq numbers. Cascaded irq controllers are not - * supported. - */ -unsigned int irq_create_of_mapping(struct device_node *controller, - const u32 *intspec, unsigned int intsize) -{ - return intspec[0]; -} -EXPORT_SYMBOL_GPL(irq_create_of_mapping); diff --git a/include/linux/irq.h b/include/linux/irq.h index 5f695041090c..87a06f345bd2 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -108,14 +108,18 @@ enum { }; struct msi_desc; +struct irq_domain; /** * struct irq_data - per irq and irq chip data passed down to chip functions * @irq: interrupt number + * @hwirq: hardware interrupt number, local to the interrupt domain * @node: node index useful for balancing * @state_use_accessors: status information for irq chip functions. * Use accessor functions to deal with it * @chip: low level interrupt hardware access + * @domain: Interrupt translation domain; responsible for mapping + * between hwirq number and linux irq number. * @handler_data: per-IRQ data for the irq_chip methods * @chip_data: platform-specific per-chip private data for the chip * methods, to allow shared chip implementations @@ -128,9 +132,11 @@ struct msi_desc; */ struct irq_data { unsigned int irq; + unsigned long hwirq; unsigned int node; unsigned int state_use_accessors; struct irq_chip *chip; + struct irq_domain *domain; void *handler_data; void *chip_data; struct msi_desc *msi_desc; diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h new file mode 100644 index 000000000000..8f2c10a784a4 --- /dev/null +++ b/include/linux/irqdomain.h @@ -0,0 +1,81 @@ +/* + * irq_domain - IRQ translation domains + * + * Translation infrastructure between hw and linux irq numbers. This is + * helpful for interrupt controllers to implement mapping between hardware + * irq numbers and the Linux irq number space. + * + * irq_domains also have a hook for translating device tree interrupt + * representation into a hardware irq number that can be mapped back to a + * Linux irq number without any extra platform support code. + * + * irq_domain is expected to be embedded in an interrupt controller's private + * data structure. + */ +#ifndef _LINUX_IRQDOMAIN_H +#define _LINUX_IRQDOMAIN_H + +#include + +#ifdef CONFIG_IRQ_DOMAIN +struct device_node; +struct irq_domain; + +/** + * struct irq_domain_ops - Methods for irq_domain objects + * @to_irq: (optional) given a local hardware irq number, return the linux + * irq number. If to_irq is not implemented, then the irq_domain + * will use this translation: irq = (domain->irq_base + hwirq) + * @dt_translate: Given a device tree node and interrupt specifier, decode + * the hardware irq number and linux irq type value. + */ +struct irq_domain_ops { + unsigned int (*to_irq)(struct irq_domain *d, unsigned long hwirq); + +#ifdef CONFIG_OF + int (*dt_translate)(struct irq_domain *d, struct device_node *node, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type); +#endif /* CONFIG_OF */ +}; + +/** + * struct irq_domain - Hardware interrupt number translation object + * @list: Element in global irq_domain list. + * @irq_base: Start of irq_desc range assigned to the irq_domain. The creator + * of the irq_domain is responsible for allocating the array of + * irq_desc structures. + * @nr_irq: Number of irqs managed by the irq domain + * @ops: pointer to irq_domain methods + * @priv: private data pointer for use by owner. Not touched by irq_domain + * core code. + * @of_node: (optional) Pointer to device tree nodes associated with the + * irq_domain. Used when decoding device tree interrupt specifiers. + */ +struct irq_domain { + struct list_head list; + unsigned int irq_base; + unsigned int nr_irq; + const struct irq_domain_ops *ops; + void *priv; + struct device_node *of_node; +}; + +/** + * irq_domain_to_irq() - Translate from a hardware irq to a linux irq number + * + * Returns the linux irq number associated with a hardware irq. By default, + * the mapping is irq == domain->irq_base + hwirq, but this mapping can + * be overridden if the irq_domain implements a .to_irq() hook. + */ +static inline unsigned int irq_domain_to_irq(struct irq_domain *d, + unsigned long hwirq) +{ + return d->ops->to_irq ? d->ops->to_irq(d, hwirq) : d->irq_base + hwirq; +} + +extern void irq_domain_add(struct irq_domain *domain); +extern void irq_domain_del(struct irq_domain *domain); +#endif /* CONFIG_IRQ_DOMAIN */ + +#endif /* _LINUX_IRQDOMAIN_H */ diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index e6955f5d1f08..cd2e61ce4e83 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -63,6 +63,9 @@ extern int of_irq_map_one(struct device_node *device, int index, extern unsigned int irq_create_of_mapping(struct device_node *controller, const u32 *intspec, unsigned int intsize); +#ifdef CONFIG_IRQ_DOMAIN +extern void irq_dispose_mapping(unsigned int irq); +#endif extern int of_irq_to_resource(struct device_node *dev, int index, struct resource *r); extern int of_irq_count(struct device_node *dev); @@ -70,6 +73,7 @@ extern int of_irq_to_resource_table(struct device_node *dev, struct resource *res, int nr_irqs); extern struct device_node *of_irq_find_parent(struct device_node *child); + #endif /* CONFIG_OF_IRQ */ #endif /* CONFIG_OF */ #endif /* __OF_IRQ_H */ diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index d1d051b38e0b..5a38bf4de641 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -52,6 +52,10 @@ config IRQ_EDGE_EOI_HANDLER config GENERIC_IRQ_CHIP bool +# Generic irq_domain hw <--> linux irq number translation +config IRQ_DOMAIN + bool + # Support forced irq threading config IRQ_FORCED_THREADING bool diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile index 73290056cfb6..fff17381f0af 100644 --- a/kernel/irq/Makefile +++ b/kernel/irq/Makefile @@ -2,6 +2,7 @@ obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o +obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o obj-$(CONFIG_PM_SLEEP) += pm.o diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c new file mode 100644 index 000000000000..29c7bd42e25d --- /dev/null +++ b/kernel/irq/irqdomain.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +static LIST_HEAD(irq_domain_list); +static DEFINE_MUTEX(irq_domain_mutex); + +/** + * irq_domain_add() - Register an irq_domain + * @domain: ptr to initialized irq_domain structure + * + * Registers an irq_domain structure. The irq_domain must at a minimum be + * initialized with an ops structure pointer, and either a ->to_irq hook or + * a valid irq_base value. Everything else is optional. + */ +void irq_domain_add(struct irq_domain *domain) +{ + struct irq_data *d; + int hwirq; + + /* + * This assumes that the irq_domain owner has already allocated + * the irq_descs. This block will be removed when support for dynamic + * allocation of irq_descs is added to irq_domain. + */ + for (hwirq = 0; hwirq < domain->nr_irq; hwirq++) { + d = irq_get_irq_data(irq_domain_to_irq(domain, hwirq)); + if (d || d->domain) { + /* things are broken; just report, don't clean up */ + WARN(1, "error: irq_desc already assigned to a domain"); + return; + } + d->domain = domain; + d->hwirq = hwirq; + } + + mutex_lock(&irq_domain_mutex); + list_add(&domain->list, &irq_domain_list); + mutex_unlock(&irq_domain_mutex); +} + +/** + * irq_domain_del() - Unregister an irq_domain + * @domain: ptr to registered irq_domain. + */ +void irq_domain_del(struct irq_domain *domain) +{ + struct irq_data *d; + int hwirq; + + mutex_lock(&irq_domain_mutex); + list_del(&domain->list); + mutex_unlock(&irq_domain_mutex); + + /* Clear the irq_domain assignments */ + for (hwirq = 0; hwirq < domain->nr_irq; hwirq++) { + d = irq_get_irq_data(irq_domain_to_irq(domain, hwirq)); + d->domain = NULL; + } +} + +#if defined(CONFIG_OF_IRQ) +/** + * irq_create_of_mapping() - Map a linux irq number from a DT interrupt spec + * + * Used by the device tree interrupt mapping code to translate a device tree + * interrupt specifier to a valid linux irq number. Returns either a valid + * linux IRQ number or 0. + * + * When the caller no longer need the irq number returned by this function it + * should arrange to call irq_dispose_mapping(). + */ +unsigned int irq_create_of_mapping(struct device_node *controller, + const u32 *intspec, unsigned int intsize) +{ + struct irq_domain *domain; + unsigned long hwirq; + unsigned int irq, type; + int rc = -EINVAL; + + /* Find a domain which can translate the irq spec */ + mutex_lock(&irq_domain_mutex); + list_for_each_entry(domain, &irq_domain_list, list) { + if (!domain->ops->dt_translate) + continue; + rc = domain->ops->dt_translate(domain, controller, + intspec, intsize, &hwirq, &type); + if (rc == 0) + break; + } + mutex_unlock(&irq_domain_mutex); + + if (rc != 0) + return 0; + + irq = irq_domain_to_irq(domain, hwirq); + if (type != IRQ_TYPE_NONE) + irq_set_irq_type(irq, type); + pr_debug("%s: mapped hwirq=%i to irq=%i, flags=%x\n", + controller->full_name, (int)hwirq, irq, type); + return irq; +} +EXPORT_SYMBOL_GPL(irq_create_of_mapping); + +/** + * irq_dispose_mapping() - Discard a mapping created by irq_create_of_mapping() + * @irq: linux irq number to be discarded + * + * Calling this function indicates the caller no longer needs a reference to + * the linux irq number returned by a prior call to irq_create_of_mapping(). + */ +void irq_dispose_mapping(unsigned int irq) +{ + /* + * nothing yet; will be filled when support for dynamic allocation of + * irq_descs is added to irq_domain + */ +} +EXPORT_SYMBOL_GPL(irq_dispose_mapping); +#endif /* CONFIG_OF_IRQ */ -- cgit v1.2.3 From 7e71330169d8056536b299290544980bccc6b300 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Tue, 26 Jul 2011 03:19:06 -0600 Subject: dt/irq: add irq_domain_generate_simple() helper irq_domain_generate_simple() is an easy way to generate an irq translation domain for simple irq controllers. It assumes a flat 1:1 mapping from hardware irq number to an offset of the first linux irq number assigned to the controller Signed-off-by: Grant Likely --- include/linux/irqdomain.h | 10 ++++++++ kernel/irq/irqdomain.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) (limited to 'include') diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 8f2c10a784a4..e807ad687a07 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -16,6 +16,7 @@ #define _LINUX_IRQDOMAIN_H #include +#include #ifdef CONFIG_IRQ_DOMAIN struct device_node; @@ -78,4 +79,13 @@ extern void irq_domain_add(struct irq_domain *domain); extern void irq_domain_del(struct irq_domain *domain); #endif /* CONFIG_IRQ_DOMAIN */ +#if defined(CONFIG_IRQ_DOMAIN) && defined(CONFIG_OF_IRQ) +extern void irq_domain_add_simple(struct device_node *controller, int irq_base); +extern void irq_domain_generate_simple(const struct of_device_id *match, + u64 phys_base, unsigned int irq_start); +#else /* CONFIG_IRQ_DOMAIN && CONFIG_OF_IRQ */ +static inline void irq_domain_generate_simple(const struct of_device_id *match, + u64 phys_base, unsigned int irq_start) { } +#endif /* CONFIG_IRQ_DOMAIN && CONFIG_OF_IRQ */ + #endif /* _LINUX_IRQDOMAIN_H */ diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 29c7bd42e25d..d5828da3fd38 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include static LIST_HEAD(irq_domain_list); static DEFINE_MUTEX(irq_domain_mutex); @@ -119,4 +121,60 @@ void irq_dispose_mapping(unsigned int irq) */ } EXPORT_SYMBOL_GPL(irq_dispose_mapping); + +int irq_domain_simple_dt_translate(struct irq_domain *d, + struct device_node *controller, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + if (d->of_node != controller) + return -EINVAL; + if (intsize < 1) + return -EINVAL; + + *out_hwirq = intspec[0]; + *out_type = IRQ_TYPE_NONE; + if (intsize > 1) + *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; + return 0; +} + +struct irq_domain_ops irq_domain_simple_ops = { + .dt_translate = irq_domain_simple_dt_translate, +}; +EXPORT_SYMBOL_GPL(irq_domain_simple_ops); + +/** + * irq_domain_create_simple() - Set up a 'simple' translation range + */ +void irq_domain_add_simple(struct device_node *controller, int irq_base) +{ + struct irq_domain *domain; + + domain = kzalloc(sizeof(*domain), GFP_KERNEL); + if (!domain) { + WARN_ON(1); + return; + } + + domain->irq_base = irq_base; + domain->of_node = of_node_get(controller); + domain->ops = &irq_domain_simple_ops; + irq_domain_add(domain); +} +EXPORT_SYMBOL_GPL(irq_domain_add_simple); + +void irq_domain_generate_simple(const struct of_device_id *match, + u64 phys_base, unsigned int irq_start) +{ + struct device_node *node; + pr_info("looking for phys_base=%llx, irq_start=%i\n", + (unsigned long long) phys_base, (int) irq_start); + node = of_find_matching_node_by_address(NULL, match, phys_base); + if (node) + irq_domain_add_simple(node, irq_start); + else + pr_info("no node found\n"); +} +EXPORT_SYMBOL_GPL(irq_domain_generate_simple); #endif /* CONFIG_OF_IRQ */ -- cgit v1.2.3 From 43316044d4f64da008d6aca7d4b60771b9a24eb8 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:55:18 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add basic framework The WatchDog Timer Driver Core is a framework that contains the common code for all watchdog-driver's. It also introduces a watchdog device structure and the operations that go with it. This is the introduction of this framework. This part supports the minimal watchdog userspace API (or with other words: the functionality to use /dev/watchdog's open, release and write functionality as defined in the simplest watchdog API). Extra functionality will follow in the next set of patches. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/00-INDEX | 2 + Documentation/watchdog/watchdog-kernel-api.txt | 119 +++++++++++++ drivers/watchdog/Kconfig | 11 ++ drivers/watchdog/Makefile | 4 + drivers/watchdog/watchdog_core.c | 101 +++++++++++ drivers/watchdog/watchdog_dev.c | 235 +++++++++++++++++++++++++ drivers/watchdog/watchdog_dev.h | 33 ++++ include/linux/watchdog.h | 61 +++++++ 8 files changed, 566 insertions(+) create mode 100644 Documentation/watchdog/watchdog-kernel-api.txt create mode 100644 drivers/watchdog/watchdog_core.c create mode 100644 drivers/watchdog/watchdog_dev.c create mode 100644 drivers/watchdog/watchdog_dev.h (limited to 'include') diff --git a/Documentation/watchdog/00-INDEX b/Documentation/watchdog/00-INDEX index ee994513a9b1..fc51128071c2 100644 --- a/Documentation/watchdog/00-INDEX +++ b/Documentation/watchdog/00-INDEX @@ -8,6 +8,8 @@ src/ - directory holding watchdog related example programs. watchdog-api.txt - description of the Linux Watchdog driver API. +watchdog-kernel-api.txt + - description of the Linux WatchDog Timer Driver Core kernel API. watchdog-parameters.txt - information on driver parameters (for drivers other than the ones that have driver-specific files here) diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt new file mode 100644 index 000000000000..3db67e74b80e --- /dev/null +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -0,0 +1,119 @@ +The Linux WatchDog Timer Driver Core kernel API. +=============================================== +Last reviewed: 22-Jul-2011 + +Wim Van Sebroeck + +Introduction +------------ +This document does not describe what a WatchDog Timer (WDT) Driver or Device is. +It also does not describe the API which can be used by user space to communicate +with a WatchDog Timer. If you want to know this then please read the following +file: Documentation/watchdog/watchdog-api.txt . + +So what does this document describe? It describes the API that can be used by +WatchDog Timer Drivers that want to use the WatchDog Timer Driver Core +Framework. This framework provides all interfacing towards user space so that +the same code does not have to be reproduced each time. This also means that +a watchdog timer driver then only needs to provide the different routines +(operations) that control the watchdog timer (WDT). + +The API +------- +Each watchdog timer driver that wants to use the WatchDog Timer Driver Core +must #include (you would have to do this anyway when +writing a watchdog device driver). This include file contains following +register/unregister routines: + +extern int watchdog_register_device(struct watchdog_device *); +extern void watchdog_unregister_device(struct watchdog_device *); + +The watchdog_register_device routine registers a watchdog timer device. +The parameter of this routine is a pointer to a watchdog_device structure. +This routine returns zero on success and a negative errno code for failure. + +The watchdog_unregister_device routine deregisters a registered watchdog timer +device. The parameter of this routine is the pointer to the registered +watchdog_device structure. + +The watchdog device structure looks like this: + +struct watchdog_device { + const struct watchdog_info *info; + const struct watchdog_ops *ops; + void *driver_data; + unsigned long status; +}; + +It contains following fields: +* info: a pointer to a watchdog_info structure. This structure gives some + additional information about the watchdog timer itself. (Like it's unique name) +* ops: a pointer to the list of watchdog operations that the watchdog supports. +* driver_data: a pointer to the drivers private data of a watchdog device. + This data should only be accessed via the watchdog_set_drvadata and + watchdog_get_drvdata routines. +* status: this field contains a number of status bits that give extra + information about the status of the device (Like: is the device opened via + the /dev/watchdog interface or not, ...). + +The list of watchdog operations is defined as: + +struct watchdog_ops { + struct module *owner; + /* mandatory operations */ + int (*start)(struct watchdog_device *); + int (*stop)(struct watchdog_device *); + /* optional operations */ + int (*ping)(struct watchdog_device *); +}; + +It is important that you first define the module owner of the watchdog timer +driver's operations. This module owner will be used to lock the module when +the watchdog is active. (This to avoid a system crash when you unload the +module and /dev/watchdog is still open). +Some operations are mandatory and some are optional. The mandatory operations +are: +* start: this is a pointer to the routine that starts the watchdog timer + device. + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. +* stop: with this routine the watchdog timer device is being stopped. + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. + Some watchdog timer hardware can only be started and not be stopped. The + driver supporting this hardware needs to make sure that a start and stop + routine is being provided. This can be done by using a timer in the driver + that regularly sends a keepalive ping to the watchdog timer hardware. + +Not all watchdog timer hardware supports the same functionality. That's why +all other routines/operations are optional. They only need to be provided if +they are supported. These optional routines/operations are: +* ping: this is the routine that sends a keepalive ping to the watchdog timer + hardware. + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. + Most hardware that does not support this as a separate function uses the + start function to restart the watchdog timer hardware. And that's also what + the watchdog timer driver core does: to send a keepalive ping to the watchdog + timer hardware it will either use the ping operation (when available) or the + start operation (when the ping operation is not available). + +The status bits should (preferably) be set with the set_bit and clear_bit alike +bit-operations. The status bits that are defined are: +* WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device + was opened via /dev/watchdog. + (This bit should only be used by the WatchDog Timer Driver Core). + +To get or set driver specific data the following two helper functions should be +used: + +static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data) +static inline void *watchdog_get_drvdata(struct watchdog_device *wdd) + +The watchdog_set_drvdata function allows you to add driver specific data. The +arguments of this function are the watchdog device where you want to add the +driver specific data to and a pointer to the data itself. + +The watchdog_get_drvdata function allows you to retrieve driver specific data. +The argument of this function is the watchdog device where you want to retrieve +data from. The function retruns the pointer to the driver specific data. diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 0635e72e0794..f441726ddf2b 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -28,6 +28,17 @@ menuconfig WATCHDOG if WATCHDOG +config WATCHDOG_CORE + bool "WatchDog Timer Driver Core" + ---help--- + Say Y here if you want to use the new watchdog timer driver core. + This driver provides a framework for all watchdog timer drivers + and gives them the /dev/watchdog interface (and later also the + sysfs interface). + + To compile this driver as a module, choose M here: the module will + be called watchdog. + config WATCHDOG_NOWAYOUT bool "Disable watchdog shutdown on close" help diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 9eaa212398d7..55bd5740e910 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -2,6 +2,10 @@ # Makefile for the WatchDog device drivers. # +# The WatchDog Timer Driver Core. +watchdog-objs += watchdog_core.o watchdog_dev.o +obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o + # Only one watchdog can succeed. We probe the ISA/PCI/USB based # watchdog-cards first, then the architecture specific watchdog # drivers and then the architecture independent "softdog" driver. diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c new file mode 100644 index 000000000000..47fc1267ad4e --- /dev/null +++ b/drivers/watchdog/watchdog_core.c @@ -0,0 +1,101 @@ +/* + * watchdog_core.c + * + * (c) Copyright 2008-2011 Alan Cox , + * All Rights Reserved. + * + * (c) Copyright 2008-2011 Wim Van Sebroeck . + * + * This source code is part of the generic code that can be used + * by all the watchdog timer drivers. + * + * Based on source code of the following authors: + * Matt Domsch , + * Rob Radez , + * Rusty Lynch + * Satyam Sharma + * Randy Dunlap + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. + * admit liability nor provide warranty for any of this software. + * This material is provided "AS-IS" and at no charge. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include /* For EXPORT_SYMBOL/module stuff/... */ +#include /* For standard types */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For watchdog specific items */ +#include /* For __init/__exit/... */ + +#include "watchdog_dev.h" /* For watchdog_dev_register/... */ + +/** + * watchdog_register_device() - register a watchdog device + * @wdd: watchdog device + * + * Register a watchdog device with the kernel so that the + * watchdog timer can be accessed from userspace. + * + * A zero is returned on success and a negative errno code for + * failure. + */ +int watchdog_register_device(struct watchdog_device *wdd) +{ + int ret; + + if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) + return -EINVAL; + + /* Mandatory operations need to be supported */ + if (wdd->ops->start == NULL || wdd->ops->stop == NULL) + return -EINVAL; + + /* + * Note: now that all watchdog_device data has been verified, we + * will not check this anymore in other functions. If data gets + * corrupted in a later stage then we expect a kernel panic! + */ + + /* We only support 1 watchdog device via the /dev/watchdog interface */ + ret = watchdog_dev_register(wdd); + if (ret) { + pr_err("error registering /dev/watchdog (err=%d).\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(watchdog_register_device); + +/** + * watchdog_unregister_device() - unregister a watchdog device + * @wdd: watchdog device to unregister + * + * Unregister a watchdog device that was previously successfully + * registered with watchdog_register_device(). + */ +void watchdog_unregister_device(struct watchdog_device *wdd) +{ + int ret; + + if (wdd == NULL) + return; + + ret = watchdog_dev_unregister(wdd); + if (ret) + pr_err("error unregistering /dev/watchdog (err=%d).\n", ret); +} +EXPORT_SYMBOL_GPL(watchdog_unregister_device); + +MODULE_AUTHOR("Alan Cox "); +MODULE_AUTHOR("Wim Van Sebroeck "); +MODULE_DESCRIPTION("WatchDog Timer Driver Core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c new file mode 100644 index 000000000000..366f49ce69b8 --- /dev/null +++ b/drivers/watchdog/watchdog_dev.c @@ -0,0 +1,235 @@ +/* + * watchdog_dev.c + * + * (c) Copyright 2008-2011 Alan Cox , + * All Rights Reserved. + * + * (c) Copyright 2008-2011 Wim Van Sebroeck . + * + * + * This source code is part of the generic code that can be used + * by all the watchdog timer drivers. + * + * This part of the generic code takes care of the following + * misc device: /dev/watchdog. + * + * Based on source code of the following authors: + * Matt Domsch , + * Rob Radez , + * Rusty Lynch + * Satyam Sharma + * Randy Dunlap + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. + * admit liability nor provide warranty for any of this software. + * This material is provided "AS-IS" and at no charge. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include /* For module stuff/... */ +#include /* For standard types (like size_t) */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For file operations */ +#include /* For watchdog specific items */ +#include /* For handling misc devices */ +#include /* For __init/__exit/... */ +#include /* For copy_to_user/put_user/... */ + +/* make sure we only register one /dev/watchdog device */ +static unsigned long watchdog_dev_busy; +/* the watchdog device behind /dev/watchdog */ +static struct watchdog_device *wdd; + +/* + * watchdog_ping: ping the watchdog. + * @wddev: the watchdog device to ping + * + * If the watchdog has no own ping operation then it needs to be + * restarted via the start operation. This wrapper function does + * exactly that. + */ + +static int watchdog_ping(struct watchdog_device *wddev) +{ + if (wddev->ops->ping) + return wddev->ops->ping(wddev); /* ping the watchdog */ + else + return wddev->ops->start(wddev); /* restart the watchdog */ +} + +/* + * watchdog_write: writes to the watchdog. + * @file: file from VFS + * @data: user address of data + * @len: length of data + * @ppos: pointer to the file offset + * + * A write to a watchdog device is defined as a keepalive ping. + */ + +static ssize_t watchdog_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + size_t i; + char c; + + if (len == 0) + return 0; + + for (i = 0; i != len; i++) { + if (get_user(c, data + i)) + return -EFAULT; + } + + /* someone wrote to us, so we send the watchdog a keepalive ping */ + watchdog_ping(wdd); + + return len; +} + +/* + * watchdog_open: open the /dev/watchdog device. + * @inode: inode of device + * @file: file handle to device + * + * When the /dev/watchdog device gets opened, we start the watchdog. + * Watch out: the /dev/watchdog device is single open, so we make sure + * it can only be opened once. + */ + +static int watchdog_open(struct inode *inode, struct file *file) +{ + int err = -EBUSY; + + /* the watchdog is single open! */ + if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) + return -EBUSY; + + /* + * If the /dev/watchdog device is open, we don't want the module + * to be unloaded. + */ + if (!try_module_get(wdd->ops->owner)) + goto out; + + err = wdd->ops->start(wdd); + if (err < 0) + goto out_mod; + + /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ + return nonseekable_open(inode, file); + +out_mod: + module_put(wdd->ops->owner); +out: + clear_bit(WDOG_DEV_OPEN, &wdd->status); + return err; +} + +/* + * watchdog_release: release the /dev/watchdog device. + * @inode: inode of device + * @file: file handle to device + * + * This is the code for when /dev/watchdog gets closed. + */ + +static int watchdog_release(struct inode *inode, struct file *file) +{ + int err; + + err = wdd->ops->stop(wdd); + if (err != 0) { + pr_crit("%s: watchdog did not stop!\n", wdd->info->identity); + watchdog_ping(wdd); + } + + /* Allow the owner module to be unloaded again */ + module_put(wdd->ops->owner); + + /* make sure that /dev/watchdog can be re-opened */ + clear_bit(WDOG_DEV_OPEN, &wdd->status); + + return 0; +} + +static const struct file_operations watchdog_fops = { + .owner = THIS_MODULE, + .write = watchdog_write, + .open = watchdog_open, + .release = watchdog_release, +}; + +static struct miscdevice watchdog_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &watchdog_fops, +}; + +/* + * watchdog_dev_register: + * @watchdog: watchdog device + * + * Register a watchdog device as /dev/watchdog. /dev/watchdog + * is actually a miscdevice and thus we set it up like that. + */ + +int watchdog_dev_register(struct watchdog_device *watchdog) +{ + int err; + + /* Only one device can register for /dev/watchdog */ + if (test_and_set_bit(0, &watchdog_dev_busy)) { + pr_err("only one watchdog can use /dev/watchdog.\n"); + return -EBUSY; + } + + wdd = watchdog; + + err = misc_register(&watchdog_miscdev); + if (err != 0) { + pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", + watchdog->info->identity, WATCHDOG_MINOR, err); + goto out; + } + + return 0; + +out: + wdd = NULL; + clear_bit(0, &watchdog_dev_busy); + return err; +} + +/* + * watchdog_dev_unregister: + * @watchdog: watchdog device + * + * Deregister the /dev/watchdog device. + */ + +int watchdog_dev_unregister(struct watchdog_device *watchdog) +{ + /* Check that a watchdog device was registered in the past */ + if (!test_bit(0, &watchdog_dev_busy) || !wdd) + return -ENODEV; + + /* We can only unregister the watchdog device that was registered */ + if (watchdog != wdd) { + pr_err("%s: watchdog was not registered as /dev/watchdog.\n", + watchdog->info->identity); + return -ENODEV; + } + + misc_deregister(&watchdog_miscdev); + wdd = NULL; + clear_bit(0, &watchdog_dev_busy); + return 0; +} diff --git a/drivers/watchdog/watchdog_dev.h b/drivers/watchdog/watchdog_dev.h new file mode 100644 index 000000000000..bc7612be25ce --- /dev/null +++ b/drivers/watchdog/watchdog_dev.h @@ -0,0 +1,33 @@ +/* + * watchdog_core.h + * + * (c) Copyright 2008-2011 Alan Cox , + * All Rights Reserved. + * + * (c) Copyright 2008-2011 Wim Van Sebroeck . + * + * This source code is part of the generic code that can be used + * by all the watchdog timer drivers. + * + * Based on source code of the following authors: + * Matt Domsch , + * Rob Radez , + * Rusty Lynch + * Satyam Sharma + * Randy Dunlap + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. + * admit liability nor provide warranty for any of this software. + * This material is provided "AS-IS" and at no charge. + */ + +/* + * Functions/procedures to be called by the core + */ +int watchdog_dev_register(struct watchdog_device *); +int watchdog_dev_unregister(struct watchdog_device *); diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 011bcfeb9f09..5ab31bfd2906 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -59,6 +59,67 @@ struct watchdog_info { #define WATCHDOG_NOWAYOUT 0 #endif +struct watchdog_ops; +struct watchdog_device; + +/** struct watchdog_ops - The watchdog-devices operations + * + * @owner: The module owner. + * @start: The routine for starting the watchdog device. + * @stop: The routine for stopping the watchdog device. + * @ping: The routine that sends a keepalive ping to the watchdog device. + * + * The watchdog_ops structure contains a list of low-level operations + * that control a watchdog device. It also contains the module that owns + * these operations. The start and stop function are mandatory, all other + * functions are optonal. + */ +struct watchdog_ops { + struct module *owner; + /* mandatory operations */ + int (*start)(struct watchdog_device *); + int (*stop)(struct watchdog_device *); + /* optional operations */ + int (*ping)(struct watchdog_device *); +}; + +/** struct watchdog_device - The structure that defines a watchdog device + * + * @info: Pointer to a watchdog_info structure. + * @ops: Pointer to the list of watchdog operations. + * @driver-data:Pointer to the drivers private data. + * @status: Field that contains the devices internal status bits. + * + * The watchdog_device structure contains all information about a + * watchdog timer device. + * + * The driver-data field may not be accessed directly. It must be accessed + * via the watchdog_set_drvdata and watchdog_get_drvdata helpers. + */ +struct watchdog_device { + const struct watchdog_info *info; + const struct watchdog_ops *ops; + void *driver_data; + unsigned long status; +/* Bit numbers for status flags */ +#define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ +}; + +/* Use the following functions to manipulate watchdog driver specific data */ +static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data) +{ + wdd->driver_data = data; +} + +static inline void *watchdog_get_drvdata(struct watchdog_device *wdd) +{ + return wdd->driver_data; +} + +/* drivers/watchdog/core/watchdog_core.c */ +extern int watchdog_register_device(struct watchdog_device *); +extern void watchdog_unregister_device(struct watchdog_device *); + #endif /* __KERNEL__ */ #endif /* ifndef _LINUX_WATCHDOG_H */ -- cgit v1.2.3 From 2fa03560ab3a6dd83cad9bfd5692179fc2ceabb3 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:56:38 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add basic ioctl functionality This part add's the basic ioctl functionality to the WatchDog Timer Driver Core framework. The supported ioctl call's are: WDIOC_GETSUPPORT WDIOC_GETSTATUS WDIOC_GETBOOTSTATUS Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 6 +++++ drivers/watchdog/watchdog_dev.c | 32 ++++++++++++++++++++++++++ include/linux/watchdog.h | 4 ++++ 3 files changed, 42 insertions(+) (limited to 'include') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 3db67e74b80e..2bdc6dc6e04c 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -41,6 +41,7 @@ The watchdog device structure looks like this: struct watchdog_device { const struct watchdog_info *info; const struct watchdog_ops *ops; + unsigned int bootstatus; void *driver_data; unsigned long status; }; @@ -49,6 +50,8 @@ It contains following fields: * info: a pointer to a watchdog_info structure. This structure gives some additional information about the watchdog timer itself. (Like it's unique name) * ops: a pointer to the list of watchdog operations that the watchdog supports. +* bootstatus: status of the device after booting (reported with watchdog + WDIOF_* status bits). * driver_data: a pointer to the drivers private data of a watchdog device. This data should only be accessed via the watchdog_set_drvadata and watchdog_get_drvdata routines. @@ -65,6 +68,7 @@ struct watchdog_ops { int (*stop)(struct watchdog_device *); /* optional operations */ int (*ping)(struct watchdog_device *); + unsigned int (*status)(struct watchdog_device *); }; It is important that you first define the module owner of the watchdog timer @@ -97,6 +101,8 @@ they are supported. These optional routines/operations are: the watchdog timer driver core does: to send a keepalive ping to the watchdog timer hardware it will either use the ping operation (when available) or the start operation (when the ping operation is not available). +* status: this routine checks the status of the watchdog timer device. The + status of the device is reported with watchdog WDIOF_* status flags/bits. The status bits should (preferably) be set with the set_bit and clear_bit alike bit-operations. The status bits that are defined are: diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 366f49ce69b8..00a611293065 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -94,6 +94,37 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, return len; } +/* + * watchdog_ioctl: handle the different ioctl's for the watchdog device. + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. + */ + +static long watchdog_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + unsigned int val; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, wdd->info, + sizeof(struct watchdog_info)) ? -EFAULT : 0; + case WDIOC_GETSTATUS: + val = wdd->ops->status ? wdd->ops->status(wdd) : 0; + return put_user(val, p); + case WDIOC_GETBOOTSTATUS: + return put_user(wdd->bootstatus, p); + default: + return -ENOTTY; + } +} + /* * watchdog_open: open the /dev/watchdog device. * @inode: inode of device @@ -163,6 +194,7 @@ static int watchdog_release(struct inode *inode, struct file *file) static const struct file_operations watchdog_fops = { .owner = THIS_MODULE, .write = watchdog_write, + .unlocked_ioctl = watchdog_ioctl, .open = watchdog_open, .release = watchdog_release, }; diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 5ab31bfd2906..29ff80807dd4 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -68,6 +68,7 @@ struct watchdog_device; * @start: The routine for starting the watchdog device. * @stop: The routine for stopping the watchdog device. * @ping: The routine that sends a keepalive ping to the watchdog device. + * @status: The routine that shows the status of the watchdog device. * * The watchdog_ops structure contains a list of low-level operations * that control a watchdog device. It also contains the module that owns @@ -81,12 +82,14 @@ struct watchdog_ops { int (*stop)(struct watchdog_device *); /* optional operations */ int (*ping)(struct watchdog_device *); + unsigned int (*status)(struct watchdog_device *); }; /** struct watchdog_device - The structure that defines a watchdog device * * @info: Pointer to a watchdog_info structure. * @ops: Pointer to the list of watchdog operations. + * @bootstatus: Status of the watchdog device at boot. * @driver-data:Pointer to the drivers private data. * @status: Field that contains the devices internal status bits. * @@ -99,6 +102,7 @@ struct watchdog_ops { struct watchdog_device { const struct watchdog_info *info; const struct watchdog_ops *ops; + unsigned int bootstatus; void *driver_data; unsigned long status; /* Bit numbers for status flags */ -- cgit v1.2.3 From 234445b4e4542f3e0f216459245ab369a18adcf2 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:57:55 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add WDIOC_SETOPTIONS ioctl This part add's the WDIOC_SETOPTIONS ioctl functionality to the WatchDog Timer Driver Core framework. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 9 ++- drivers/watchdog/watchdog_dev.c | 79 +++++++++++++++++++++++--- include/linux/watchdog.h | 1 + 3 files changed, 80 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index abbcf2ce8f62..429f81bf0cf7 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -56,8 +56,9 @@ It contains following fields: This data should only be accessed via the watchdog_set_drvadata and watchdog_get_drvdata routines. * status: this field contains a number of status bits that give extra - information about the status of the device (Like: is the device opened via - the /dev/watchdog interface or not, ...). + information about the status of the device (Like: is the watchdog timer + running/active, is the device opened via the /dev/watchdog interface or not, + ...). The list of watchdog operations is defined as: @@ -109,6 +110,10 @@ they are supported. These optional routines/operations are: The status bits should (preferably) be set with the set_bit and clear_bit alike bit-operations. The status bits that are defined are: +* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device + is active or not. When the watchdog is active after booting, then you should + set this status bit (Note: when you register the watchdog timer device with + this bit set, then opening /dev/watchdog will skip the start operation) * WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device was opened via /dev/watchdog. (This bit should only be used by the WatchDog Timer Driver Core). diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 2fb4cecd50d8..9f5550e16ab5 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -54,14 +54,64 @@ static struct watchdog_device *wdd; * If the watchdog has no own ping operation then it needs to be * restarted via the start operation. This wrapper function does * exactly that. + * We only ping when the watchdog device is running. */ static int watchdog_ping(struct watchdog_device *wddev) { - if (wddev->ops->ping) - return wddev->ops->ping(wddev); /* ping the watchdog */ - else - return wddev->ops->start(wddev); /* restart the watchdog */ + if (test_bit(WDOG_ACTIVE, &wdd->status)) { + if (wddev->ops->ping) + return wddev->ops->ping(wddev); /* ping the watchdog */ + else + return wddev->ops->start(wddev); /* restart watchdog */ + } + return 0; +} + +/* + * watchdog_start: wrapper to start the watchdog. + * @wddev: the watchdog device to start + * + * Start the watchdog if it is not active and mark it active. + * This function returns zero on success or a negative errno code for + * failure. + */ + +static int watchdog_start(struct watchdog_device *wddev) +{ + int err; + + if (!test_bit(WDOG_ACTIVE, &wdd->status)) { + err = wddev->ops->start(wddev); + if (err < 0) + return err; + + set_bit(WDOG_ACTIVE, &wdd->status); + } + return 0; +} + +/* + * watchdog_stop: wrapper to stop the watchdog. + * @wddev: the watchdog device to stop + * + * Stop the watchdog if it is still active and unmark it active. + * This function returns zero on success or a negative errno code for + * failure. + */ + +static int watchdog_stop(struct watchdog_device *wddev) +{ + int err; + + if (test_bit(WDOG_ACTIVE, &wdd->status)) { + err = wddev->ops->stop(wddev); + if (err < 0) + return err; + + clear_bit(WDOG_ACTIVE, &wdd->status); + } + return 0; } /* @@ -110,6 +160,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, void __user *argp = (void __user *)arg; int __user *p = argp; unsigned int val; + int err; switch (cmd) { case WDIOC_GETSUPPORT: @@ -120,6 +171,20 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, return put_user(val, p); case WDIOC_GETBOOTSTATUS: return put_user(wdd->bootstatus, p); + case WDIOC_SETOPTIONS: + if (get_user(val, p)) + return -EFAULT; + if (val & WDIOS_DISABLECARD) { + err = watchdog_stop(wdd); + if (err < 0) + return err; + } + if (val & WDIOS_ENABLECARD) { + err = watchdog_start(wdd); + if (err < 0) + return err; + } + return 0; case WDIOC_KEEPALIVE: if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) return -EOPNOTSUPP; @@ -155,7 +220,7 @@ static int watchdog_open(struct inode *inode, struct file *file) if (!try_module_get(wdd->ops->owner)) goto out; - err = wdd->ops->start(wdd); + err = watchdog_start(wdd); if (err < 0) goto out_mod; @@ -181,8 +246,8 @@ static int watchdog_release(struct inode *inode, struct file *file) { int err; - err = wdd->ops->stop(wdd); - if (err != 0) { + err = watchdog_stop(wdd); + if (err < 0) { pr_crit("%s: watchdog did not stop!\n", wdd->info->identity); watchdog_ping(wdd); } diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 29ff80807dd4..db46fe89563e 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -106,6 +106,7 @@ struct watchdog_device { void *driver_data; unsigned long status; /* Bit numbers for status flags */ +#define WDOG_ACTIVE 0 /* Is the watchdog running/active */ #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ }; -- cgit v1.2.3 From 014d694e5d59e4219803cd14deaae496d86e4910 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:58:21 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add WDIOC_SETTIMEOUT and WDIOC_GETTIMEOUT ioctl This part add's the WDIOC_SETTIMEOUT and WDIOC_GETTIMEOUT ioctl functionality to the WatchDog Timer Driver Core framework. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 10 ++++++++++ drivers/watchdog/watchdog_dev.c | 20 ++++++++++++++++++++ include/linux/watchdog.h | 4 ++++ 3 files changed, 34 insertions(+) (limited to 'include') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 429f81bf0cf7..acdee39fb08a 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -42,6 +42,7 @@ struct watchdog_device { const struct watchdog_info *info; const struct watchdog_ops *ops; unsigned int bootstatus; + unsigned int timeout; void *driver_data; unsigned long status; }; @@ -50,6 +51,7 @@ It contains following fields: * info: a pointer to a watchdog_info structure. This structure gives some additional information about the watchdog timer itself. (Like it's unique name) * ops: a pointer to the list of watchdog operations that the watchdog supports. +* timeout: the watchdog timer's timeout value (in seconds). * bootstatus: status of the device after booting (reported with watchdog WDIOF_* status bits). * driver_data: a pointer to the drivers private data of a watchdog device. @@ -70,6 +72,7 @@ struct watchdog_ops { /* optional operations */ int (*ping)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *); + int (*set_timeout)(struct watchdog_device *, unsigned int); }; It is important that you first define the module owner of the watchdog timer @@ -107,6 +110,13 @@ they are supported. These optional routines/operations are: info structure). * status: this routine checks the status of the watchdog timer device. The status of the device is reported with watchdog WDIOF_* status flags/bits. +* set_timeout: this routine checks and changes the timeout of the watchdog + timer device. It returns 0 on success, -EINVAL for "parameter out of range" + and -EIO for "could not write value to the watchdog". On success the timeout + value of the watchdog_device will be changed to the value that was just used + to re-program the watchdog timer device. + (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the + watchdog's info structure). The status bits should (preferably) be set with the set_bit and clear_bit alike bit-operations. The status bits that are defined are: diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 9f5550e16ab5..2c0289deaadd 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -190,6 +190,26 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, return -EOPNOTSUPP; watchdog_ping(wdd); return 0; + case WDIOC_SETTIMEOUT: + if ((wdd->ops->set_timeout == NULL) || + !(wdd->info->options & WDIOF_SETTIMEOUT)) + return -EOPNOTSUPP; + if (get_user(val, p)) + return -EFAULT; + err = wdd->ops->set_timeout(wdd, val); + if (err < 0) + return err; + wdd->timeout = val; + /* If the watchdog is active then we send a keepalive ping + * to make sure that the watchdog keep's running (and if + * possible that it takes the new timeout) */ + watchdog_ping(wdd); + /* Fall */ + case WDIOC_GETTIMEOUT: + /* timeout == 0 means that we don't know the timeout */ + if (wdd->timeout == 0) + return -EOPNOTSUPP; + return put_user(wdd->timeout, p); default: return -ENOTTY; } diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index db46fe89563e..9f33efe199d1 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -69,6 +69,7 @@ struct watchdog_device; * @stop: The routine for stopping the watchdog device. * @ping: The routine that sends a keepalive ping to the watchdog device. * @status: The routine that shows the status of the watchdog device. + * @set_timeout:The routine for setting the watchdog devices timeout value. * * The watchdog_ops structure contains a list of low-level operations * that control a watchdog device. It also contains the module that owns @@ -83,6 +84,7 @@ struct watchdog_ops { /* optional operations */ int (*ping)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *); + int (*set_timeout)(struct watchdog_device *, unsigned int); }; /** struct watchdog_device - The structure that defines a watchdog device @@ -90,6 +92,7 @@ struct watchdog_ops { * @info: Pointer to a watchdog_info structure. * @ops: Pointer to the list of watchdog operations. * @bootstatus: Status of the watchdog device at boot. + * @timeout: The watchdog devices timeout value. * @driver-data:Pointer to the drivers private data. * @status: Field that contains the devices internal status bits. * @@ -103,6 +106,7 @@ struct watchdog_device { const struct watchdog_info *info; const struct watchdog_ops *ops; unsigned int bootstatus; + unsigned int timeout; void *driver_data; unsigned long status; /* Bit numbers for status flags */ -- cgit v1.2.3 From 017cf0805105496ab1880e236cb3e4bf156fb915 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:58:54 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add Magic Close feature Add support for the Magic Close feature to the WatchDog Timer Driver Core framework. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 7 +++++++ drivers/watchdog/watchdog_dev.c | 27 +++++++++++++++++++++++--- include/linux/watchdog.h | 1 + 3 files changed, 32 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index acdee39fb08a..41d552698ada 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -127,6 +127,13 @@ bit-operations. The status bits that are defined are: * WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device was opened via /dev/watchdog. (This bit should only be used by the WatchDog Timer Driver Core). +* WDOG_ALLOW_RELEASE: this bit stores whether or not the magic close character + has been sent (so that we can support the magic close feature). + (This bit should only be used by the WatchDog Timer Driver Core). + +Note: The WatchDog Timer Driver Core supports the magic close feature. To use +the magic close feature you must set the WDIOF_MAGICCLOSE bit in the options +field of the watchdog's info structure. To get or set driver specific data the following two helper functions should be used: diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 2c0289deaadd..db40c6c79ef8 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -122,6 +122,8 @@ static int watchdog_stop(struct watchdog_device *wddev) * @ppos: pointer to the file offset * * A write to a watchdog device is defined as a keepalive ping. + * Writing the magic 'V' sequence allows the next close to turn + * off the watchdog. */ static ssize_t watchdog_write(struct file *file, const char __user *data, @@ -133,9 +135,18 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, if (len == 0) return 0; + /* + * Note: just in case someone wrote the magic character + * five months ago... + */ + clear_bit(WDOG_ALLOW_RELEASE, &wdd->status); + + /* scan to see whether or not we got the magic character */ for (i = 0; i != len; i++) { if (get_user(c, data + i)) return -EFAULT; + if (c == 'V') + set_bit(WDOG_ALLOW_RELEASE, &wdd->status); } /* someone wrote to us, so we send the watchdog a keepalive ping */ @@ -259,14 +270,24 @@ out: * @inode: inode of device * @file: file handle to device * - * This is the code for when /dev/watchdog gets closed. + * This is the code for when /dev/watchdog gets closed. We will only + * stop the watchdog when we have received the magic char, else the + * watchdog will keep running. */ static int watchdog_release(struct inode *inode, struct file *file) { - int err; + int err = -EBUSY; + + /* + * We only stop the watchdog if we received the magic character + * or if WDIOF_MAGICCLOSE is not set + */ + if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || + !(wdd->info->options & WDIOF_MAGICCLOSE)) + err = watchdog_stop(wdd); - err = watchdog_stop(wdd); + /* If the watchdog was not stopped, send a keepalive ping */ if (err < 0) { pr_crit("%s: watchdog did not stop!\n", wdd->info->identity); watchdog_ping(wdd); diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 9f33efe199d1..e9881ca2452b 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -112,6 +112,7 @@ struct watchdog_device { /* Bit numbers for status flags */ #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ +#define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ }; /* Use the following functions to manipulate watchdog driver specific data */ -- cgit v1.2.3 From 7e192b9c4234d29bdc20ac8d0a67edf7624b4206 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:59:17 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add nowayout feature Add support for the nowayout feature to the WatchDog Timer Driver Core framework. This feature prevents the watchdog timer from being stopped. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 13 ++++++++----- drivers/watchdog/watchdog_dev.c | 18 +++++++++++++----- include/linux/watchdog.h | 1 + 3 files changed, 22 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 41d552698ada..785fa0c996a4 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -59,8 +59,8 @@ It contains following fields: watchdog_get_drvdata routines. * status: this field contains a number of status bits that give extra information about the status of the device (Like: is the watchdog timer - running/active, is the device opened via the /dev/watchdog interface or not, - ...). + running/active, is the nowayout bit set, is the device opened via + the /dev/watchdog interface or not, ...). The list of watchdog operations is defined as: @@ -130,10 +130,13 @@ bit-operations. The status bits that are defined are: * WDOG_ALLOW_RELEASE: this bit stores whether or not the magic close character has been sent (so that we can support the magic close feature). (This bit should only be used by the WatchDog Timer Driver Core). +* WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. + If this bit is set then the watchdog timer will not be able to stop. -Note: The WatchDog Timer Driver Core supports the magic close feature. To use -the magic close feature you must set the WDIOF_MAGICCLOSE bit in the options -field of the watchdog's info structure. +Note: The WatchDog Timer Driver Core supports the magic close feature and +the nowayout feature. To use the magic close feature you must set the +WDIOF_MAGICCLOSE bit in the options field of the watchdog's info structure. +The nowayout feature will overrule the magic close feature. To get or set driver specific data the following two helper functions should be used: diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index db40c6c79ef8..ac20f92347b1 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -98,11 +98,18 @@ static int watchdog_start(struct watchdog_device *wddev) * Stop the watchdog if it is still active and unmark it active. * This function returns zero on success or a negative errno code for * failure. + * If the 'nowayout' feature was set, the watchdog cannot be stopped. */ static int watchdog_stop(struct watchdog_device *wddev) { - int err; + int err = -EBUSY; + + if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { + pr_info("%s: nowayout prevents watchdog to be stopped!\n", + wdd->info->identity); + return err; + } if (test_bit(WDOG_ACTIVE, &wdd->status)) { err = wddev->ops->stop(wddev); @@ -123,7 +130,7 @@ static int watchdog_stop(struct watchdog_device *wddev) * * A write to a watchdog device is defined as a keepalive ping. * Writing the magic 'V' sequence allows the next close to turn - * off the watchdog. + * off the watchdog (if 'nowayout' is not set). */ static ssize_t watchdog_write(struct file *file, const char __user *data, @@ -271,8 +278,8 @@ out: * @file: file handle to device * * This is the code for when /dev/watchdog gets closed. We will only - * stop the watchdog when we have received the magic char, else the - * watchdog will keep running. + * stop the watchdog when we have received the magic char (and nowayout + * was not set), else the watchdog will keep running. */ static int watchdog_release(struct inode *inode, struct file *file) @@ -281,7 +288,8 @@ static int watchdog_release(struct inode *inode, struct file *file) /* * We only stop the watchdog if we received the magic character - * or if WDIOF_MAGICCLOSE is not set + * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then + * watchdog_stop will fail. */ if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || !(wdd->info->options & WDIOF_MAGICCLOSE)) diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index e9881ca2452b..f719883c5141 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -113,6 +113,7 @@ struct watchdog_device { #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ +#define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ }; /* Use the following functions to manipulate watchdog driver specific data */ -- cgit v1.2.3 From 78d88fc01202b088573c962e2885556a5e99bf74 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:59:49 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add ioctl call Add support for extra ioctl calls by adding a ioctl watchdog operation. This operation will be called before we do our own handling of ioctl commands. This way we can override the internal ioctl command handling and we can also add extra ioctl commands. The ioctl watchdog operation should return the appropriate error codes or -ENOIOCTLCMD if the ioctl command should be handled through the internal ioctl handling of the framework. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 5 +++++ drivers/watchdog/watchdog_dev.c | 6 ++++++ include/linux/watchdog.h | 2 ++ 3 files changed, 13 insertions(+) (limited to 'include') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 785fa0c996a4..829955bd245e 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -73,6 +73,7 @@ struct watchdog_ops { int (*ping)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *); int (*set_timeout)(struct watchdog_device *, unsigned int); + long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); }; It is important that you first define the module owner of the watchdog timer @@ -117,6 +118,10 @@ they are supported. These optional routines/operations are: to re-program the watchdog timer device. (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the watchdog's info structure). +* ioctl: if this routine is present then it will be called first before we do + our own internal ioctl call handling. This routine should return -ENOIOCTLCMD + if a command is not supported. The parameters that are passed to the ioctl + call are: watchdog_device, cmd and arg. The status bits should (preferably) be set with the set_bit and clear_bit alike bit-operations. The status bits that are defined are: diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index ac20f92347b1..e7134a5979c6 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -180,6 +180,12 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, unsigned int val; int err; + if (wdd->ops->ioctl) { + err = wdd->ops->ioctl(wdd, cmd, arg); + if (err != -ENOIOCTLCMD) + return err; + } + switch (cmd) { case WDIOC_GETSUPPORT: return copy_to_user(argp, wdd->info, diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index f719883c5141..325d90b6641b 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -70,6 +70,7 @@ struct watchdog_device; * @ping: The routine that sends a keepalive ping to the watchdog device. * @status: The routine that shows the status of the watchdog device. * @set_timeout:The routine for setting the watchdog devices timeout value. + * @ioctl: The routines that handles extra ioctl calls. * * The watchdog_ops structure contains a list of low-level operations * that control a watchdog device. It also contains the module that owns @@ -85,6 +86,7 @@ struct watchdog_ops { int (*ping)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *); int (*set_timeout)(struct watchdog_device *, unsigned int); + long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); }; /** struct watchdog_device - The structure that defines a watchdog device -- cgit v1.2.3 From 3f43f68e29f1dcb853d70280c7412fc0ef9a0da6 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 19:00:16 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add minimum and max timeout Add min_timeout (minimum timeout) and max_timeout values so that the framework can check if the new timeout value is between the minimum and maximum timeout values. If both values are 0, then the framework will leave the check for the watchdog device driver itself. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 4 ++++ drivers/watchdog/watchdog_core.c | 10 ++++++++++ drivers/watchdog/watchdog_dev.c | 3 +++ include/linux/watchdog.h | 4 ++++ 4 files changed, 21 insertions(+) (limited to 'include') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 829955bd245e..4f7c894244d2 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -43,6 +43,8 @@ struct watchdog_device { const struct watchdog_ops *ops; unsigned int bootstatus; unsigned int timeout; + unsigned int min_timeout; + unsigned int max_timeout; void *driver_data; unsigned long status; }; @@ -52,6 +54,8 @@ It contains following fields: additional information about the watchdog timer itself. (Like it's unique name) * ops: a pointer to the list of watchdog operations that the watchdog supports. * timeout: the watchdog timer's timeout value (in seconds). +* min_timeout: the watchdog timer's minimum timeout value (in seconds). +* max_timeout: the watchdog timer's maximum timeout value (in seconds). * bootstatus: status of the device after booting (reported with watchdog WDIOF_* status bits). * driver_data: a pointer to the drivers private data of a watchdog device. diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 47fc1267ad4e..cfa1a1518aad 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -58,6 +58,16 @@ int watchdog_register_device(struct watchdog_device *wdd) if (wdd->ops->start == NULL || wdd->ops->stop == NULL) return -EINVAL; + /* + * Check that we have valid min and max timeout values, if + * not reset them both to 0 (=not used or unknown) + */ + if (wdd->min_timeout > wdd->max_timeout) { + pr_info("Invalid min and max timeout values, resetting to 0!\n"); + wdd->min_timeout = 0; + wdd->max_timeout = 0; + } + /* * Note: now that all watchdog_device data has been verified, we * will not check this anymore in other functions. If data gets diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index e7134a5979c6..d33520d0b4c9 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -220,6 +220,9 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, return -EOPNOTSUPP; if (get_user(val, p)) return -EFAULT; + if ((wdd->max_timeout != 0) && + (val < wdd->min_timeout || val > wdd->max_timeout)) + return -EINVAL; err = wdd->ops->set_timeout(wdd, val); if (err < 0) return err; diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 325d90b6641b..111843f88b2a 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -95,6 +95,8 @@ struct watchdog_ops { * @ops: Pointer to the list of watchdog operations. * @bootstatus: Status of the watchdog device at boot. * @timeout: The watchdog devices timeout value. + * @min_timeout:The watchdog devices minimum timeout value. + * @max_timeout:The watchdog devices maximum timeout value. * @driver-data:Pointer to the drivers private data. * @status: Field that contains the devices internal status bits. * @@ -109,6 +111,8 @@ struct watchdog_device { const struct watchdog_ops *ops; unsigned int bootstatus; unsigned int timeout; + unsigned int min_timeout; + unsigned int max_timeout; void *driver_data; unsigned long status; /* Bit numbers for status flags */ -- cgit v1.2.3 From 302ff541d981e58cd455fdbd6a90bd74d0f2109b Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Wed, 27 Jul 2011 15:10:23 -0700 Subject: [SCSI] fcoe: remove unused ptype field in fcoe_rcv_info There is no need to cache the ptype in fcoe_rcv_info struct as it is never used anywhere. Signed-off-by: Yi Zou Tested-by: Ross Brattain Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 1 - drivers/scsi/fcoe/fcoe.c | 1 - include/scsi/fc_frame.h | 1 - 3 files changed, 3 deletions(-) (limited to 'include') diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 833e9122e018..7cb2cd48b17b 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -418,7 +418,6 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev, fr = fcoe_dev_from_skb(skb); fr->fr_dev = lport; - fr->ptype = ptype; bg = &bnx2fc_global; spin_lock_bh(&bg->fcoe_rx_list.lock); diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 204fa8d4b4ab..f7547fb000c0 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1350,7 +1350,6 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, fr = fcoe_dev_from_skb(skb); fr->fr_dev = lport; - fr->ptype = ptype; /* * In case the incoming frame's exchange is originated from diff --git a/include/scsi/fc_frame.h b/include/scsi/fc_frame.h index 4ad02041b667..8225d8063ec4 100644 --- a/include/scsi/fc_frame.h +++ b/include/scsi/fc_frame.h @@ -78,7 +78,6 @@ struct fc_frame { }; struct fcoe_rcv_info { - struct packet_type *ptype; struct fc_lport *fr_dev; /* transport layer private pointer */ struct fc_seq *fr_seq; /* for use with exchange manager */ struct fc_fcp_pkt *fr_fsp; /* for the corresponding fcp I/O */ -- cgit v1.2.3 From 3038bbdf7404ae3948385cbde30df946579d4e3a Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Thu, 28 Jul 2011 16:25:41 +0100 Subject: of/gpio: export of_gpio_simple_xlate Allow GPIO drivers to use of_gpio_simple_xlate. This is useful for the generic GPIO driver for example where gpio_chip is embedded in bgpio_chip and doesn't need of_mm_gpio_chip but has a simple 1:1 GPIO mapping. Cc: Anton Vorontsov Cc: Grant Likely Signed-off-by: Jamie Iles Signed-off-by: Grant Likely --- drivers/of/gpio.c | 5 +++-- include/linux/of_gpio.h | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c index 3007662ac614..ef0105fa52b1 100644 --- a/drivers/of/gpio.c +++ b/drivers/of/gpio.c @@ -127,8 +127,8 @@ EXPORT_SYMBOL(of_gpio_count); * gpio chips. This function performs only one sanity check: whether gpio * is less than ngpios (that is specified in the gpio_chip). */ -static int of_gpio_simple_xlate(struct gpio_chip *gc, struct device_node *np, - const void *gpio_spec, u32 *flags) +int of_gpio_simple_xlate(struct gpio_chip *gc, struct device_node *np, + const void *gpio_spec, u32 *flags) { const __be32 *gpio = gpio_spec; const u32 n = be32_to_cpup(gpio); @@ -152,6 +152,7 @@ static int of_gpio_simple_xlate(struct gpio_chip *gc, struct device_node *np, return n; } +EXPORT_SYMBOL(of_gpio_simple_xlate); /** * of_mm_gpiochip_add - Add memory mapped GPIO chip (bank) diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index aec8025c786a..52280a2b5e63 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -57,6 +57,8 @@ extern int of_mm_gpiochip_add(struct device_node *np, extern void of_gpiochip_add(struct gpio_chip *gc); extern void of_gpiochip_remove(struct gpio_chip *gc); extern struct gpio_chip *of_node_to_gpiochip(struct device_node *np); +extern int of_gpio_simple_xlate(struct gpio_chip *gc, struct device_node *np, + const void *gpio_spec, u32 *flags); #else /* CONFIG_OF_GPIO */ @@ -72,6 +74,13 @@ static inline unsigned int of_gpio_count(struct device_node *np) return 0; } +static inline int of_gpio_simple_xlate(struct gpio_chip *gc, + struct device_node *np, + const void *gpio_spec, u32 *flags) +{ + return -ENOSYS; +} + static inline void of_gpiochip_add(struct gpio_chip *gc) { } static inline void of_gpiochip_remove(struct gpio_chip *gc) { } -- cgit v1.2.3 From 14b9f27886ce69c5f11445d107dd020f6fc5754b Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Tue, 26 Jul 2011 18:52:35 -0400 Subject: pcmcia: add PCMCIA_DEVICE_MANF_CARD_PROD_ID3 This is needed to match wireless cards with Intersil firmware that have ID 0x0156:0x0002 and the third ID "Version 01.02". Such cards are currently matched by orinoco_cs, which doesn't support WPA. They should be matched by hostap_cs. The first and the second product ID vary widely, so there are few users with some particular IDs. Of those, very few can submit a patch for hostap_cs or write a useful bugreport. It's still important to support their hardware properly. With PCMCIA_DEVICE_MANF_CARD_PROD_ID3, it should be possible to cover the remaining Intersil based designs that kept the numeric ID and the "version" of the reference design. Signed-off-by: Pavel Roskin Signed-off-by: Dominik Brodowski --- include/pcmcia/device_id.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/pcmcia/device_id.h b/include/pcmcia/device_id.h index 63e5b8f6b7dd..00dbfac9c6e1 100644 --- a/include/pcmcia/device_id.h +++ b/include/pcmcia/device_id.h @@ -95,6 +95,15 @@ .prod_id = { (v1), NULL, NULL, NULL }, \ .prod_id_hash = { (vh1), 0, 0, 0 }, } +#define PCMCIA_DEVICE_MANF_CARD_PROD_ID3(manf, card, v3, vh3) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \ + PCMCIA_DEV_ID_MATCH_CARD_ID| \ + PCMCIA_DEV_ID_MATCH_PROD_ID3, \ + .manf_id = (manf), \ + .card_id = (card), \ + .prod_id = { NULL, NULL, (v3), NULL }, \ + .prod_id_hash = { 0, 0, (vh3), 0 }, } + /* multi-function devices */ -- cgit v1.2.3 From 2a11c8ea20bf850b3a2c60db8c2e7497d28aba99 Mon Sep 17 00:00:00 2001 From: Michal Marek Date: Wed, 20 Jul 2011 17:38:57 +0200 Subject: kconfig: Introduce IS_ENABLED(), IS_BUILTIN() and IS_MODULE() Replace the config_is_*() macros with a variant that allows for grepping for usage of CONFIG_* options in the code. Usage: if (IS_ENABLED(CONFIG_NUMA)) or #if IS_ENABLED(CONFIG_NUMA) The IS_ENABLED() macro evaluates to 1 if the argument is set (to either 'y' or 'm'), IS_BUILTIN() tests if the option is 'y' and IS_MODULE() test if the option is 'm'. Only boolean and tristate options are supported. Reviewed-by: Arnaud Lacombe Acked-by: Randy Dunlap Signed-off-by: Michal Marek --- Makefile | 2 +- include/linux/kconfig.h | 32 +++++++++++++++++++++ scripts/kconfig/confdata.c | 71 +++++++++------------------------------------- 3 files changed, 47 insertions(+), 58 deletions(-) create mode 100644 include/linux/kconfig.h (limited to 'include') diff --git a/Makefile b/Makefile index afb8e0d26f2c..bd7629bb93da 100644 --- a/Makefile +++ b/Makefile @@ -360,7 +360,7 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include \ -Iarch/$(hdr-arch)/include/generated -Iinclude \ $(if $(KBUILD_SRC), -I$(srctree)/include) \ - -include include/generated/autoconf.h + -include $(srctree)/include/linux/kconfig.h KBUILD_CPPFLAGS := -D__KERNEL__ diff --git a/include/linux/kconfig.h b/include/linux/kconfig.h new file mode 100644 index 000000000000..067eda0e4b32 --- /dev/null +++ b/include/linux/kconfig.h @@ -0,0 +1,32 @@ +#ifndef __LINUX_KCONFIG_H +#define __LINUX_KCONFIG_H + +#include + +/* + * Helper macros to use CONFIG_ options in C expressions. Note that + * these only work with boolean and tristate options. + */ + +/* + * IS_ENABLED(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y' or 'm', + * 0 otherwise. + * + */ +#define IS_ENABLED(option) \ + (__enabled_ ## option || __enabled_ ## option ## _MODULE) + +/* + * IS_BUILTIN(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y', 0 + * otherwise. For boolean options, this is equivalent to + * IS_ENABLED(CONFIG_FOO). + */ +#define IS_BUILTIN(option) __enabled_ ## option + +/* + * IS_MODULE(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'm', 0 + * otherwise. + */ +#define IS_MODULE(option) __enabled_ ## option ## _MODULE + +#endif /* __LINUX_KCONFIG_H */ diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index df629ecb4fdf..59b667cae5f3 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -495,15 +495,25 @@ header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) switch (*value) { case 'n': - return; + break; case 'm': suffix = "_MODULE"; /* fall through */ default: - value = "1"; + fprintf(fp, "#define %s%s%s 1\n", + CONFIG_, sym->name, suffix); } - fprintf(fp, "#define %s%s%s %s\n", - CONFIG_, sym->name, suffix, value); + /* + * Generate the __enabled_CONFIG_* and + * __enabled_CONFIG_*_MODULE macros for use by the + * IS_{ENABLED,BUILTIN,MODULE} macros. The _MODULE variant is + * generated even for booleans so that the IS_ENABLED() macro + * works. + */ + fprintf(fp, "#define __enabled_" CONFIG_ "%s %d\n", + sym->name, (*value == 'y')); + fprintf(fp, "#define __enabled_" CONFIG_ "%s_MODULE %d\n", + sym->name, (*value == 'm')); break; } case S_HEX: { @@ -554,58 +564,6 @@ static struct conf_printer header_printer_cb = .print_comment = header_print_comment, }; -/* - * Function-style header printer - * - * This printer is used to generate the config_is_xxx() function-style macros - * in `include/generated/autoconf.h' - */ -static void -header_function_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) -{ - int val = 0; - char c; - char *tmp, *d; - - switch (sym->type) { - case S_BOOLEAN: - case S_TRISTATE: - break; - default: - return; - } - if (*value == 'm') - val = 2; - else if (*value == 'y') - val = 1; - - d = strdup(CONFIG_); - tmp = d; - while ((c = *d)) { - *d = tolower(c); - d++; - } - - fprintf(fp, "#define %sis_", tmp); - free(tmp); - - d = strdup(sym->name); - tmp = d; - while ((c = *d)) { - *d = tolower(c); - d++; - } - fprintf(fp, "%s%s() %d\n", tmp, (val > 1) ? "_module" : "", - val ? 1 : 0); - free(tmp); -} - -static struct conf_printer header_function_printer_cb = -{ - .print_symbol = header_function_print_symbol, -}; - - /* * Tristate printer * @@ -997,7 +955,6 @@ int conf_write_autoconf(void) conf_write_symbol(tristate, sym, &tristate_printer_cb, (void *)1); conf_write_symbol(out_h, sym, &header_printer_cb, NULL); - conf_write_symbol(out_h, sym, &header_function_printer_cb, NULL); } fclose(out); fclose(tristate); -- cgit v1.2.3 From 59be8e7280c10fd8f078ba6dc2bcdc2b1453b6ab Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 30 Jul 2011 12:38:46 -0400 Subject: ext4: change umode_t in tracepoint headers to be an explicit __u16 As requested by Al Viro, since umode_t may be changing to a u32 for some architectures. Signed-off-by: "Theodore Ts'o" Cc: Al Viro --- include/trace/events/ext4.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 51d88139eb8c..bf5518d50423 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -23,7 +23,7 @@ TRACE_EVENT(ext4_free_inode, TP_STRUCT__entry( __field( dev_t, dev ) __field( ino_t, ino ) - __field( umode_t, mode ) + __field( __u16, mode ) __field( uid_t, uid ) __field( gid_t, gid ) __field( __u64, blocks ) @@ -52,7 +52,7 @@ TRACE_EVENT(ext4_request_inode, TP_STRUCT__entry( __field( dev_t, dev ) __field( ino_t, dir ) - __field( umode_t, mode ) + __field( __u16, mode ) ), TP_fast_assign( @@ -75,7 +75,7 @@ TRACE_EVENT(ext4_allocate_inode, __field( dev_t, dev ) __field( ino_t, ino ) __field( ino_t, dir ) - __field( umode_t, mode ) + __field( __u16, mode ) ), TP_fast_assign( @@ -727,7 +727,7 @@ TRACE_EVENT(ext4_free_blocks, TP_STRUCT__entry( __field( dev_t, dev ) __field( ino_t, ino ) - __field( umode_t, mode ) + __field( __u16, mode ) __field( __u64, block ) __field( unsigned long, count ) __field( int, flags ) @@ -1014,7 +1014,7 @@ TRACE_EVENT(ext4_forget, TP_STRUCT__entry( __field( dev_t, dev ) __field( ino_t, ino ) - __field( umode_t, mode ) + __field( __u16, mode ) __field( int, is_metadata ) __field( __u64, block ) ), @@ -1041,7 +1041,7 @@ TRACE_EVENT(ext4_da_update_reserve_space, TP_STRUCT__entry( __field( dev_t, dev ) __field( ino_t, ino ) - __field( umode_t, mode ) + __field( __u16, mode ) __field( __u64, i_blocks ) __field( int, used_blocks ) __field( int, reserved_data_blocks ) @@ -1078,7 +1078,7 @@ TRACE_EVENT(ext4_da_reserve_space, TP_STRUCT__entry( __field( dev_t, dev ) __field( ino_t, ino ) - __field( umode_t, mode ) + __field( __u16, mode ) __field( __u64, i_blocks ) __field( int, md_needed ) __field( int, reserved_data_blocks ) @@ -1112,7 +1112,7 @@ TRACE_EVENT(ext4_da_release_space, TP_STRUCT__entry( __field( dev_t, dev ) __field( ino_t, ino ) - __field( umode_t, mode ) + __field( __u16, mode ) __field( __u64, i_blocks ) __field( int, freed_blocks ) __field( int, reserved_data_blocks ) -- cgit v1.2.3 From 0c0308066ca53fdf1423895f3a42838b67b3a5a8 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 30 Jul 2011 12:45:35 -0400 Subject: NFS: Fix spurious readdir cookie loop messages If the directory contents change, then we have to accept that the file->f_pos value may shrink if we do a 'search-by-cookie'. In that case, we should turn off the loop detection and let the NFS client try to recover. The patch also fixes a second loop detection bug by ensuring that after turning on the ctx->duped flag, we read at least one new cookie into ctx->dir_cookie before attempting to match with ctx->dup_cookie. Reported-by: Petr Vandrovec Cc: stable@kernel.org [2.6.39+] Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 56 +++++++++++++++++++++++++++++--------------------- include/linux/nfs_fs.h | 3 ++- 2 files changed, 35 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 57f578e2560a..d23108b1e338 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -134,18 +134,19 @@ const struct inode_operations nfs4_dir_inode_operations = { #endif /* CONFIG_NFS_V4 */ -static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct rpc_cred *cred) +static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred) { struct nfs_open_dir_context *ctx; ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); if (ctx != NULL) { ctx->duped = 0; + ctx->attr_gencount = NFS_I(dir)->attr_gencount; ctx->dir_cookie = 0; ctx->dup_cookie = 0; ctx->cred = get_rpccred(cred); - } else - ctx = ERR_PTR(-ENOMEM); - return ctx; + return ctx; + } + return ERR_PTR(-ENOMEM); } static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx) @@ -173,7 +174,7 @@ nfs_opendir(struct inode *inode, struct file *filp) cred = rpc_lookup_cred(); if (IS_ERR(cred)) return PTR_ERR(cred); - ctx = alloc_nfs_open_dir_context(cred); + ctx = alloc_nfs_open_dir_context(inode, cred); if (IS_ERR(ctx)) { res = PTR_ERR(ctx); goto out; @@ -323,7 +324,6 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri { loff_t diff = desc->file->f_pos - desc->current_index; unsigned int index; - struct nfs_open_dir_context *ctx = desc->file->private_data; if (diff < 0) goto out_eof; @@ -336,7 +336,6 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri index = (unsigned int)diff; *desc->dir_cookie = array->array[index].cookie; desc->cache_entry_index = index; - ctx->duped = 0; return 0; out_eof: desc->eof = 1; @@ -349,14 +348,33 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des int i; loff_t new_pos; int status = -EAGAIN; - struct nfs_open_dir_context *ctx = desc->file->private_data; for (i = 0; i < array->size; i++) { if (array->array[i].cookie == *desc->dir_cookie) { + struct nfs_inode *nfsi = NFS_I(desc->file->f_path.dentry->d_inode); + struct nfs_open_dir_context *ctx = desc->file->private_data; + new_pos = desc->current_index + i; - if (new_pos < desc->file->f_pos) { + if (ctx->attr_gencount != nfsi->attr_gencount + || (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))) { + ctx->duped = 0; + ctx->attr_gencount = nfsi->attr_gencount; + } else if (new_pos < desc->file->f_pos) { + if (ctx->duped > 0 + && ctx->dup_cookie == *desc->dir_cookie) { + if (printk_ratelimit()) { + pr_notice("NFS: directory %s/%s contains a readdir loop." + "Please contact your server vendor. " + "Offending cookie: %llu\n", + desc->file->f_dentry->d_parent->d_name.name, + desc->file->f_dentry->d_name.name, + *desc->dir_cookie); + } + status = -ELOOP; + goto out; + } ctx->dup_cookie = *desc->dir_cookie; - ctx->duped = 1; + ctx->duped = -1; } desc->file->f_pos = new_pos; desc->cache_entry_index = i; @@ -368,6 +386,7 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des if (*desc->dir_cookie == array->last_cookie) desc->eof = 1; } +out: return status; } @@ -740,19 +759,6 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, struct nfs_cache_array *array = NULL; struct nfs_open_dir_context *ctx = file->private_data; - if (ctx->duped != 0 && ctx->dup_cookie == *desc->dir_cookie) { - if (printk_ratelimit()) { - pr_notice("NFS: directory %s/%s contains a readdir loop. " - "Please contact your server vendor. " - "Offending cookie: %llu\n", - file->f_dentry->d_parent->d_name.name, - file->f_dentry->d_name.name, - *desc->dir_cookie); - } - res = -ELOOP; - goto out; - } - array = nfs_readdir_get_array(desc->page); if (IS_ERR(array)) { res = PTR_ERR(array); @@ -774,6 +780,8 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, *desc->dir_cookie = array->array[i+1].cookie; else *desc->dir_cookie = array->last_cookie; + if (ctx->duped != 0) + ctx->duped = 1; } if (array->eof_index >= 0) desc->eof = 1; @@ -805,6 +813,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, struct page *page = NULL; int status; struct inode *inode = desc->file->f_path.dentry->d_inode; + struct nfs_open_dir_context *ctx = desc->file->private_data; dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n", (unsigned long long)*desc->dir_cookie); @@ -818,6 +827,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, desc->page_index = 0; desc->last_cookie = *desc->dir_cookie; desc->page = page; + ctx->duped = 0; status = nfs_readdir_xdr_to_array(desc, page, inode); if (status < 0) diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 8b579beb6358..b96fb99072ff 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -99,9 +99,10 @@ struct nfs_open_context { struct nfs_open_dir_context { struct rpc_cred *cred; + unsigned long attr_gencount; __u64 dir_cookie; __u64 dup_cookie; - int duped; + signed char duped; }; /* -- cgit v1.2.3 From 5774ed014f02120db9a6945a1ecebeb97c2acccb Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 29 Jul 2011 03:55:31 +0400 Subject: shm: handle separate PID namespaces case shm_try_destroy_orphaned() and shm_try_destroy_current() didn't handle the case of separate PID namespaces, but a single IPC namespace. If there are tasks with the same PID values using the same shmem object, the wrong destroy decision could be reached. On shm segment creation store the pointer to the creator task in shmid_kernel->shm_creator field and zero it on task exit. Then use the ->shm_creator insread of shm_cprid in both functions. As shmid_kernel object is already locked at this stage, no additional locking is needed. Signed-off-by: Vasiliy Kulikov Acked-by: Serge Hallyn Signed-off-by: Linus Torvalds --- include/linux/shm.h | 3 +++ ipc/shm.c | 27 ++++++++++++++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/shm.h b/include/linux/shm.h index 7d27ffde0190..92808b86703b 100644 --- a/include/linux/shm.h +++ b/include/linux/shm.h @@ -95,6 +95,9 @@ struct shmid_kernel /* private to the kernel */ pid_t shm_cprid; pid_t shm_lprid; struct user_struct *mlock_user; + + /* The task created the shm object. NULL if the task is dead. */ + struct task_struct *shm_creator; }; /* shm_mode upper byte flags */ diff --git a/ipc/shm.c b/ipc/shm.c index 3f5b14365f33..fdaf8be65b75 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -239,7 +239,23 @@ static int shm_try_destroy_current(int id, void *p, void *data) if (IS_ERR(shp)) return 0; - if (shp->shm_cprid != task_tgid_vnr(current)) { + if (shp->shm_creator != current) { + shm_unlock(shp); + return 0; + } + + /* + * Mark it as orphaned to destroy the segment when + * kernel.shm_rmid_forced is changed. + * It is noop if the following shm_may_destroy() returns true. + */ + shp->shm_creator = NULL; + + /* + * Don't even try to destroy it. If shm_rmid_forced=0 and IPC_RMID + * is not set, it shouldn't be deleted here. + */ + if (!ns->shm_rmid_forced) { shm_unlock(shp); return 0; } @@ -255,7 +271,6 @@ static int shm_try_destroy_orphaned(int id, void *p, void *data) { struct ipc_namespace *ns = data; struct shmid_kernel *shp = shm_lock(ns, id); - struct task_struct *task; if (IS_ERR(shp)) return 0; @@ -263,11 +278,8 @@ static int shm_try_destroy_orphaned(int id, void *p, void *data) /* * We want to destroy segments without users and with already * exit'ed originating process. - * - * XXX: the originating process may exist in another pid namespace. */ - task = find_task_by_vpid(shp->shm_cprid); - if (task != NULL) { + if (shp->shm_creator != NULL) { shm_unlock(shp); return 0; } @@ -295,7 +307,7 @@ void exit_shm(struct task_struct *task) if (!nsp) return; ns = nsp->ipc_ns; - if (!ns || !ns->shm_rmid_forced) + if (!ns) return; /* Destroy all already created segments, but not mapped yet */ @@ -494,6 +506,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) shp->shm_segsz = size; shp->shm_nattch = 0; shp->shm_file = file; + shp->shm_creator = current; /* * shmid gets reported as "inode#" in /proc/pid/maps. * proc-ps tools use this. Changing this will break them. -- cgit v1.2.3 From 1c388919d89ca35741e9c4d3255adf87f76f0c06 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sat, 7 May 2011 20:53:16 +0200 Subject: resources: Add lookup_resource() Add a function to find an existing resource by a resource start address. This allows to implement simple allocators (with a malloc/free-alike API) on top of the resource system. Signed-off-by: Geert Uytterhoeven --- include/linux/ioport.h | 1 + kernel/resource.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) (limited to 'include') diff --git a/include/linux/ioport.h b/include/linux/ioport.h index e9bb22cba764..63eb429ecbe6 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -132,6 +132,7 @@ extern int allocate_resource(struct resource *root, struct resource *new, resource_size_t, resource_size_t), void *alignf_data); +struct resource *lookup_resource(struct resource *root, resource_size_t start); int adjust_resource(struct resource *res, resource_size_t start, resource_size_t size); resource_size_t resource_alignment(struct resource *res); diff --git a/kernel/resource.c b/kernel/resource.c index 3ff40178dce7..3b3cedc52592 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -553,6 +553,27 @@ int allocate_resource(struct resource *root, struct resource *new, EXPORT_SYMBOL(allocate_resource); +/** + * lookup_resource - find an existing resource by a resource start address + * @root: root resource descriptor + * @start: resource start address + * + * Returns a pointer to the resource if found, NULL otherwise + */ +struct resource *lookup_resource(struct resource *root, resource_size_t start) +{ + struct resource *res; + + read_lock(&resource_lock); + for (res = root->child; res; res = res->sibling) { + if (res->start == start) + break; + } + read_unlock(&resource_lock); + + return res; +} + /* * Insert a resource into the resource tree. If successful, return NULL, * otherwise return the conflicting resource (compare to __request_resource()) -- cgit v1.2.3 From a9bae5666d0510ad69bdb437371c9a3e6b770705 Mon Sep 17 00:00:00 2001 From: Peng Tao Date: Sat, 30 Jul 2011 20:52:33 -0400 Subject: pnfs: let layoutcommit handle a list of lseg There can be multiple lseg per file, so layoutcommit should be able to handle it. [Needed in v3.0] CC: Stable Tree Signed-off-by: Peng Tao Signed-off-by: Boaz Harrosh Signed-off-by: Jim Rees Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 8 +++++++- fs/nfs/pnfs.c | 32 ++++++++++++++++---------------- fs/nfs/pnfs.h | 2 ++ include/linux/nfs_xdr.h | 2 +- 4 files changed, 26 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 079614deca3f..efa6ae822ca8 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5912,9 +5912,15 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata) static void nfs4_layoutcommit_release(void *calldata) { struct nfs4_layoutcommit_data *data = calldata; + struct pnfs_layout_segment *lseg, *tmp; /* Matched by references in pnfs_set_layoutcommit */ - put_lseg(data->lseg); + list_for_each_entry_safe(lseg, tmp, &data->lseg_list, pls_lc_list) { + list_del_init(&lseg->pls_lc_list); + if (test_and_clear_bit(NFS_LSEG_LAYOUTCOMMIT, + &lseg->pls_flags)) + put_lseg(lseg); + } put_rpccred(data->cred); kfree(data); } diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index ab2cb04f8a28..a7e5f17f7776 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -225,6 +225,7 @@ static void init_lseg(struct pnfs_layout_hdr *lo, struct pnfs_layout_segment *lseg) { INIT_LIST_HEAD(&lseg->pls_list); + INIT_LIST_HEAD(&lseg->pls_lc_list); atomic_set(&lseg->pls_refcount, 1); smp_mb(); set_bit(NFS_LSEG_VALID, &lseg->pls_flags); @@ -1356,16 +1357,17 @@ pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc) EXPORT_SYMBOL_GPL(pnfs_generic_pg_readpages); /* - * Currently there is only one (whole file) write lseg. + * There can be multiple RW segments. */ -static struct pnfs_layout_segment *pnfs_list_write_lseg(struct inode *inode) +static void pnfs_list_write_lseg(struct inode *inode, struct list_head *listp) { - struct pnfs_layout_segment *lseg, *rv = NULL; + struct pnfs_layout_segment *lseg; - list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) - if (lseg->pls_range.iomode == IOMODE_RW) - rv = lseg; - return rv; + list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) { + if (lseg->pls_range.iomode == IOMODE_RW && + test_bit(NFS_LSEG_LAYOUTCOMMIT, &lseg->pls_flags)) + list_add(&lseg->pls_lc_list, listp); + } } void @@ -1377,12 +1379,14 @@ pnfs_set_layoutcommit(struct nfs_write_data *wdata) spin_lock(&nfsi->vfs_inode.i_lock); if (!test_and_set_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) { - /* references matched in nfs4_layoutcommit_release */ - get_lseg(wdata->lseg); mark_as_dirty = true; dprintk("%s: Set layoutcommit for inode %lu ", __func__, wdata->inode->i_ino); } + if (!test_and_set_bit(NFS_LSEG_LAYOUTCOMMIT, &wdata->lseg->pls_flags)) { + /* references matched in nfs4_layoutcommit_release */ + get_lseg(wdata->lseg); + } if (end_pos > nfsi->layout->plh_lwb) nfsi->layout->plh_lwb = end_pos; spin_unlock(&nfsi->vfs_inode.i_lock); @@ -1409,7 +1413,6 @@ pnfs_layoutcommit_inode(struct inode *inode, bool sync) { struct nfs4_layoutcommit_data *data; struct nfs_inode *nfsi = NFS_I(inode); - struct pnfs_layout_segment *lseg; loff_t end_pos; int status = 0; @@ -1426,17 +1429,15 @@ pnfs_layoutcommit_inode(struct inode *inode, bool sync) goto out; } + INIT_LIST_HEAD(&data->lseg_list); spin_lock(&inode->i_lock); if (!test_and_clear_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) { spin_unlock(&inode->i_lock); kfree(data); goto out; } - /* - * Currently only one (whole file) write lseg which is referenced - * in pnfs_set_layoutcommit and will be found. - */ - lseg = pnfs_list_write_lseg(inode); + + pnfs_list_write_lseg(inode, &data->lseg_list); end_pos = nfsi->layout->plh_lwb; nfsi->layout->plh_lwb = 0; @@ -1446,7 +1447,6 @@ pnfs_layoutcommit_inode(struct inode *inode, bool sync) spin_unlock(&inode->i_lock); data->args.inode = inode; - data->lseg = lseg; data->cred = get_rpccred(nfsi->layout->plh_lc_cred); nfs_fattr_init(&data->fattr); data->args.bitmask = NFS_SERVER(inode)->cache_consistency_bitmask; diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 1f5cb47e2a2c..b94f874886ca 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -36,10 +36,12 @@ enum { NFS_LSEG_VALID = 0, /* cleared when lseg is recalled/returned */ NFS_LSEG_ROC, /* roc bit received from server */ + NFS_LSEG_LAYOUTCOMMIT, /* layoutcommit bit set for layoutcommit */ }; struct pnfs_layout_segment { struct list_head pls_list; + struct list_head pls_lc_list; struct pnfs_layout_range pls_range; atomic_t pls_refcount; unsigned long pls_flags; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 5b115956abac..feb312716c07 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -262,7 +262,7 @@ struct nfs4_layoutcommit_res { struct nfs4_layoutcommit_data { struct rpc_task task; struct nfs_fattr fattr; - struct pnfs_layout_segment *lseg; + struct list_head lseg_list; struct rpc_cred *cred; struct nfs4_layoutcommit_args args; struct nfs4_layoutcommit_res res; -- cgit v1.2.3 From 7f11d8d38d64739e190581e015a2a2730ff54e2a Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Sat, 30 Jul 2011 20:52:35 -0400 Subject: pnfs: GETDEVICELIST The block driver uses GETDEVICELIST Signed-off-by: Andy Adamson [pass struct nfs_server * to getdevicelist] [get machince creds for getdevicelist] [fix getdevicelist decode sizing] Signed-off-by: Benny Halevy Signed-off-by: Benny Halevy Signed-off-by: Jim Rees Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 48 ++++++++++++++++++ fs/nfs/nfs4xdr.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/pnfs.h | 12 +++++ include/linux/nfs4.h | 1 + include/linux/nfs_xdr.h | 11 ++++ 5 files changed, 203 insertions(+) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index efa6ae822ca8..af32d3df0544 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5834,6 +5834,54 @@ int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp) return status; } +/* + * Retrieve the list of Data Server devices from the MDS. + */ +static int _nfs4_getdevicelist(struct nfs_server *server, + const struct nfs_fh *fh, + struct pnfs_devicelist *devlist) +{ + struct nfs4_getdevicelist_args args = { + .fh = fh, + .layoutclass = server->pnfs_curr_ld->id, + }; + struct nfs4_getdevicelist_res res = { + .devlist = devlist, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICELIST], + .rpc_argp = &args, + .rpc_resp = &res, + }; + int status; + + dprintk("--> %s\n", __func__); + status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, + &res.seq_res, 0); + dprintk("<-- %s status=%d\n", __func__, status); + return status; +} + +int nfs4_proc_getdevicelist(struct nfs_server *server, + const struct nfs_fh *fh, + struct pnfs_devicelist *devlist) +{ + struct nfs4_exception exception = { }; + int err; + + do { + err = nfs4_handle_exception(server, + _nfs4_getdevicelist(server, fh, devlist), + &exception); + } while (exception.retry); + + dprintk("%s: err=%d, num_devs=%u\n", __func__, + err, devlist->num_devs); + + return err; +} +EXPORT_SYMBOL_GPL(nfs4_proc_getdevicelist); + static int _nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev) { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index b851b560a6f8..5f769f8d05b0 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -314,6 +314,17 @@ static int nfs4_stat_to_errno(int); XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 5) #define encode_reclaim_complete_maxsz (op_encode_hdr_maxsz + 4) #define decode_reclaim_complete_maxsz (op_decode_hdr_maxsz + 4) +#define encode_getdevicelist_maxsz (op_encode_hdr_maxsz + 4 + \ + encode_verifier_maxsz) +#define decode_getdevicelist_maxsz (op_decode_hdr_maxsz + \ + 2 /* nfs_cookie4 gdlr_cookie */ + \ + decode_verifier_maxsz \ + /* verifier4 gdlr_verifier */ + \ + 1 /* gdlr_deviceid_list count */ + \ + XDR_QUADLEN(NFS4_PNFS_GETDEVLIST_MAXNUM * \ + NFS4_DEVICEID4_SIZE) \ + /* gdlr_deviceid_list */ + \ + 1 /* bool gdlr_eof */) #define encode_getdeviceinfo_maxsz (op_encode_hdr_maxsz + 4 + \ XDR_QUADLEN(NFS4_DEVICEID4_SIZE)) #define decode_getdeviceinfo_maxsz (op_decode_hdr_maxsz + \ @@ -748,6 +759,14 @@ static int nfs4_stat_to_errno(int); #define NFS4_dec_reclaim_complete_sz (compound_decode_hdr_maxsz + \ decode_sequence_maxsz + \ decode_reclaim_complete_maxsz) +#define NFS4_enc_getdevicelist_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ + encode_putfh_maxsz + \ + encode_getdevicelist_maxsz) +#define NFS4_dec_getdevicelist_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ + decode_putfh_maxsz + \ + decode_getdevicelist_maxsz) #define NFS4_enc_getdeviceinfo_sz (compound_encode_hdr_maxsz + \ encode_sequence_maxsz +\ encode_getdeviceinfo_maxsz) @@ -1854,6 +1873,26 @@ static void encode_sequence(struct xdr_stream *xdr, } #ifdef CONFIG_NFS_V4_1 +static void +encode_getdevicelist(struct xdr_stream *xdr, + const struct nfs4_getdevicelist_args *args, + struct compound_hdr *hdr) +{ + __be32 *p; + nfs4_verifier dummy = { + .data = "dummmmmy", + }; + + p = reserve_space(xdr, 20); + *p++ = cpu_to_be32(OP_GETDEVICELIST); + *p++ = cpu_to_be32(args->layoutclass); + *p++ = cpu_to_be32(NFS4_PNFS_GETDEVLIST_MAXNUM); + xdr_encode_hyper(p, 0ULL); /* cookie */ + encode_nfs4_verifier(xdr, &dummy); + hdr->nops++; + hdr->replen += decode_getdevicelist_maxsz; +} + static void encode_getdeviceinfo(struct xdr_stream *xdr, const struct nfs4_getdeviceinfo_args *args, @@ -2774,6 +2813,24 @@ static void nfs4_xdr_enc_reclaim_complete(struct rpc_rqst *req, encode_nops(&hdr); } +/* + * Encode GETDEVICELIST request + */ +static void nfs4_xdr_enc_getdevicelist(struct rpc_rqst *req, + struct xdr_stream *xdr, + struct nfs4_getdevicelist_args *args) +{ + struct compound_hdr hdr = { + .minorversion = nfs4_xdr_minorversion(&args->seq_args), + }; + + encode_compound_hdr(xdr, req, &hdr); + encode_sequence(xdr, &args->seq_args, &hdr); + encode_putfh(xdr, args->fh, &hdr); + encode_getdevicelist(xdr, args, &hdr); + encode_nops(&hdr); +} + /* * Encode GETDEVICEINFO request */ @@ -5268,6 +5325,53 @@ out_overflow: } #if defined(CONFIG_NFS_V4_1) +/* + * TODO: Need to handle case when EOF != true; + */ +static int decode_getdevicelist(struct xdr_stream *xdr, + struct pnfs_devicelist *res) +{ + __be32 *p; + int status, i; + struct nfs_writeverf verftemp; + + status = decode_op_hdr(xdr, OP_GETDEVICELIST); + if (status) + return status; + + p = xdr_inline_decode(xdr, 8 + 8 + 4); + if (unlikely(!p)) + goto out_overflow; + + /* TODO: Skip cookie for now */ + p += 2; + + /* Read verifier */ + p = xdr_decode_opaque_fixed(p, verftemp.verifier, 8); + + res->num_devs = be32_to_cpup(p); + + dprintk("%s: num_dev %d\n", __func__, res->num_devs); + + if (res->num_devs > NFS4_PNFS_GETDEVLIST_MAXNUM) { + printk(KERN_ERR "%s too many result dev_num %u\n", + __func__, res->num_devs); + return -EIO; + } + + p = xdr_inline_decode(xdr, + res->num_devs * NFS4_DEVICEID4_SIZE + 4); + if (unlikely(!p)) + goto out_overflow; + for (i = 0; i < res->num_devs; i++) + p = xdr_decode_opaque_fixed(p, res->dev_id[i].data, + NFS4_DEVICEID4_SIZE); + res->eof = be32_to_cpup(p); + return 0; +out_overflow: + print_overflow_msg(__func__, xdr); + return -EIO; +} static int decode_getdeviceinfo(struct xdr_stream *xdr, struct pnfs_device *pdev) @@ -6541,6 +6645,32 @@ static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp, return status; } +/* + * Decode GETDEVICELIST response + */ +static int nfs4_xdr_dec_getdevicelist(struct rpc_rqst *rqstp, + struct xdr_stream *xdr, + struct nfs4_getdevicelist_res *res) +{ + struct compound_hdr hdr; + int status; + + dprintk("encoding getdevicelist!\n"); + + status = decode_compound_hdr(xdr, &hdr); + if (status != 0) + goto out; + status = decode_sequence(xdr, &res->seq_res, rqstp); + if (status != 0) + goto out; + status = decode_putfh(xdr); + if (status != 0) + goto out; + status = decode_getdevicelist(xdr, res->devlist); +out: + return status; +} + /* * Decode GETDEVINFO response */ @@ -6908,6 +7038,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(SECINFO_NO_NAME, enc_secinfo_no_name, dec_secinfo_no_name), PROC(TEST_STATEID, enc_test_stateid, dec_test_stateid), PROC(FREE_STATEID, enc_free_stateid, dec_free_stateid), + PROC(GETDEVICELIST, enc_getdevicelist, dec_getdevicelist), #endif /* CONFIG_NFS_V4_1 */ }; diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index b94f874886ca..7074394944a9 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -135,14 +135,26 @@ struct pnfs_device { unsigned int layout_type; unsigned int mincount; struct page **pages; + void *area; unsigned int pgbase; unsigned int pglen; }; +#define NFS4_PNFS_GETDEVLIST_MAXNUM 16 + +struct pnfs_devicelist { + unsigned int eof; + unsigned int num_devs; + struct nfs4_deviceid dev_id[NFS4_PNFS_GETDEVLIST_MAXNUM]; +}; + extern int pnfs_register_layoutdriver(struct pnfs_layoutdriver_type *); extern void pnfs_unregister_layoutdriver(struct pnfs_layoutdriver_type *); /* nfs4proc.c */ +extern int nfs4_proc_getdevicelist(struct nfs_server *server, + const struct nfs_fh *fh, + struct pnfs_devicelist *devlist); extern int nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *dev); extern int nfs4_proc_layoutget(struct nfs4_layoutget *lgp); diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index a3c4bc800dce..76f99e8714f3 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -566,6 +566,7 @@ enum { NFSPROC4_CLNT_SECINFO_NO_NAME, NFSPROC4_CLNT_TEST_STATEID, NFSPROC4_CLNT_FREE_STATEID, + NFSPROC4_CLNT_GETDEVICELIST, }; /* nfs41 types */ diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index feb312716c07..21f333eae3c8 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -235,6 +235,17 @@ struct nfs4_layoutget { gfp_t gfp_flags; }; +struct nfs4_getdevicelist_args { + const struct nfs_fh *fh; + u32 layoutclass; + struct nfs4_sequence_args seq_args; +}; + +struct nfs4_getdevicelist_res { + struct pnfs_devicelist *devlist; + struct nfs4_sequence_res seq_res; +}; + struct nfs4_getdeviceinfo_args { struct pnfs_device *pdev; struct nfs4_sequence_args seq_args; -- cgit v1.2.3 From dae100c2b1b9463996aab9162f2258145c43f7df Mon Sep 17 00:00:00 2001 From: Fred Isaman Date: Sat, 30 Jul 2011 20:52:37 -0400 Subject: pnfs: ask for layout_blksize and save it in nfs_server Block layout needs it to determine IO size. Signed-off-by: Fred Isaman Signed-off-by: Tao Guo Signed-off-by: Benny Halevy Signed-off-by: Benny Halevy Signed-off-by: Jim Rees Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 1 + fs/nfs/nfs4_fs.h | 2 +- fs/nfs/nfs4proc.c | 5 ++- fs/nfs/nfs4xdr.c | 99 +++++++++++++++++++++++++++++++++++++++-------- include/linux/nfs_fs_sb.h | 3 +- include/linux/nfs_xdr.h | 3 +- 6 files changed, 91 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index a9b18483cb24..de00a373f085 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -936,6 +936,7 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, if (server->wsize > NFS_MAX_FILE_IO_SIZE) server->wsize = NFS_MAX_FILE_IO_SIZE; server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + server->pnfs_blksize = fsinfo->blksize; set_pnfs_layoutdriver(server, mntfh, fsinfo->layouttype); server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 1909ee8be350..1ec1a85fa71c 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -318,7 +318,7 @@ extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[]; extern const u32 nfs4_fattr_bitmap[2]; extern const u32 nfs4_statfs_bitmap[2]; extern const u32 nfs4_pathconf_bitmap[2]; -extern const u32 nfs4_fsinfo_bitmap[2]; +extern const u32 nfs4_fsinfo_bitmap[3]; extern const u32 nfs4_fs_locations_bitmap[2]; /* nfs4renewd.c */ diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index af32d3df0544..e86de799dd12 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -140,12 +140,13 @@ const u32 nfs4_pathconf_bitmap[2] = { 0 }; -const u32 nfs4_fsinfo_bitmap[2] = { FATTR4_WORD0_MAXFILESIZE +const u32 nfs4_fsinfo_bitmap[3] = { FATTR4_WORD0_MAXFILESIZE | FATTR4_WORD0_MAXREAD | FATTR4_WORD0_MAXWRITE | FATTR4_WORD0_LEASE_TIME, FATTR4_WORD1_TIME_DELTA - | FATTR4_WORD1_FS_LAYOUT_TYPES + | FATTR4_WORD1_FS_LAYOUT_TYPES, + FATTR4_WORD2_LAYOUT_BLKSIZE }; const u32 nfs4_fs_locations_bitmap[2] = { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 5f769f8d05b0..026166993d11 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -113,7 +113,11 @@ static int nfs4_stat_to_errno(int); #define encode_restorefh_maxsz (op_encode_hdr_maxsz) #define decode_restorefh_maxsz (op_decode_hdr_maxsz) #define encode_fsinfo_maxsz (encode_getattr_maxsz) -#define decode_fsinfo_maxsz (op_decode_hdr_maxsz + 15) +/* The 5 accounts for the PNFS attributes, and assumes that at most three + * layout types will be returned. + */ +#define decode_fsinfo_maxsz (op_decode_hdr_maxsz + \ + nfs4_fattr_bitmap_maxsz + 4 + 8 + 5) #define encode_renew_maxsz (op_encode_hdr_maxsz + 3) #define decode_renew_maxsz (op_decode_hdr_maxsz) #define encode_setclientid_maxsz \ @@ -1123,6 +1127,35 @@ static void encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm hdr->replen += decode_getattr_maxsz; } +static void +encode_getattr_three(struct xdr_stream *xdr, + uint32_t bm0, uint32_t bm1, uint32_t bm2, + struct compound_hdr *hdr) +{ + __be32 *p; + + p = reserve_space(xdr, 4); + *p = cpu_to_be32(OP_GETATTR); + if (bm2) { + p = reserve_space(xdr, 16); + *p++ = cpu_to_be32(3); + *p++ = cpu_to_be32(bm0); + *p++ = cpu_to_be32(bm1); + *p = cpu_to_be32(bm2); + } else if (bm1) { + p = reserve_space(xdr, 12); + *p++ = cpu_to_be32(2); + *p++ = cpu_to_be32(bm0); + *p = cpu_to_be32(bm1); + } else { + p = reserve_space(xdr, 8); + *p++ = cpu_to_be32(1); + *p = cpu_to_be32(bm0); + } + hdr->nops++; + hdr->replen += decode_getattr_maxsz; +} + static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr) { encode_getattr_two(xdr, bitmask[0] & nfs4_fattr_bitmap[0], @@ -1131,8 +1164,11 @@ static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct c static void encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr) { - encode_getattr_two(xdr, bitmask[0] & nfs4_fsinfo_bitmap[0], - bitmask[1] & nfs4_fsinfo_bitmap[1], hdr); + encode_getattr_three(xdr, + bitmask[0] & nfs4_fsinfo_bitmap[0], + bitmask[1] & nfs4_fsinfo_bitmap[1], + bitmask[2] & nfs4_fsinfo_bitmap[2], + hdr); } static void encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr) @@ -2643,7 +2679,7 @@ static void nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, struct compound_hdr hdr = { .nops = 0, }; - const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 }; + const u32 lease_bitmap[3] = { FATTR4_WORD0_LEASE_TIME }; encode_compound_hdr(xdr, req, &hdr); encode_setclientid_confirm(xdr, arg, &hdr); @@ -2787,7 +2823,7 @@ static void nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req, struct compound_hdr hdr = { .minorversion = nfs4_xdr_minorversion(&args->la_seq_args), }; - const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 }; + const u32 lease_bitmap[3] = { FATTR4_WORD0_LEASE_TIME }; encode_compound_hdr(xdr, req, &hdr); encode_sequence(xdr, &args->la_seq_args, &hdr); @@ -3068,14 +3104,17 @@ static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap) goto out_overflow; bmlen = be32_to_cpup(p); - bitmap[0] = bitmap[1] = 0; + bitmap[0] = bitmap[1] = bitmap[2] = 0; p = xdr_inline_decode(xdr, (bmlen << 2)); if (unlikely(!p)) goto out_overflow; if (bmlen > 0) { bitmap[0] = be32_to_cpup(p++); - if (bmlen > 1) - bitmap[1] = be32_to_cpup(p); + if (bmlen > 1) { + bitmap[1] = be32_to_cpup(p++); + if (bmlen > 2) + bitmap[2] = be32_to_cpup(p); + } } return 0; out_overflow: @@ -3107,8 +3146,9 @@ static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint3 return ret; bitmap[0] &= ~FATTR4_WORD0_SUPPORTED_ATTRS; } else - bitmask[0] = bitmask[1] = 0; - dprintk("%s: bitmask=%08x:%08x\n", __func__, bitmask[0], bitmask[1]); + bitmask[0] = bitmask[1] = bitmask[2] = 0; + dprintk("%s: bitmask=%08x:%08x:%08x\n", __func__, + bitmask[0], bitmask[1], bitmask[2]); return 0; } @@ -4162,7 +4202,7 @@ out_overflow: static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res) { __be32 *savep; - uint32_t attrlen, bitmap[2] = {0}; + uint32_t attrlen, bitmap[3] = {0}; int status; if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) @@ -4188,7 +4228,7 @@ xdr_error: static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat) { __be32 *savep; - uint32_t attrlen, bitmap[2] = {0}; + uint32_t attrlen, bitmap[3] = {0}; int status; if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) @@ -4220,7 +4260,7 @@ xdr_error: static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf) { __be32 *savep; - uint32_t attrlen, bitmap[2] = {0}; + uint32_t attrlen, bitmap[3] = {0}; int status; if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) @@ -4360,7 +4400,7 @@ static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fat { __be32 *savep; uint32_t attrlen, - bitmap[2] = {0}; + bitmap[3] = {0}; int status; status = decode_op_hdr(xdr, OP_GETATTR); @@ -4446,10 +4486,32 @@ static int decode_attr_pnfstype(struct xdr_stream *xdr, uint32_t *bitmap, return status; } +/* + * The prefered block size for layout directed io + */ +static int decode_attr_layout_blksize(struct xdr_stream *xdr, uint32_t *bitmap, + uint32_t *res) +{ + __be32 *p; + + dprintk("%s: bitmap is %x\n", __func__, bitmap[2]); + *res = 0; + if (bitmap[2] & FATTR4_WORD2_LAYOUT_BLKSIZE) { + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) { + print_overflow_msg(__func__, xdr); + return -EIO; + } + *res = be32_to_cpup(p); + bitmap[2] &= ~FATTR4_WORD2_LAYOUT_BLKSIZE; + } + return 0; +} + static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) { __be32 *savep; - uint32_t attrlen, bitmap[2]; + uint32_t attrlen, bitmap[3]; int status; if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) @@ -4477,6 +4539,9 @@ static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) status = decode_attr_pnfstype(xdr, bitmap, &fsinfo->layouttype); if (status != 0) goto xdr_error; + status = decode_attr_layout_blksize(xdr, bitmap, &fsinfo->blksize); + if (status) + goto xdr_error; status = verify_attr_len(xdr, savep, attrlen); xdr_error: @@ -4896,7 +4961,7 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, { __be32 *savep; uint32_t attrlen, - bitmap[2] = {0}; + bitmap[3] = {0}; struct kvec *iov = req->rq_rcv_buf.head; int status; @@ -6852,7 +6917,7 @@ out: int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus) { - uint32_t bitmap[2] = {0}; + uint32_t bitmap[3] = {0}; uint32_t len; __be32 *p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 4faeac8f448a..b2ea8b82d2cb 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -132,7 +132,7 @@ struct nfs_server { #endif #ifdef CONFIG_NFS_V4 - u32 attr_bitmask[2];/* V4 bitmask representing the set + u32 attr_bitmask[3];/* V4 bitmask representing the set of attributes supported on this filesystem */ u32 cache_consistency_bitmask[2]; @@ -145,6 +145,7 @@ struct nfs_server { filesystem */ struct pnfs_layoutdriver_type *pnfs_curr_ld; /* Active layout driver */ struct rpc_wait_queue roc_rpcwaitq; + u32 pnfs_blksize; /* layout_blksize attr */ /* the following fields are protected by nfs_client->cl_lock */ struct rb_root state_owners; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 21f333eae3c8..94f27e56df9c 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -122,6 +122,7 @@ struct nfs_fsinfo { struct timespec time_delta; /* server time granularity */ __u32 lease_time; /* in seconds */ __u32 layouttype; /* supported pnfs layout driver */ + __u32 blksize; /* preferred pnfs io block size */ }; struct nfs_fsstat { @@ -954,7 +955,7 @@ struct nfs4_server_caps_arg { }; struct nfs4_server_caps_res { - u32 attr_bitmask[2]; + u32 attr_bitmask[3]; u32 acl_bitmask; u32 has_links; u32 has_symlinks; -- cgit v1.2.3 From db29c089094b2e686ebc9ed9f002be4a4f94b1f8 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Sat, 30 Jul 2011 20:52:38 -0400 Subject: pnfs: cleanup_layoutcommit This gives layout driver a chance to cleanup structures they put in at encode_layoutcommit. Signed-off-by: Andy Adamson [fixup layout header pointer for layoutcommit] Signed-off-by: Benny Halevy Signed-off-by: Benny Halevy [rm inode and pnfs_layout_hdr args from cleanup_layoutcommit()] Signed-off-by: Jim Rees Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 1 + fs/nfs/nfs4xdr.c | 1 + fs/nfs/pnfs.c | 8 ++++++++ fs/nfs/pnfs.h | 3 +++ include/linux/nfs_xdr.h | 1 + 5 files changed, 14 insertions(+) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e86de799dd12..8c77039e7a81 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5963,6 +5963,7 @@ static void nfs4_layoutcommit_release(void *calldata) struct nfs4_layoutcommit_data *data = calldata; struct pnfs_layout_segment *lseg, *tmp; + pnfs_cleanup_layoutcommit(data); /* Matched by references in pnfs_set_layoutcommit */ list_for_each_entry_safe(lseg, tmp, &data->lseg_list, pls_lc_list) { list_del_init(&lseg->pls_lc_list); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 026166993d11..1dce12f41a4f 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -5599,6 +5599,7 @@ static int decode_layoutcommit(struct xdr_stream *xdr, int status; status = decode_op_hdr(xdr, OP_LAYOUTCOMMIT); + res->status = status; if (status) return status; diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 3a47f7ce1e90..e550e8836c37 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1411,6 +1411,14 @@ pnfs_set_layoutcommit(struct nfs_write_data *wdata) } EXPORT_SYMBOL_GPL(pnfs_set_layoutcommit); +void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data) +{ + struct nfs_server *nfss = NFS_SERVER(data->args.inode); + + if (nfss->pnfs_curr_ld->cleanup_layoutcommit) + nfss->pnfs_curr_ld->cleanup_layoutcommit(data); +} + /* * For the LAYOUT4_NFSV4_1_FILES layout type, NFS_DATA_SYNC WRITEs and * NFS_UNSTABLE WRITEs with a COMMIT to data servers must store enough diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index bddd8b997e18..606fbde2e757 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -113,6 +113,8 @@ struct pnfs_layoutdriver_type { struct xdr_stream *xdr, const struct nfs4_layoutreturn_args *args); + void (*cleanup_layoutcommit) (struct nfs4_layoutcommit_data *data); + void (*encode_layoutcommit) (struct pnfs_layout_hdr *layoutid, struct xdr_stream *xdr, const struct nfs4_layoutcommit_args *args); @@ -196,6 +198,7 @@ void pnfs_roc_release(struct inode *ino); void pnfs_roc_set_barrier(struct inode *ino, u32 barrier); bool pnfs_roc_drain(struct inode *ino, u32 *barrier); void pnfs_set_layoutcommit(struct nfs_write_data *wdata); +void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data); int pnfs_layoutcommit_inode(struct inode *inode, bool sync); int _pnfs_return_layout(struct inode *); int pnfs_ld_write_done(struct nfs_write_data *); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 94f27e56df9c..569ea5b76fda 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -269,6 +269,7 @@ struct nfs4_layoutcommit_res { struct nfs_fattr *fattr; const struct nfs_server *server; struct nfs4_sequence_res seq_res; + int status; }; struct nfs4_layoutcommit_data { -- cgit v1.2.3 From fe0a9b740881d181e3c96c1f6f6043e252692ffe Mon Sep 17 00:00:00 2001 From: Jim Rees Date: Sat, 30 Jul 2011 20:52:42 -0400 Subject: pnfsblock: add device operations Signed-off-by: Jim Rees Signed-off-by: Fred Isaman Signed-off-by: Benny Halevy Signed-off-by: Benny Halevy [upcall bugfixes] Signed-off-by: Peng Tao Signed-off-by: Trond Myklebust --- fs/nfs/blocklayout/Makefile | 2 +- fs/nfs/blocklayout/blocklayout.c | 42 ++++++++ fs/nfs/blocklayout/blocklayout.h | 40 ++++++++ fs/nfs/blocklayout/blocklayoutdev.c | 191 ++++++++++++++++++++++++++++++++++++ fs/nfs/client.c | 2 +- include/linux/nfs.h | 2 + 6 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 fs/nfs/blocklayout/blocklayoutdev.c (limited to 'include') diff --git a/fs/nfs/blocklayout/Makefile b/fs/nfs/blocklayout/Makefile index 5cfadf6ebc90..5bf3409084d2 100644 --- a/fs/nfs/blocklayout/Makefile +++ b/fs/nfs/blocklayout/Makefile @@ -2,4 +2,4 @@ # Makefile for the pNFS block layout driver kernel module # obj-$(CONFIG_PNFS_BLOCK) += blocklayoutdriver.o -blocklayoutdriver-objs := blocklayout.o extents.o +blocklayoutdriver-objs := blocklayout.o extents.o blocklayoutdev.o diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 8dde3723482e..c83878441047 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -31,6 +31,8 @@ */ #include #include +#include +#include #include "blocklayout.h" @@ -40,6 +42,9 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Andy Adamson "); MODULE_DESCRIPTION("The NFSv4.1 pNFS Block layout driver"); +struct dentry *bl_device_pipe; +wait_queue_head_t bl_wq; + static enum pnfs_try_status bl_read_pagelist(struct nfs_read_data *rdata) { @@ -176,13 +181,49 @@ static struct pnfs_layoutdriver_type blocklayout_type = { .pg_write_ops = &bl_pg_write_ops, }; +static const struct rpc_pipe_ops bl_upcall_ops = { + .upcall = bl_pipe_upcall, + .downcall = bl_pipe_downcall, + .destroy_msg = bl_pipe_destroy_msg, +}; + static int __init nfs4blocklayout_init(void) { + struct vfsmount *mnt; + struct path path; int ret; dprintk("%s: NFSv4 Block Layout Driver Registering...\n", __func__); ret = pnfs_register_layoutdriver(&blocklayout_type); + if (ret) + goto out; + + init_waitqueue_head(&bl_wq); + + mnt = rpc_get_mount(); + if (IS_ERR(mnt)) { + ret = PTR_ERR(mnt); + goto out_remove; + } + + ret = vfs_path_lookup(mnt->mnt_root, + mnt, + NFS_PIPE_DIRNAME, 0, &path); + if (ret) + goto out_remove; + + bl_device_pipe = rpc_mkpipe(path.dentry, "blocklayout", NULL, + &bl_upcall_ops, 0); + if (IS_ERR(bl_device_pipe)) { + ret = PTR_ERR(bl_device_pipe); + goto out_remove; + } +out: + return ret; + +out_remove: + pnfs_unregister_layoutdriver(&blocklayout_type); return ret; } @@ -192,6 +233,7 @@ static void __exit nfs4blocklayout_exit(void) __func__); pnfs_unregister_layoutdriver(&blocklayout_type); + rpc_unlink(bl_device_pipe); } MODULE_ALIAS("nfs-layouttype4-3"); diff --git a/fs/nfs/blocklayout/blocklayout.h b/fs/nfs/blocklayout/blocklayout.h index 98e2f60c2143..dd25f1b3fe1e 100644 --- a/fs/nfs/blocklayout/blocklayout.h +++ b/fs/nfs/blocklayout/blocklayout.h @@ -34,8 +34,16 @@ #include #include +#include + #include "../pnfs.h" +struct pnfs_block_dev { + struct list_head bm_node; + struct nfs4_deviceid bm_mdevid; /* associated devid */ + struct block_device *bm_mdev; /* meta device itself */ +}; + enum exstate4 { PNFS_BLOCK_READWRITE_DATA = 0, PNFS_BLOCK_READ_DATA = 1, @@ -88,5 +96,37 @@ static inline struct pnfs_block_layout *BLK_LO2EXT(struct pnfs_layout_hdr *lo) return container_of(lo, struct pnfs_block_layout, bl_layout); } +struct bl_dev_msg { + int status; + uint32_t major, minor; +}; + +struct bl_msg_hdr { + u8 type; + u16 totallen; /* length of entire message, including hdr itself */ +}; + +extern struct dentry *bl_device_pipe; +extern wait_queue_head_t bl_wq; + +#define BL_DEVICE_UMOUNT 0x0 /* Umount--delete devices */ +#define BL_DEVICE_MOUNT 0x1 /* Mount--create devices*/ +#define BL_DEVICE_REQUEST_INIT 0x0 /* Start request */ +#define BL_DEVICE_REQUEST_PROC 0x1 /* User level process succeeds */ +#define BL_DEVICE_REQUEST_ERR 0x2 /* User level process fails */ + +/* blocklayoutdev.c */ +ssize_t bl_pipe_upcall(struct file *, struct rpc_pipe_msg *, + char __user *, size_t); +ssize_t bl_pipe_downcall(struct file *, const char __user *, size_t); +void bl_pipe_destroy_msg(struct rpc_pipe_msg *); +struct block_device *nfs4_blkdev_get(dev_t dev); +int nfs4_blkdev_put(struct block_device *bdev); +struct pnfs_block_dev *nfs4_blk_decode_device(struct nfs_server *server, + struct pnfs_device *dev, + struct list_head *sdlist); +int nfs4_blk_process_layoutget(struct pnfs_layout_hdr *lo, + struct nfs4_layoutget_res *lgr, gfp_t gfp_flags); + void bl_put_extent(struct pnfs_block_extent *be); #endif /* FS_NFS_NFS4BLOCKLAYOUT_H */ diff --git a/fs/nfs/blocklayout/blocklayoutdev.c b/fs/nfs/blocklayout/blocklayoutdev.c new file mode 100644 index 000000000000..7e1377fcfdce --- /dev/null +++ b/fs/nfs/blocklayout/blocklayoutdev.c @@ -0,0 +1,191 @@ +/* + * linux/fs/nfs/blocklayout/blocklayoutdev.c + * + * Device operations for the pnfs nfs4 file layout driver. + * + * Copyright (c) 2006 The Regents of the University of Michigan. + * All rights reserved. + * + * Andy Adamson + * Fred Isaman + * + * permission is granted to use, copy, create derivative works and + * redistribute this software and such derivative works for any purpose, + * so long as the name of the university of michigan is not used in + * any advertising or publicity pertaining to the use or distribution + * of this software without specific, written prior authorization. if + * the above copyright notice or any other identification of the + * university of michigan is included in any copy of any portion of + * this software, then the disclaimer below must also be included. + * + * this software is provided as is, without representation from the + * university of michigan as to its fitness for any purpose, and without + * warranty by the university of michigan of any kind, either express + * or implied, including without limitation the implied warranties of + * merchantability and fitness for a particular purpose. the regents + * of the university of michigan shall not be liable for any damages, + * including special, indirect, incidental, or consequential damages, + * with respect to any claim arising out or in connection with the use + * of the software, even if it has been or is hereafter advised of the + * possibility of such damages. + */ +#include +#include /* __bread */ + +#include +#include +#include + +#include "blocklayout.h" + +#define NFSDBG_FACILITY NFSDBG_PNFS_LD + +/* Open a block_device by device number. */ +struct block_device *nfs4_blkdev_get(dev_t dev) +{ + struct block_device *bd; + + dprintk("%s enter\n", __func__); + bd = blkdev_get_by_dev(dev, FMODE_READ, NULL); + if (IS_ERR(bd)) + goto fail; + return bd; +fail: + dprintk("%s failed to open device : %ld\n", + __func__, PTR_ERR(bd)); + return NULL; +} + +/* + * Release the block device + */ +int nfs4_blkdev_put(struct block_device *bdev) +{ + dprintk("%s for device %d:%d\n", __func__, MAJOR(bdev->bd_dev), + MINOR(bdev->bd_dev)); + return blkdev_put(bdev, FMODE_READ); +} + +/* + * Shouldn't there be a rpc_generic_upcall() to do this for us? + */ +ssize_t bl_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg, + char __user *dst, size_t buflen) +{ + char *data = (char *)msg->data + msg->copied; + size_t mlen = min(msg->len - msg->copied, buflen); + unsigned long left; + + left = copy_to_user(dst, data, mlen); + if (left == mlen) { + msg->errno = -EFAULT; + return -EFAULT; + } + + mlen -= left; + msg->copied += mlen; + msg->errno = 0; + return mlen; +} + +static struct bl_dev_msg bl_mount_reply; + +ssize_t bl_pipe_downcall(struct file *filp, const char __user *src, + size_t mlen) +{ + if (mlen != sizeof (struct bl_dev_msg)) + return -EINVAL; + + if (copy_from_user(&bl_mount_reply, src, mlen) != 0) + return -EFAULT; + + wake_up(&bl_wq); + + return mlen; +} + +void bl_pipe_destroy_msg(struct rpc_pipe_msg *msg) +{ + if (msg->errno >= 0) + return; + wake_up(&bl_wq); +} + +/* + * Decodes pnfs_block_deviceaddr4 which is XDR encoded in dev->dev_addr_buf. + */ +struct pnfs_block_dev * +nfs4_blk_decode_device(struct nfs_server *server, + struct pnfs_device *dev, + struct list_head *sdlist) +{ + struct pnfs_block_dev *rv = NULL; + struct block_device *bd = NULL; + struct rpc_pipe_msg msg; + struct bl_msg_hdr bl_msg = { + .type = BL_DEVICE_MOUNT, + .totallen = dev->mincount, + }; + uint8_t *dataptr; + DECLARE_WAITQUEUE(wq, current); + struct bl_dev_msg *reply = &bl_mount_reply; + + dprintk("%s CREATING PIPEFS MESSAGE\n", __func__); + dprintk("%s: deviceid: %s, mincount: %d\n", __func__, dev->dev_id.data, + dev->mincount); + + memset(&msg, 0, sizeof(msg)); + msg.data = kzalloc(sizeof(bl_msg) + dev->mincount, GFP_NOFS); + if (!msg.data) { + rv = ERR_PTR(-ENOMEM); + goto out; + } + + memcpy(msg.data, &bl_msg, sizeof(bl_msg)); + dataptr = (uint8_t *) msg.data; + memcpy(&dataptr[sizeof(bl_msg)], dev->area, dev->mincount); + msg.len = sizeof(bl_msg) + dev->mincount; + + dprintk("%s CALLING USERSPACE DAEMON\n", __func__); + add_wait_queue(&bl_wq, &wq); + if (rpc_queue_upcall(bl_device_pipe->d_inode, &msg) < 0) { + remove_wait_queue(&bl_wq, &wq); + goto out; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule(); + __set_current_state(TASK_RUNNING); + remove_wait_queue(&bl_wq, &wq); + + if (reply->status != BL_DEVICE_REQUEST_PROC) { + dprintk("%s failed to open device: %d\n", + __func__, reply->status); + rv = ERR_PTR(-EINVAL); + goto out; + } + + bd = nfs4_blkdev_get(MKDEV(reply->major, reply->minor)); + if (IS_ERR(bd)) { + dprintk("%s failed to open device : %ld\n", + __func__, PTR_ERR(bd)); + goto out; + } + + rv = kzalloc(sizeof(*rv), GFP_NOFS); + if (!rv) { + rv = ERR_PTR(-ENOMEM); + goto out; + } + + rv->bm_mdev = bd; + memcpy(&rv->bm_mdevid, &dev->dev_id, sizeof(struct nfs4_deviceid)); + dprintk("%s Created device %s with bd_block_size %u\n", + __func__, + bd->bd_disk->disk_name, + bd->bd_block_size); + +out: + kfree(msg.data); + return rv; +} diff --git a/fs/nfs/client.c b/fs/nfs/client.c index de00a373f085..5833fbbf59b0 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -105,7 +105,7 @@ struct rpc_program nfs_program = { .nrvers = ARRAY_SIZE(nfs_version), .version = nfs_version, .stats = &nfs_rpcstat, - .pipe_dir_name = "/nfs", + .pipe_dir_name = NFS_PIPE_DIRNAME, }; struct rpc_stat nfs_rpcstat = { diff --git a/include/linux/nfs.h b/include/linux/nfs.h index f387919bbc59..8c6ee44914cb 100644 --- a/include/linux/nfs.h +++ b/include/linux/nfs.h @@ -29,6 +29,8 @@ #define NFS_MNT_VERSION 1 #define NFS_MNT3_VERSION 3 +#define NFS_PIPE_DIRNAME "/nfs" + /* * NFS stats. The good thing with these values is that NFSv3 errors are * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which -- cgit v1.2.3 From 2f9fd182607e7b3bdca35f6ed7f2fae539f7c46b Mon Sep 17 00:00:00 2001 From: Fred Isaman Date: Sat, 30 Jul 2011 20:52:46 -0400 Subject: pnfsblock: call and parse getdevicelist Call GETDEVICELIST during mount, then call and parse GETDEVICEINFO for each device returned. [pnfsblock: get rid of deprecated xdr macros] Signed-off-by: Jim Rees [pnfsblock: fix pnfs_deviceid references] Signed-off-by: Fred Isaman [pnfsblock: fix print format warnings for sector_t and size_t] [pnfs-block: #include ] [pnfsblock: no PNFS_NFS_SERVER] Signed-off-by: Benny Halevy [pnfsblock: fix bug determining size of striped volume] [pnfsblock: fix oops when using multiple devices] Signed-off-by: Fred Isaman Signed-off-by: Benny Halevy Signed-off-by: Benny Halevy [pnfsblock: get rid of vmap and deviceid->area structure] Signed-off-by: Peng Tao Signed-off-by: Jim Rees Signed-off-by: Trond Myklebust --- fs/nfs/blocklayout/blocklayout.c | 138 +++++++++++++++++++++++++++++++++++- fs/nfs/blocklayout/blocklayout.h | 13 +++- fs/nfs/blocklayout/blocklayoutdev.c | 13 +++- fs/nfs/pnfs.h | 1 - include/linux/nfs_fs_sb.h | 1 + 5 files changed, 158 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index e7bc7a57b3bb..6cd7f4f3acdb 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -157,17 +157,153 @@ bl_cleanup_layoutcommit(struct nfs4_layoutcommit_data *lcdata) { } +static void free_blk_mountid(struct block_mount_id *mid) +{ + if (mid) { + struct pnfs_block_dev *dev; + spin_lock(&mid->bm_lock); + while (!list_empty(&mid->bm_devlist)) { + dev = list_first_entry(&mid->bm_devlist, + struct pnfs_block_dev, + bm_node); + list_del(&dev->bm_node); + bl_free_block_dev(dev); + } + spin_unlock(&mid->bm_lock); + kfree(mid); + } +} + +/* This is mostly copied from the filelayout's get_device_info function. + * It seems much of this should be at the generic pnfs level. + */ +static struct pnfs_block_dev * +nfs4_blk_get_deviceinfo(struct nfs_server *server, const struct nfs_fh *fh, + struct nfs4_deviceid *d_id) +{ + struct pnfs_device *dev; + struct pnfs_block_dev *rv = NULL; + u32 max_resp_sz; + int max_pages; + struct page **pages = NULL; + int i, rc; + + /* + * Use the session max response size as the basis for setting + * GETDEVICEINFO's maxcount + */ + max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz; + max_pages = max_resp_sz >> PAGE_SHIFT; + dprintk("%s max_resp_sz %u max_pages %d\n", + __func__, max_resp_sz, max_pages); + + dev = kmalloc(sizeof(*dev), GFP_NOFS); + if (!dev) { + dprintk("%s kmalloc failed\n", __func__); + return NULL; + } + + pages = kzalloc(max_pages * sizeof(struct page *), GFP_NOFS); + if (pages == NULL) { + kfree(dev); + return NULL; + } + for (i = 0; i < max_pages; i++) { + pages[i] = alloc_page(GFP_NOFS); + if (!pages[i]) + goto out_free; + } + + memcpy(&dev->dev_id, d_id, sizeof(*d_id)); + dev->layout_type = LAYOUT_BLOCK_VOLUME; + dev->pages = pages; + dev->pgbase = 0; + dev->pglen = PAGE_SIZE * max_pages; + dev->mincount = 0; + + dprintk("%s: dev_id: %s\n", __func__, dev->dev_id.data); + rc = nfs4_proc_getdeviceinfo(server, dev); + dprintk("%s getdevice info returns %d\n", __func__, rc); + if (rc) + goto out_free; + + rv = nfs4_blk_decode_device(server, dev); + out_free: + for (i = 0; i < max_pages; i++) + __free_page(pages[i]); + kfree(pages); + kfree(dev); + return rv; +} + static int bl_set_layoutdriver(struct nfs_server *server, const struct nfs_fh *fh) { + struct block_mount_id *b_mt_id = NULL; + struct pnfs_devicelist *dlist = NULL; + struct pnfs_block_dev *bdev; + LIST_HEAD(block_disklist); + int status = 0, i; + dprintk("%s enter\n", __func__); - return 0; + + if (server->pnfs_blksize == 0) { + dprintk("%s Server did not return blksize\n", __func__); + return -EINVAL; + } + b_mt_id = kzalloc(sizeof(struct block_mount_id), GFP_NOFS); + if (!b_mt_id) { + status = -ENOMEM; + goto out_error; + } + /* Initialize nfs4 block layout mount id */ + spin_lock_init(&b_mt_id->bm_lock); + INIT_LIST_HEAD(&b_mt_id->bm_devlist); + + dlist = kmalloc(sizeof(struct pnfs_devicelist), GFP_NOFS); + if (!dlist) { + status = -ENOMEM; + goto out_error; + } + dlist->eof = 0; + while (!dlist->eof) { + status = nfs4_proc_getdevicelist(server, fh, dlist); + if (status) + goto out_error; + dprintk("%s GETDEVICELIST numdevs=%i, eof=%i\n", + __func__, dlist->num_devs, dlist->eof); + for (i = 0; i < dlist->num_devs; i++) { + bdev = nfs4_blk_get_deviceinfo(server, fh, + &dlist->dev_id[i]); + if (!bdev) { + status = -ENODEV; + goto out_error; + } + spin_lock(&b_mt_id->bm_lock); + list_add(&bdev->bm_node, &b_mt_id->bm_devlist); + spin_unlock(&b_mt_id->bm_lock); + } + } + dprintk("%s SUCCESS\n", __func__); + server->pnfs_ld_data = b_mt_id; + + out_return: + kfree(dlist); + return status; + + out_error: + free_blk_mountid(b_mt_id); + goto out_return; } static int bl_clear_layoutdriver(struct nfs_server *server) { + struct block_mount_id *b_mt_id = server->pnfs_ld_data; + dprintk("%s enter\n", __func__); + free_blk_mountid(b_mt_id); + dprintk("%s RETURNS\n", __func__); return 0; } diff --git a/fs/nfs/blocklayout/blocklayout.h b/fs/nfs/blocklayout/blocklayout.h index 581d8f47a723..d645880f61a0 100644 --- a/fs/nfs/blocklayout/blocklayout.h +++ b/fs/nfs/blocklayout/blocklayout.h @@ -38,6 +38,11 @@ #include "../pnfs.h" +struct block_mount_id { + spinlock_t bm_lock; /* protects list */ + struct list_head bm_devlist; /* holds pnfs_block_dev */ +}; + struct pnfs_block_dev { struct list_head bm_node; struct nfs4_deviceid bm_mdevid; /* associated devid */ @@ -99,7 +104,10 @@ struct pnfs_block_layout { sector_t bl_blocksize; /* Server blocksize in sectors */ }; -static inline struct pnfs_block_layout *BLK_LO2EXT(struct pnfs_layout_hdr *lo) +#define BLK_ID(lo) ((struct block_mount_id *)(NFS_SERVER(lo->plh_inode)->pnfs_ld_data)) + +static inline struct pnfs_block_layout * +BLK_LO2EXT(struct pnfs_layout_hdr *lo) { return container_of(lo, struct pnfs_block_layout, bl_layout); } @@ -137,8 +145,7 @@ void bl_pipe_destroy_msg(struct rpc_pipe_msg *); struct block_device *nfs4_blkdev_get(dev_t dev); int nfs4_blkdev_put(struct block_device *bdev); struct pnfs_block_dev *nfs4_blk_decode_device(struct nfs_server *server, - struct pnfs_device *dev, - struct list_head *sdlist); + struct pnfs_device *dev); int nfs4_blk_process_layoutget(struct pnfs_layout_hdr *lo, struct nfs4_layoutget_res *lgr, gfp_t gfp_flags); diff --git a/fs/nfs/blocklayout/blocklayoutdev.c b/fs/nfs/blocklayout/blocklayoutdev.c index 64da33a40eaf..b23fe601d1c9 100644 --- a/fs/nfs/blocklayout/blocklayoutdev.c +++ b/fs/nfs/blocklayout/blocklayoutdev.c @@ -116,8 +116,7 @@ void bl_pipe_destroy_msg(struct rpc_pipe_msg *msg) */ struct pnfs_block_dev * nfs4_blk_decode_device(struct nfs_server *server, - struct pnfs_device *dev, - struct list_head *sdlist) + struct pnfs_device *dev) { struct pnfs_block_dev *rv = NULL; struct block_device *bd = NULL; @@ -129,6 +128,7 @@ nfs4_blk_decode_device(struct nfs_server *server, uint8_t *dataptr; DECLARE_WAITQUEUE(wq, current); struct bl_dev_msg *reply = &bl_mount_reply; + int offset, len, i; dprintk("%s CREATING PIPEFS MESSAGE\n", __func__); dprintk("%s: deviceid: %s, mincount: %d\n", __func__, dev->dev_id.data, @@ -143,7 +143,14 @@ nfs4_blk_decode_device(struct nfs_server *server, memcpy(msg.data, &bl_msg, sizeof(bl_msg)); dataptr = (uint8_t *) msg.data; - memcpy(&dataptr[sizeof(bl_msg)], dev->area, dev->mincount); + len = dev->mincount; + offset = sizeof(bl_msg); + for (i = 0; len > 0; i++) { + memcpy(&dataptr[offset], page_address(dev->pages[i]), + len < PAGE_CACHE_SIZE ? len : PAGE_CACHE_SIZE); + len -= PAGE_CACHE_SIZE; + offset += PAGE_CACHE_SIZE; + } msg.len = sizeof(bl_msg) + dev->mincount; dprintk("%s CALLING USERSPACE DAEMON\n", __func__); diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 606fbde2e757..e0b5d80a43f6 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -140,7 +140,6 @@ struct pnfs_device { unsigned int layout_type; unsigned int mincount; struct page **pages; - void *area; unsigned int pgbase; unsigned int pglen; }; diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index b2ea8b82d2cb..cc03fc1dfb72 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -146,6 +146,7 @@ struct nfs_server { struct pnfs_layoutdriver_type *pnfs_curr_ld; /* Active layout driver */ struct rpc_wait_queue roc_rpcwaitq; u32 pnfs_blksize; /* layout_blksize attr */ + void *pnfs_ld_data; /* per mount point data */ /* the following fields are protected by nfs_client->cl_lock */ struct rb_root state_owners; -- cgit v1.2.3 From ca7a71824ac957b1b9d3322656c05aad38d7275c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 2 Jun 2011 19:18:47 +0100 Subject: mfd: Fix bus lock interaction for WM831x IRQ set_type() operation The WM831x IRQ set_type() operation is doing a direct register write when called but since set_type() is called with the bus lock held this isn't legal and could cause deadlocks in the IRQ core. Fix this by posting the updates into an array and syncing in the bus_sync_unlock() callback. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-irq.c | 24 ++++++++++++++++++------ include/linux/mfd/wm831x/core.h | 4 ++++ 2 files changed, 22 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index 42b928ec891e..b23d8d5ee96c 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -348,6 +348,15 @@ static void wm831x_irq_sync_unlock(struct irq_data *data) struct wm831x *wm831x = irq_data_get_irq_chip_data(data); int i; + for (i = 0; i < ARRAY_SIZE(wm831x->gpio_update); i++) { + if (wm831x->gpio_update[i]) { + wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + i, + WM831X_GPN_INT_MODE | WM831X_GPN_POL, + wm831x->gpio_update[i]); + wm831x->gpio_update[i] = 0; + } + } + for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) { /* If there's been a change in the mask write it back * to the hardware. */ @@ -387,7 +396,7 @@ static void wm831x_irq_disable(struct irq_data *data) static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) { struct wm831x *wm831x = irq_data_get_irq_chip_data(data); - int val, irq; + int irq; irq = data->irq - wm831x->irq_base; @@ -399,22 +408,25 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) return -EINVAL; } + /* We set the high bit to flag that we need an update; don't + * do the update here as we can be called with the bus lock + * held. + */ switch (type) { case IRQ_TYPE_EDGE_BOTH: - val = WM831X_GPN_INT_MODE; + wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE; break; case IRQ_TYPE_EDGE_RISING: - val = WM831X_GPN_POL; + wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL; break; case IRQ_TYPE_EDGE_FALLING: - val = 0; + wm831x->gpio_update[irq] = 0x10000; break; default: return -EINVAL; } - return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq, - WM831X_GPN_INT_MODE | WM831X_GPN_POL, val); + return 0; } static struct irq_chip wm831x_irq_chip = { diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index 0d515ee1c247..ebead1c401aa 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -237,6 +237,7 @@ struct regulator_dev; #define WM831X_NUM_IRQ_REGS 5 +#define WM831X_NUM_GPIO_REGS 16 enum wm831x_parent { WM8310 = 0x8310, @@ -272,6 +273,9 @@ struct wm831x { int num_gpio; + /* Used by the interrupt controller code to post writes */ + int gpio_update[WM831X_NUM_GPIO_REGS]; + struct mutex auxadc_lock; struct completion auxadc_done; -- cgit v1.2.3 From 266a5e02fe2690637b2318c9fc5c3513937128f4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 2 Jun 2011 19:18:49 +0100 Subject: mfd: Allow touchscreen to be disabled on wm831x devices Allow platform data to flag the touchscreen as disabled so that if the touch driver is built in we don't end up causing lots of work by spuriously detecting touchscreen activity on systems where it isn't in use. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-core.c | 27 +++++++++++++++++---------- include/linux/mfd/wm831x/pdata.h | 3 +++ 2 files changed, 20 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 04994b23bb6b..ff57a1674758 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -1124,11 +1124,6 @@ static struct mfd_cell wm8311_devs[] = { .num_resources = ARRAY_SIZE(wm831x_status2_resources), .resources = wm831x_status2_resources, }, - { - .name = "wm831x-touch", - .num_resources = ARRAY_SIZE(wm831x_touch_resources), - .resources = wm831x_touch_resources, - }, { .name = "wm831x-watchdog", .num_resources = ARRAY_SIZE(wm831x_wdt_resources), @@ -1285,11 +1280,6 @@ static struct mfd_cell wm8312_devs[] = { .num_resources = ARRAY_SIZE(wm831x_status2_resources), .resources = wm831x_status2_resources, }, - { - .name = "wm831x-touch", - .num_resources = ARRAY_SIZE(wm831x_touch_resources), - .resources = wm831x_touch_resources, - }, { .name = "wm831x-watchdog", .num_resources = ARRAY_SIZE(wm831x_wdt_resources), @@ -1428,6 +1418,15 @@ static struct mfd_cell wm8320_devs[] = { }, }; +static struct mfd_cell touch_devs[] = { + { + .name = "wm831x-touch", + .num_resources = ARRAY_SIZE(wm831x_touch_resources), + .resources = wm831x_touch_resources, + }, +}; + + static struct mfd_cell backlight_devs[] = { { .name = "wm831x-backlight", @@ -1624,12 +1623,20 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8311_devs, ARRAY_SIZE(wm8311_devs), NULL, wm831x->irq_base); + if (!pdata || !pdata->disable_touch) + mfd_add_devices(wm831x->dev, wm831x_num, + touch_devs, ARRAY_SIZE(touch_devs), + NULL, wm831x->irq_base); break; case WM8312: ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8312_devs, ARRAY_SIZE(wm8312_devs), NULL, wm831x->irq_base); + if (!pdata || !pdata->disable_touch) + mfd_add_devices(wm831x->dev, wm831x_num, + touch_devs, ARRAY_SIZE(touch_devs), + NULL, wm831x->irq_base); break; case WM8320: diff --git a/include/linux/mfd/wm831x/pdata.h b/include/linux/mfd/wm831x/pdata.h index ff42d700293f..0ba24599fe51 100644 --- a/include/linux/mfd/wm831x/pdata.h +++ b/include/linux/mfd/wm831x/pdata.h @@ -120,6 +120,9 @@ struct wm831x_pdata { /** Put the /IRQ line into CMOS mode */ bool irq_cmos; + /** Disable the touchscreen */ + bool disable_touch; + int irq_base; int gpio_base; int gpio_defaults[WM831X_GPIO_NUM]; -- cgit v1.2.3 From 5c05a8d1f0105ada3cb04be5b70686fc6b272619 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 2 Jun 2011 19:18:51 +0100 Subject: mfd: Support dynamic allocation of IRQ range for wm831x Use irq_allocate_desc() to get the IRQ range, which turns into a noop on non-sparse systems. Since all existing users are non-sparse there should be no compatibility issues. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-irq.c | 18 +++++++++++++----- include/linux/mfd/wm831x/core.h | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index b23d8d5ee96c..1808deb8083b 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -527,13 +527,22 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) 0xffff); } - if (!pdata || !pdata->irq_base) { - dev_err(wm831x->dev, - "No interrupt base specified, no interrupts\n"); + /* Try to dynamically allocate IRQs if no base is specified */ + if (!pdata || !pdata->irq_base) + wm831x->irq_base = -1; + else + wm831x->irq_base = pdata->irq_base; + + wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0, + WM831X_NUM_IRQS, 0); + if (wm831x->irq_base < 0) { + dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n", + wm831x->irq_base); + wm831x->irq_base = 0; return 0; } - if (pdata->irq_cmos) + if (pdata && pdata->irq_cmos) i = 0; else i = WM831X_IRQ_OD; @@ -553,7 +562,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) } wm831x->irq = irq; - wm831x->irq_base = pdata->irq_base; /* Register them with genirq */ for (cur_irq = wm831x->irq_base; diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index ebead1c401aa..9564cf315276 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -262,7 +262,7 @@ struct wm831x { int irq; /* Our chip IRQ */ struct mutex irq_lock; - unsigned int irq_base; + int irq_base; int irq_masks_cur[WM831X_NUM_IRQ_REGS]; /* Currently active value */ int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */ -- cgit v1.2.3 From c1a82780b41e78f31636c49279ce940afe60a453 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 2 Jun 2011 19:18:52 +0100 Subject: mfd: Read wm831x AUXADC conversion results before acknowledging interrupt Ensure that there's no possibility of loosing an AUXADC interrupt by reading the conversion result in the IRQ handler when using interrupts. Otherwise it's possible that under very heavy load a new conversion could be initiated before the acknowledgement for a previous interrupt has happened. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-core.c | 47 +++++++++++++++++++++++++++-------------- include/linux/mfd/wm831x/core.h | 1 + 2 files changed, 32 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 58f033b57efe..480abc18f9f3 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -376,6 +376,16 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) goto disable; } } + + ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); + if (ret < 0) { + dev_err(wm831x->dev, + "Failed to read AUXADC data: %d\n", ret); + goto disable; + } + + wm831x->auxadc_data = ret; + } else { /* If we are using interrupts then wait for the * interrupt to complete. Use an extremely long @@ -390,23 +400,18 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) } } - ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); - if (ret < 0) { - dev_err(wm831x->dev, "Failed to read AUXADC data: %d\n", ret); + src = ((wm831x->auxadc_data & WM831X_AUX_DATA_SRC_MASK) + >> WM831X_AUX_DATA_SRC_SHIFT) - 1; + + if (src == 14) + src = WM831X_AUX_CAL; + + if (src != input) { + dev_err(wm831x->dev, "Data from source %d not %d\n", + src, input); + ret = -EINVAL; } else { - src = ((ret & WM831X_AUX_DATA_SRC_MASK) - >> WM831X_AUX_DATA_SRC_SHIFT) - 1; - - if (src == 14) - src = WM831X_AUX_CAL; - - if (src != input) { - dev_err(wm831x->dev, "Data from source %d not %d\n", - src, input); - ret = -EINVAL; - } else { - ret &= WM831X_AUX_DATA_MASK; - } + ret = wm831x->auxadc_data & WM831X_AUX_DATA_MASK; } disable: @@ -420,6 +425,16 @@ EXPORT_SYMBOL_GPL(wm831x_auxadc_read); static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data) { struct wm831x *wm831x = irq_data; + int ret; + + ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); + if (ret < 0) { + dev_err(wm831x->dev, + "Failed to read AUXADC data: %d\n", ret); + wm831x->auxadc_data = 0xffff; + } else { + wm831x->auxadc_data = ret; + } complete(&wm831x->auxadc_done); diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index 9564cf315276..592e4fd6ab3f 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -278,6 +278,7 @@ struct wm831x { struct mutex auxadc_lock; struct completion auxadc_done; + u16 auxadc_data; /* The WM831x has a security key blocking access to certain * registers. The mutex is taken by the accessors for locking -- cgit v1.2.3 From 78bb3688ea1830672b8095fb6388593f582cd591 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 7 Jun 2011 11:47:28 +0100 Subject: mfd: Support multiple active WM831x AUXADC conversions The WM831x AUXADC hardware can schedule multiple conversions at once, allowing higher performance when more than one source is in use as we can have the hardware start new conversions without having to wait for a register write. Take advantage of this in the interrupt driven case, maintaining a list of callers that are waiting for AUXADC conversions and completing them all simultaneously. The external interface of the AUXADC is not changed so there will be limited use of the feature immediately. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-auxadc.c | 254 ++++++++++++++++++++++++++++------------ include/linux/mfd/wm831x/core.h | 13 +- 2 files changed, 188 insertions(+), 79 deletions(-) (limited to 'include') diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c index 2fc9531b243d..87210954a066 100644 --- a/drivers/mfd/wm831x-auxadc.c +++ b/drivers/mfd/wm831x-auxadc.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -25,19 +26,139 @@ #include #include -/** - * wm831x_auxadc_read: Read a value from the WM831x AUXADC - * - * @wm831x: Device to read from. - * @input: AUXADC input to read. - */ -int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) +struct wm831x_auxadc_req { + struct list_head list; + enum wm831x_auxadc input; + int val; + struct completion done; +}; + +static int wm831x_auxadc_read_irq(struct wm831x *wm831x, + enum wm831x_auxadc input) { - int ret, src, irq_masked, timeout; + struct wm831x_auxadc_req *req; + int ret; + bool ena = false; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + init_completion(&req->done); + req->input = input; + req->val = -ETIMEDOUT; + + mutex_lock(&wm831x->auxadc_lock); + + /* Enqueue the request */ + list_add(&req->list, &wm831x->auxadc_pending); + + ena = !wm831x->auxadc_active; + + if (ena) { + ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, + WM831X_AUX_ENA, WM831X_AUX_ENA); + if (ret != 0) { + dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", + ret); + goto out; + } + } + + /* Enable the conversion if not already running */ + if (!(wm831x->auxadc_active & (1 << input))) { + ret = wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE, + 1 << input, 1 << input); + if (ret != 0) { + dev_err(wm831x->dev, + "Failed to set AUXADC source: %d\n", ret); + goto out; + } + + wm831x->auxadc_active |= 1 << input; + } + + /* We convert at the fastest rate possible */ + if (ena) { + ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, + WM831X_AUX_CVT_ENA | + WM831X_AUX_RATE_MASK, + WM831X_AUX_CVT_ENA | + WM831X_AUX_RATE_MASK); + if (ret != 0) { + dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", + ret); + goto out; + } + } + + mutex_unlock(&wm831x->auxadc_lock); + + /* Wait for an interrupt */ + wait_for_completion_timeout(&req->done, msecs_to_jiffies(500)); + + mutex_lock(&wm831x->auxadc_lock); + + list_del(&req->list); + ret = req->val; + +out: + mutex_unlock(&wm831x->auxadc_lock); + + kfree(req); + + return ret; +} + +static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data) +{ + struct wm831x *wm831x = irq_data; + struct wm831x_auxadc_req *req; + int ret, input, val; + + ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); + if (ret < 0) { + dev_err(wm831x->dev, + "Failed to read AUXADC data: %d\n", ret); + return IRQ_NONE; + } + + input = ((ret & WM831X_AUX_DATA_SRC_MASK) + >> WM831X_AUX_DATA_SRC_SHIFT) - 1; + + if (input == 14) + input = WM831X_AUX_CAL; - /* Are we using the interrupt? */ - irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK); - irq_masked &= WM831X_AUXADC_DATA_EINT; + val = ret & WM831X_AUX_DATA_MASK; + + mutex_lock(&wm831x->auxadc_lock); + + /* Disable this conversion, we're about to complete all users */ + wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE, + 1 << input, 0); + wm831x->auxadc_active &= ~(1 << input); + + /* Turn off the entire convertor if idle */ + if (!wm831x->auxadc_active) + wm831x_reg_write(wm831x, WM831X_AUXADC_CONTROL, 0); + + /* Wake up any threads waiting for this request */ + list_for_each_entry(req, &wm831x->auxadc_pending, list) { + if (req->input == input) { + req->val = val; + complete(&req->done); + } + } + + mutex_unlock(&wm831x->auxadc_lock); + + return IRQ_HANDLED; +} + +static int wm831x_auxadc_read_polled(struct wm831x *wm831x, + enum wm831x_auxadc input) +{ + int ret, src, timeout; mutex_lock(&wm831x->auxadc_lock); @@ -57,9 +178,6 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) goto out; } - /* Clear any notification from a very late arriving interrupt */ - try_wait_for_completion(&wm831x->auxadc_done); - ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA); if (ret < 0) { @@ -67,59 +185,42 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) goto disable; } - if (irq_masked) { - /* If we're not using interrupts then poll the - * interrupt status register */ - timeout = 5; - while (timeout) { - msleep(1); - - ret = wm831x_reg_read(wm831x, - WM831X_INTERRUPT_STATUS_1); - if (ret < 0) { - dev_err(wm831x->dev, - "ISR 1 read failed: %d\n", ret); - goto disable; - } - - /* Did it complete? */ - if (ret & WM831X_AUXADC_DATA_EINT) { - wm831x_reg_write(wm831x, - WM831X_INTERRUPT_STATUS_1, - WM831X_AUXADC_DATA_EINT); - break; - } else { - dev_err(wm831x->dev, - "AUXADC conversion timeout\n"); - ret = -EBUSY; - goto disable; - } - } + /* If we're not using interrupts then poll the + * interrupt status register */ + timeout = 5; + while (timeout) { + msleep(1); - ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); + ret = wm831x_reg_read(wm831x, + WM831X_INTERRUPT_STATUS_1); if (ret < 0) { dev_err(wm831x->dev, - "Failed to read AUXADC data: %d\n", ret); + "ISR 1 read failed: %d\n", ret); goto disable; } - wm831x->auxadc_data = ret; - - } else { - /* If we are using interrupts then wait for the - * interrupt to complete. Use an extremely long - * timeout to handle situations with heavy load where - * the notification of the interrupt may be delayed by - * threaded IRQ handling. */ - if (!wait_for_completion_timeout(&wm831x->auxadc_done, - msecs_to_jiffies(500))) { - dev_err(wm831x->dev, "Timed out waiting for AUXADC\n"); + /* Did it complete? */ + if (ret & WM831X_AUXADC_DATA_EINT) { + wm831x_reg_write(wm831x, + WM831X_INTERRUPT_STATUS_1, + WM831X_AUXADC_DATA_EINT); + break; + } else { + dev_err(wm831x->dev, + "AUXADC conversion timeout\n"); ret = -EBUSY; goto disable; } } - src = ((wm831x->auxadc_data & WM831X_AUX_DATA_SRC_MASK) + ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); + if (ret < 0) { + dev_err(wm831x->dev, + "Failed to read AUXADC data: %d\n", ret); + goto disable; + } + + src = ((ret & WM831X_AUX_DATA_SRC_MASK) >> WM831X_AUX_DATA_SRC_SHIFT) - 1; if (src == 14) @@ -130,7 +231,7 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) src, input); ret = -EINVAL; } else { - ret = wm831x->auxadc_data & WM831X_AUX_DATA_MASK; + ret &= WM831X_AUX_DATA_MASK; } disable: @@ -139,26 +240,18 @@ out: mutex_unlock(&wm831x->auxadc_lock); return ret; } -EXPORT_SYMBOL_GPL(wm831x_auxadc_read); -static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data) +/** + * wm831x_auxadc_read: Read a value from the WM831x AUXADC + * + * @wm831x: Device to read from. + * @input: AUXADC input to read. + */ +int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) { - struct wm831x *wm831x = irq_data; - int ret; - - ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); - if (ret < 0) { - dev_err(wm831x->dev, - "Failed to read AUXADC data: %d\n", ret); - wm831x->auxadc_data = 0xffff; - } else { - wm831x->auxadc_data = ret; - } - - complete(&wm831x->auxadc_done); - - return IRQ_HANDLED; + return wm831x->auxadc_read(wm831x, input); } +EXPORT_SYMBOL_GPL(wm831x_auxadc_read); /** * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC @@ -185,15 +278,22 @@ void wm831x_auxadc_init(struct wm831x *wm831x) int ret; mutex_init(&wm831x->auxadc_lock); - init_completion(&wm831x->auxadc_done); + INIT_LIST_HEAD(&wm831x->auxadc_pending); + + if (wm831x->irq && wm831x->irq_base) { + wm831x->auxadc_read = wm831x_auxadc_read_irq; - if (wm831x->irq_base) { ret = request_threaded_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, NULL, wm831x_auxadc_irq, 0, "auxadc", wm831x); - if (ret < 0) + if (ret < 0) { dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n", ret); + wm831x->auxadc_read = NULL; + } } + + if (!wm831x->auxadc_read) + wm831x->auxadc_read = wm831x_auxadc_read_polled; } diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index 592e4fd6ab3f..9fa14cdc1590 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -17,6 +17,7 @@ #include #include +#include /* * Register values. @@ -249,6 +250,12 @@ enum wm831x_parent { WM8326 = 0x8326, }; +struct wm831x; +enum wm831x_auxadc; + +typedef int (*wm831x_auxadc_read_fn)(struct wm831x *wm831x, + enum wm831x_auxadc input); + struct wm831x { struct mutex io_lock; @@ -277,8 +284,9 @@ struct wm831x { int gpio_update[WM831X_NUM_GPIO_REGS]; struct mutex auxadc_lock; - struct completion auxadc_done; - u16 auxadc_data; + struct list_head auxadc_pending; + u16 auxadc_active; + wm831x_auxadc_read_fn auxadc_read; /* The WM831x has a security key blocking access to certain * registers. The mutex is taken by the accessors for locking @@ -305,5 +313,6 @@ void wm831x_device_exit(struct wm831x *wm831x); int wm831x_device_suspend(struct wm831x *wm831x); int wm831x_irq_init(struct wm831x *wm831x, int irq); void wm831x_irq_exit(struct wm831x *wm831x); +void wm831x_auxadc_init(struct wm831x *wm831x); #endif -- cgit v1.2.3 From 36e52873c6393b569f2befcdd1847929211892b8 Mon Sep 17 00:00:00 2001 From: Margarita Olaya Date: Thu, 9 Jun 2011 14:50:00 -0500 Subject: mfd: tps65912: Add new mfd device The tps65912 chip is a power management IC. It contains the following components: - Regulators - GPIO controller The core driver is registered as a platform driver, it provides communication through I2C and SPI interfaces. Signed-off-by: Margarita Olaya Cabrera Acked-by: Samuel Ortiz Acked-by: Liam Girdwood Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 22 +++ drivers/mfd/Makefile | 4 + drivers/mfd/tps65912-core.c | 164 ++++++++++++++++++++++ drivers/mfd/tps65912-i2c.c | 139 +++++++++++++++++++ drivers/mfd/tps65912-spi.c | 142 +++++++++++++++++++ include/linux/mfd/tps65912.h | 321 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 792 insertions(+) create mode 100644 drivers/mfd/tps65912-core.c create mode 100644 drivers/mfd/tps65912-i2c.c create mode 100644 drivers/mfd/tps65912-spi.c create mode 100644 include/linux/mfd/tps65912.h (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1e2c9f03629b..f94036a4f33d 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -171,6 +171,28 @@ config MFD_TPS6586X This driver can also be built as a module. If so, the module will be called tps6586x. +config MFD_TPS65912 + bool "TPS65912 PMIC" + depends on GPIOLIB + +config MFD_TPS65912_I2C + bool "TPS95612 Power Management chip with I2C" + select MFD_CORE + select MFD_TPS65912 + depends on I2C=y && GPIOLIB + help + If you say yes here you get support for the TPS65912 series of + PM chips with I2C interface. + +config MFD_TPS65912_SPI + bool "TPS65912 Power Management chip with SPI" + select MFD_CORE + select MFD_TPS65912 + depends on SPI_MASTER && GPIOLIB + help + If you say yes here you get support for the TPS65912 series of + PM chips with SPI interface. + config MENELAUS bool "Texas Instruments TWL92330/Menelaus PM chip" depends on I2C=y && ARCH_OMAP2 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 0889f53c8b02..c9925e5dad99 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -36,6 +36,10 @@ obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o +tps65912-objs := tps65912-core.o +obj-$(CONFIG_MFD_TPS65912) += tps65912.o +obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o +obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c new file mode 100644 index 000000000000..10baf7655542 --- /dev/null +++ b/drivers/mfd/tps65912-core.c @@ -0,0 +1,164 @@ +/* + * tps65912-core.c -- TI TPS65912x + * + * Copyright 2011 Texas Instruments Inc. + * + * Author: Margarita Olaya Cabrera + * + * 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 driver is based on wm8350 implementation. + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct mfd_cell tps65912s[] = { + { + .name = "tps65912-pmic", + }, +}; + +int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask) +{ + u8 data; + int err; + + mutex_lock(&tps65912->io_mutex); + + err = tps65912->read(tps65912, reg, 1, &data); + if (err) { + dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg); + goto out; + } + + data |= mask; + err = tps65912->write(tps65912, reg, 1, &data); + if (err) + dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg); + +out: + mutex_unlock(&tps65912->io_mutex); + return err; +} +EXPORT_SYMBOL_GPL(tps65912_set_bits); + +int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask) +{ + u8 data; + int err; + + mutex_lock(&tps65912->io_mutex); + err = tps65912->read(tps65912, reg, 1, &data); + if (err) { + dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg); + goto out; + } + + data &= ~mask; + err = tps65912->write(tps65912, reg, 1, &data); + if (err) + dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg); + +out: + mutex_unlock(&tps65912->io_mutex); + return err; +} +EXPORT_SYMBOL_GPL(tps65912_clear_bits); + +static inline int tps65912_read(struct tps65912 *tps65912, u8 reg) +{ + u8 val; + int err; + + err = tps65912->read(tps65912, reg, 1, &val); + if (err < 0) + return err; + + return val; +} + +static inline int tps65912_write(struct tps65912 *tps65912, u8 reg, u8 val) +{ + return tps65912->write(tps65912, reg, 1, &val); +} + +int tps65912_reg_read(struct tps65912 *tps65912, u8 reg) +{ + int data; + + mutex_lock(&tps65912->io_mutex); + + data = tps65912_read(tps65912, reg); + if (data < 0) + dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg); + + mutex_unlock(&tps65912->io_mutex); + return data; +} +EXPORT_SYMBOL_GPL(tps65912_reg_read); + +int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val) +{ + int err; + + mutex_lock(&tps65912->io_mutex); + + err = tps65912_write(tps65912, reg, val); + if (err < 0) + dev_err(tps65912->dev, "Write for reg 0x%x failed\n", reg); + + mutex_unlock(&tps65912->io_mutex); + return err; +} +EXPORT_SYMBOL_GPL(tps65912_reg_write); + +int tps65912_device_init(struct tps65912 *tps65912) +{ + struct tps65912_board *pmic_plat_data = tps65912->dev->platform_data; + int ret, dcdc_avs, value; + + mutex_init(&tps65912->io_mutex); + dev_set_drvdata(tps65912->dev, tps65912); + + dcdc_avs = (pmic_plat_data->is_dcdc1_avs << 0 | + pmic_plat_data->is_dcdc2_avs << 1 | + pmic_plat_data->is_dcdc3_avs << 2 | + pmic_plat_data->is_dcdc4_avs << 3); + if (dcdc_avs) { + tps65912->read(tps65912, TPS65912_I2C_SPI_CFG, 1, &value); + dcdc_avs |= value; + tps65912->write(tps65912, TPS65912_I2C_SPI_CFG, 1, &dcdc_avs); + } + + ret = mfd_add_devices(tps65912->dev, -1, + tps65912s, ARRAY_SIZE(tps65912s), + NULL, 0); + if (ret < 0) + goto err; + + return ret; + +err: + mfd_remove_devices(tps65912->dev); + kfree(tps65912); + return ret; +} + +void tps65912_device_exit(struct tps65912 *tps65912) +{ + mfd_remove_devices(tps65912->dev); + kfree(tps65912); +} + +MODULE_AUTHOR("Margarita Olaya "); +MODULE_DESCRIPTION("TPS65912x chip family multi-function driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/tps65912-i2c.c b/drivers/mfd/tps65912-i2c.c new file mode 100644 index 000000000000..9ed123aa6624 --- /dev/null +++ b/drivers/mfd/tps65912-i2c.c @@ -0,0 +1,139 @@ +/* + * tps65912-i2c.c -- I2C access for TI TPS65912x PMIC + * + * Copyright 2011 Texas Instruments Inc. + * + * Author: Margarita Olaya Cabrera + * + * 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 driver is based on wm8350 implementation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int tps65912_i2c_read(struct tps65912 *tps65912, u8 reg, + int bytes, void *dest) +{ + struct i2c_client *i2c = tps65912->control_data; + struct i2c_msg xfer[2]; + int ret; + + /* Write register */ + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = ® + + /* Read data */ + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = bytes; + xfer[1].buf = dest; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (ret == 2) + ret = 0; + else if (ret >= 0) + ret = -EIO; + return ret; +} + +static int tps65912_i2c_write(struct tps65912 *tps65912, u8 reg, + int bytes, void *src) +{ + struct i2c_client *i2c = tps65912->control_data; + /* we add 1 byte for device register */ + u8 msg[TPS6591X_MAX_REGISTER + 1]; + int ret; + + if (bytes > (TPS6591X_MAX_REGISTER + 1)) + return -EINVAL; + + msg[0] = reg; + memcpy(&msg[1], src, bytes); + + ret = i2c_master_send(i2c, msg, bytes + 1); + if (ret < 0) + return ret; + if (ret != bytes + 1) + return -EIO; + + return 0; +} + +static int tps65912_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tps65912 *tps65912; + + tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL); + if (tps65912 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, tps65912); + tps65912->dev = &i2c->dev; + tps65912->control_data = i2c; + tps65912->read = tps65912_i2c_read; + tps65912->write = tps65912_i2c_write; + + return tps65912_device_init(tps65912); +} + +static int tps65912_i2c_remove(struct i2c_client *i2c) +{ + struct tps65912 *tps65912 = i2c_get_clientdata(i2c); + + tps65912_device_exit(tps65912); + + return 0; +} + +static const struct i2c_device_id tps65912_i2c_id[] = { + {"tps65912", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tps65912_i2c_id); + +static struct i2c_driver tps65912_i2c_driver = { + .driver = { + .name = "tps65912", + .owner = THIS_MODULE, + }, + .probe = tps65912_i2c_probe, + .remove = tps65912_i2c_remove, + .id_table = tps65912_i2c_id, +}; + +static int __init tps65912_i2c_init(void) +{ + int ret; + + ret = i2c_add_driver(&tps65912_i2c_driver); + if (ret != 0) + pr_err("Failed to register TPS65912 I2C driver: %d\n", ret); + + return ret; +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(tps65912_i2c_init); + +static void __exit tps65912_i2c_exit(void) +{ + i2c_del_driver(&tps65912_i2c_driver); +} +module_exit(tps65912_i2c_exit); + +MODULE_AUTHOR("Margarita Olaya "); +MODULE_DESCRIPTION("TPS6591x chip family multi-function driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/tps65912-spi.c b/drivers/mfd/tps65912-spi.c new file mode 100644 index 000000000000..6d71e0d25744 --- /dev/null +++ b/drivers/mfd/tps65912-spi.c @@ -0,0 +1,142 @@ +/* + * tps65912-spi.c -- SPI access for TI TPS65912x PMIC + * + * Copyright 2011 Texas Instruments Inc. + * + * Author: Margarita Olaya Cabrera + * + * 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 driver is based on wm8350 implementation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int tps65912_spi_write(struct tps65912 *tps65912, u8 addr, + int bytes, void *src) +{ + struct spi_device *spi = tps65912->control_data; + u8 *data = (u8 *) src; + int ret; + /* bit 23 is the read/write bit */ + unsigned long spi_data = 1 << 23 | addr << 15 | *data; + struct spi_transfer xfer; + struct spi_message msg; + u32 tx_buf, rx_buf; + + tx_buf = spi_data; + rx_buf = 0; + + xfer.tx_buf = &tx_buf; + xfer.rx_buf = NULL; + xfer.len = sizeof(unsigned long); + xfer.bits_per_word = 24; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(spi, &msg); + return ret; +} + +static int tps65912_spi_read(struct tps65912 *tps65912, u8 addr, + int bytes, void *dest) +{ + struct spi_device *spi = tps65912->control_data; + /* bit 23 is the read/write bit */ + unsigned long spi_data = 0 << 23 | addr << 15; + struct spi_transfer xfer; + struct spi_message msg; + int ret; + u8 *data = (u8 *) dest; + u32 tx_buf, rx_buf; + + tx_buf = spi_data; + rx_buf = 0; + + xfer.tx_buf = &tx_buf; + xfer.rx_buf = &rx_buf; + xfer.len = sizeof(unsigned long); + xfer.bits_per_word = 24; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + if (spi == NULL) + return 0; + + ret = spi_sync(spi, &msg); + if (ret == 0) + *data = (u8) (rx_buf & 0xFF); + return ret; +} + +static int __devinit tps65912_spi_probe(struct spi_device *spi) +{ + struct tps65912 *tps65912; + + tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL); + if (tps65912 == NULL) + return -ENOMEM; + + tps65912->dev = &spi->dev; + tps65912->control_data = spi; + tps65912->read = tps65912_spi_read; + tps65912->write = tps65912_spi_write; + + spi_set_drvdata(spi, tps65912); + + return tps65912_device_init(tps65912); +} + +static int __devexit tps65912_spi_remove(struct spi_device *spi) +{ + struct tps65912 *tps65912 = spi_get_drvdata(spi); + + tps65912_device_exit(tps65912); + + return 0; +} + +static struct spi_driver tps65912_spi_driver = { + .driver = { + .name = "tps65912", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = tps65912_spi_probe, + .remove = __devexit_p(tps65912_spi_remove), +}; + +static int __init tps65912_spi_init(void) +{ + int ret; + + ret = spi_register_driver(&tps65912_spi_driver); + if (ret != 0) + pr_err("Failed to register TPS65912 SPI driver: %d\n", ret); + + return 0; +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(tps65912_spi_init); + +static void __exit tps65912_spi_exit(void) +{ + spi_unregister_driver(&tps65912_spi_driver); +} +module_exit(tps65912_spi_exit); + +MODULE_AUTHOR("Margarita Olaya "); +MODULE_DESCRIPTION("SPI support for TPS65912 chip family mfd"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/tps65912.h b/include/linux/mfd/tps65912.h new file mode 100644 index 000000000000..48d3b53316a9 --- /dev/null +++ b/include/linux/mfd/tps65912.h @@ -0,0 +1,321 @@ +/* + * tps65912.h -- TI TPS6591x + * + * Copyright 2011 Texas Instruments Inc. + * + * Author: Margarita Olaya + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __LINUX_MFD_TPS65912_H +#define __LINUX_MFD_TPS65912_H + +/* TPS regulator type list */ +#define REGULATOR_LDO 0 +#define REGULATOR_DCDC 1 + +/* + * List of registers for TPS65912 + */ + +#define TPS65912_DCDC1_CTRL 0x00 +#define TPS65912_DCDC2_CTRL 0x01 +#define TPS65912_DCDC3_CTRL 0x02 +#define TPS65912_DCDC4_CTRL 0x03 +#define TPS65912_DCDC1_OP 0x04 +#define TPS65912_DCDC1_AVS 0x05 +#define TPS65912_DCDC1_LIMIT 0x06 +#define TPS65912_DCDC2_OP 0x07 +#define TPS65912_DCDC2_AVS 0x08 +#define TPS65912_DCDC2_LIMIT 0x09 +#define TPS65912_DCDC3_OP 0x0A +#define TPS65912_DCDC3_AVS 0x0B +#define TPS65912_DCDC3_LIMIT 0x0C +#define TPS65912_DCDC4_OP 0x0D +#define TPS65912_DCDC4_AVS 0x0E +#define TPS65912_DCDC4_LIMIT 0x0F +#define TPS65912_LDO1_OP 0x10 +#define TPS65912_LDO1_AVS 0x11 +#define TPS65912_LDO1_LIMIT 0x12 +#define TPS65912_LDO2_OP 0x13 +#define TPS65912_LDO2_AVS 0x14 +#define TPS65912_LDO2_LIMIT 0x15 +#define TPS65912_LDO3_OP 0x16 +#define TPS65912_LDO3_AVS 0x17 +#define TPS65912_LDO3_LIMIT 0x18 +#define TPS65912_LDO4_OP 0x19 +#define TPS65912_LDO4_AVS 0x1A +#define TPS65912_LDO4_LIMIT 0x1B +#define TPS65912_LDO5 0x1C +#define TPS65912_LDO6 0x1D +#define TPS65912_LDO7 0x1E +#define TPS65912_LDO8 0x1F +#define TPS65912_LDO9 0x20 +#define TPS65912_LDO10 0x21 +#define TPS65912_THRM 0x22 +#define TPS65912_CLK32OUT 0x23 +#define TPS65912_DEVCTRL 0x24 +#define TPS65912_DEVCTRL2 0x25 +#define TPS65912_I2C_SPI_CFG 0x26 +#define TPS65912_KEEP_ON 0x27 +#define TPS65912_KEEP_ON2 0x28 +#define TPS65912_SET_OFF1 0x29 +#define TPS65912_SET_OFF2 0x2A +#define TPS65912_DEF_VOLT 0x2B +#define TPS65912_DEF_VOLT_MAPPING 0x2C +#define TPS65912_DISCHARGE 0x2D +#define TPS65912_DISCHARGE2 0x2E +#define TPS65912_EN1_SET1 0x2F +#define TPS65912_EN1_SET2 0x30 +#define TPS65912_EN2_SET1 0x31 +#define TPS65912_EN2_SET2 0x32 +#define TPS65912_EN3_SET1 0x33 +#define TPS65912_EN3_SET2 0x34 +#define TPS65912_EN4_SET1 0x35 +#define TPS65912_EN4_SET2 0x36 +#define TPS65912_PGOOD 0x37 +#define TPS65912_PGOOD2 0x38 +#define TPS65912_INT_STS 0x39 +#define TPS65912_INT_MSK 0x3A +#define TPS65912_INT_STS2 0x3B +#define TPS65912_INT_MSK2 0x3C +#define TPS65912_INT_STS3 0x3D +#define TPS65912_INT_MSK3 0x3E +#define TPS65912_INT_STS4 0x3F +#define TPS65912_INT_MSK4 0x40 +#define TPS65912_GPIO1 0x41 +#define TPS65912_GPIO2 0x42 +#define TPS65912_GPIO3 0x43 +#define TPS65912_GPIO4 0x44 +#define TPS65912_GPIO5 0x45 +#define TPS65912_VMON 0x46 +#define TPS65912_LEDA_CTRL1 0x47 +#define TPS65912_LEDA_CTRL2 0x48 +#define TPS65912_LEDA_CTRL3 0x49 +#define TPS65912_LEDA_CTRL4 0x4A +#define TPS65912_LEDA_CTRL5 0x4B +#define TPS65912_LEDA_CTRL6 0x4C +#define TPS65912_LEDA_CTRL7 0x4D +#define TPS65912_LEDA_CTRL8 0x4E +#define TPS65912_LEDB_CTRL1 0x4F +#define TPS65912_LEDB_CTRL2 0x50 +#define TPS65912_LEDB_CTRL3 0x51 +#define TPS65912_LEDB_CTRL4 0x52 +#define TPS65912_LEDB_CTRL5 0x53 +#define TPS65912_LEDB_CTRL6 0x54 +#define TPS65912_LEDB_CTRL7 0x55 +#define TPS65912_LEDB_CTRL8 0x56 +#define TPS65912_LEDC_CTRL1 0x57 +#define TPS65912_LEDC_CTRL2 0x58 +#define TPS65912_LEDC_CTRL3 0x59 +#define TPS65912_LEDC_CTRL4 0x5A +#define TPS65912_LEDC_CTRL5 0x5B +#define TPS65912_LEDC_CTRL6 0x5C +#define TPS65912_LEDC_CTRL7 0x5D +#define TPS65912_LEDC_CTRL8 0x5E +#define TPS65912_LED_RAMP_UP_TIME 0x5F +#define TPS65912_LED_RAMP_DOWN_TIME 0x60 +#define TPS65912_LED_SEQ_EN 0x61 +#define TPS65912_LOADSWITCH 0x62 +#define TPS65912_SPARE 0x63 +#define TPS65912_VERNUM 0x64 +#define TPS6591X_MAX_REGISTER 0x64 + +/* IRQ Definitions */ +#define TPS65912_IRQ_PWRHOLD_F 0 +#define TPS65912_IRQ_VMON 1 +#define TPS65912_IRQ_PWRON 2 +#define TPS65912_IRQ_PWRON_LP 3 +#define TPS65912_IRQ_PWRHOLD_R 4 +#define TPS65912_IRQ_HOTDIE 5 +#define TPS65912_IRQ_GPIO1_R 6 +#define TPS65912_IRQ_GPIO1_F 7 +#define TPS65912_IRQ_GPIO2_R 8 +#define TPS65912_IRQ_GPIO2_F 9 +#define TPS65912_IRQ_GPIO3_R 10 +#define TPS65912_IRQ_GPIO3_F 11 +#define TPS65912_IRQ_GPIO4_R 12 +#define TPS65912_IRQ_GPIO4_F 13 +#define TPS65912_IRQ_GPIO5_R 14 +#define TPS65912_IRQ_GPIO5_F 15 +#define TPS65912_IRQ_PGOOD_DCDC1 16 +#define TPS65912_IRQ_PGOOD_DCDC2 17 +#define TPS65912_IRQ_PGOOD_DCDC3 18 +#define TPS65912_IRQ_PGOOD_DCDC4 19 +#define TPS65912_IRQ_PGOOD_LDO1 20 +#define TPS65912_IRQ_PGOOD_LDO2 21 +#define TPS65912_IRQ_PGOOD_LDO3 22 +#define TPS65912_IRQ_PGOOD_LDO4 23 +#define TPS65912_IRQ_PGOOD_LDO5 24 +#define TPS65912_IRQ_PGOOD_LDO6 25 +#define TPS65912_IRQ_PGOOD_LDO7 26 +#define TPS65912_IRQ_PGOOD_LD08 27 +#define TPS65912_IRQ_PGOOD_LDO9 28 +#define TPS65912_IRQ_PGOOD_LDO10 29 + +#define TPS65912_NUM_IRQ 30 + +/* GPIO 1 and 2 Register Definitions */ +#define GPIO_SLEEP_MASK 0x80 +#define GPIO_SLEEP_SHIFT 7 +#define GPIO_DEB_MASK 0x10 +#define GPIO_DEB_SHIFT 4 +#define GPIO_CFG_MASK 0x04 +#define GPIO_CFG_SHIFT 2 +#define GPIO_STS_MASK 0x02 +#define GPIO_STS_SHIFT 1 +#define GPIO_SET_MASK 0x01 +#define GPIO_SET_SHIFT 0 + +/* GPIO 3 Register Definitions */ +#define GPIO3_SLEEP_MASK 0x80 +#define GPIO3_SLEEP_SHIFT 7 +#define GPIO3_SEL_MASK 0x40 +#define GPIO3_SEL_SHIFT 6 +#define GPIO3_ODEN_MASK 0x20 +#define GPIO3_ODEN_SHIFT 5 +#define GPIO3_DEB_MASK 0x10 +#define GPIO3_DEB_SHIFT 4 +#define GPIO3_PDEN_MASK 0x08 +#define GPIO3_PDEN_SHIFT 3 +#define GPIO3_CFG_MASK 0x04 +#define GPIO3_CFG_SHIFT 2 +#define GPIO3_STS_MASK 0x02 +#define GPIO3_STS_SHIFT 1 +#define GPIO3_SET_MASK 0x01 +#define GPIO3_SET_SHIFT 0 + +/* GPIO 4 Register Definitions */ +#define GPIO4_SLEEP_MASK 0x80 +#define GPIO4_SLEEP_SHIFT 7 +#define GPIO4_SEL_MASK 0x40 +#define GPIO4_SEL_SHIFT 6 +#define GPIO4_ODEN_MASK 0x20 +#define GPIO4_ODEN_SHIFT 5 +#define GPIO4_DEB_MASK 0x10 +#define GPIO4_DEB_SHIFT 4 +#define GPIO4_PDEN_MASK 0x08 +#define GPIO4_PDEN_SHIFT 3 +#define GPIO4_CFG_MASK 0x04 +#define GPIO4_CFG_SHIFT 2 +#define GPIO4_STS_MASK 0x02 +#define GPIO4_STS_SHIFT 1 +#define GPIO4_SET_MASK 0x01 +#define GPIO4_SET_SHIFT 0 + +/* Register THERM (0x80) register.RegisterDescription */ +#define THERM_THERM_HD_MASK 0x20 +#define THERM_THERM_HD_SHIFT 5 +#define THERM_THERM_TS_MASK 0x10 +#define THERM_THERM_TS_SHIFT 4 +#define THERM_THERM_HDSEL_MASK 0x0C +#define THERM_THERM_HDSEL_SHIFT 2 +#define THERM_RSVD1_MASK 0x02 +#define THERM_RSVD1_SHIFT 1 +#define THERM_THERM_STATE_MASK 0x01 +#define THERM_THERM_STATE_SHIFT 0 + +/* Register DCDCCTRL1 register.RegisterDescription */ +#define DCDCCTRL_VCON_ENABLE_MASK 0x80 +#define DCDCCTRL_VCON_ENABLE_SHIFT 7 +#define DCDCCTRL_VCON_RANGE1_MASK 0x40 +#define DCDCCTRL_VCON_RANGE1_SHIFT 6 +#define DCDCCTRL_VCON_RANGE0_MASK 0x20 +#define DCDCCTRL_VCON_RANGE0_SHIFT 5 +#define DCDCCTRL_TSTEP2_MASK 0x10 +#define DCDCCTRL_TSTEP2_SHIFT 4 +#define DCDCCTRL_TSTEP1_MASK 0x08 +#define DCDCCTRL_TSTEP1_SHIFT 3 +#define DCDCCTRL_TSTEP0_MASK 0x04 +#define DCDCCTRL_TSTEP0_SHIFT 2 +#define DCDCCTRL_DCDC1_MODE_MASK 0x02 +#define DCDCCTRL_DCDC1_MODE_SHIFT 1 + +/* Register DCDCCTRL2 and DCDCCTRL3 register.RegisterDescription */ +#define DCDCCTRL_TSTEP2_MASK 0x10 +#define DCDCCTRL_TSTEP2_SHIFT 4 +#define DCDCCTRL_TSTEP1_MASK 0x08 +#define DCDCCTRL_TSTEP1_SHIFT 3 +#define DCDCCTRL_TSTEP0_MASK 0x04 +#define DCDCCTRL_TSTEP0_SHIFT 2 +#define DCDCCTRL_DCDC_MODE_MASK 0x02 +#define DCDCCTRL_DCDC_MODE_SHIFT 1 +#define DCDCCTRL_RSVD0_MASK 0x01 +#define DCDCCTRL_RSVD0_SHIFT 0 + +/* Register DCDCCTRL4 register.RegisterDescription */ +#define DCDCCTRL_RAMP_TIME_MASK 0x01 +#define DCDCCTRL_RAMP_TIME_SHIFT 0 + +/* Register DCDCx_AVS */ +#define DCDC_AVS_ENABLE_MASK 0x80 +#define DCDC_AVS_ENABLE_SHIFT 7 +#define DCDC_AVS_ECO_MASK 0x40 +#define DCDC_AVS_ECO_SHIFT 6 + +/* Register DCDCx_LIMIT */ +#define DCDC_LIMIT_RANGE_MASK 0xC0 +#define DCDC_LIMIT_RANGE_SHIFT 6 +#define DCDC_LIMIT_MAX_SEL_MASK 0x3F +#define DCDC_LIMIT_MAX_SEL_SHIFT 0 + +/** + * struct tps65912_board + * Board platform dat may be used to initialize regulators. + */ +struct tps65912_board { + int is_dcdc1_avs; + int is_dcdc2_avs; + int is_dcdc3_avs; + int is_dcdc4_avs; + struct regulator_init_data *tps65912_pmic_init_data; +}; + +/** + * struct tps65912 - tps65912 sub-driver chip access routines + */ + +struct tps65912 { + struct device *dev; + /* for read/write acces */ + struct mutex io_mutex; + + /* For device IO interfaces: I2C or SPI */ + void *control_data; + + int (*read)(struct tps65912 *tps65912, u8 reg, int size, void *dest); + int (*write)(struct tps65912 *tps65912, u8 reg, int size, void *src); + + /* Client devices */ + struct tps65912_pmic *pmic; + + /* GPIO Handling */ + struct gpio_chip gpio; + + /* IRQ Handling */ + struct mutex irq_lock; + int chip_irq; + int irq_base; + int irq_num; + u32 irq_mask; +}; + +struct tps65912_platform_data { + int irq_base; +}; + +unsigned int tps_chip(void); + +int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask); +int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask); +int tps65912_reg_read(struct tps65912 *tps65912, u8 reg); +int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val); +int tps65912_device_init(struct tps65912 *tps65912); +void tps65912_device_exit(struct tps65912 *tps65912); + +#endif /* __LINUX_MFD_TPS65912_H */ -- cgit v1.2.3 From d49a0f3f14a763242b71244019d7881ee06e0658 Mon Sep 17 00:00:00 2001 From: Margarita Olaya Date: Thu, 9 Jun 2011 14:50:13 -0500 Subject: tps65912: irq: add interrupt controller This module controls the interrupt handling for the tps65912. The interrupt sources can be the following: - GPIO - PWRON signal - PWRHOLD signal - Temperature detection Signed-off-by: Margarita Olaya Cabrera Acked-by: Samuel Ortiz Acked-by: Liam Girdwood Signed-off-by: Samuel Ortiz --- drivers/mfd/Makefile | 2 +- drivers/mfd/tps65912-core.c | 13 +++ drivers/mfd/tps65912-irq.c | 224 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/tps65912.h | 5 + 4 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/tps65912-irq.c (limited to 'include') diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c9925e5dad99..1d593b9085b7 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -36,7 +36,7 @@ obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o -tps65912-objs := tps65912-core.o +tps65912-objs := tps65912-core.o tps65912-irq.o obj-$(CONFIG_MFD_TPS65912) += tps65912.o obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c index 10baf7655542..955bc00e4b20 100644 --- a/drivers/mfd/tps65912-core.c +++ b/drivers/mfd/tps65912-core.c @@ -124,8 +124,16 @@ EXPORT_SYMBOL_GPL(tps65912_reg_write); int tps65912_device_init(struct tps65912 *tps65912) { struct tps65912_board *pmic_plat_data = tps65912->dev->platform_data; + struct tps65912_platform_data *init_data; int ret, dcdc_avs, value; + init_data = kzalloc(sizeof(struct tps65912_platform_data), GFP_KERNEL); + if (init_data == NULL) + return -ENOMEM; + + init_data->irq = pmic_plat_data->irq; + init_data->irq_base = pmic_plat_data->irq; + mutex_init(&tps65912->io_mutex); dev_set_drvdata(tps65912->dev, tps65912); @@ -145,9 +153,14 @@ int tps65912_device_init(struct tps65912 *tps65912) if (ret < 0) goto err; + ret = tps65912_irq_init(tps65912, init_data->irq, init_data); + if (ret < 0) + goto err; + return ret; err: + kfree(init_data); mfd_remove_devices(tps65912->dev); kfree(tps65912); return ret; diff --git a/drivers/mfd/tps65912-irq.c b/drivers/mfd/tps65912-irq.c new file mode 100644 index 000000000000..d360a83a2738 --- /dev/null +++ b/drivers/mfd/tps65912-irq.c @@ -0,0 +1,224 @@ +/* + * tps65912-irq.c -- TI TPS6591x + * + * Copyright 2011 Texas Instruments Inc. + * + * Author: Margarita Olaya + * + * 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 driver is based on wm8350 implementation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline int irq_to_tps65912_irq(struct tps65912 *tps65912, + int irq) +{ + return irq - tps65912->irq_base; +} + +/* + * This is a threaded IRQ handler so can access I2C/SPI. Since the + * IRQ handler explicitly clears the IRQ it handles the IRQ line + * will be reasserted and the physical IRQ will be handled again if + * another interrupt is asserted while we run - in the normal course + * of events this is a rare occurrence so we save I2C/SPI reads. We're + * also assuming that it's rare to get lots of interrupts firing + * simultaneously so try to minimise I/O. + */ +static irqreturn_t tps65912_irq(int irq, void *irq_data) +{ + struct tps65912 *tps65912 = irq_data; + u32 irq_sts; + u32 irq_mask; + u8 reg; + int i; + + + tps65912->read(tps65912, TPS65912_INT_STS, 1, ®); + irq_sts = reg; + tps65912->read(tps65912, TPS65912_INT_STS2, 1, ®); + irq_sts |= reg << 8; + tps65912->read(tps65912, TPS65912_INT_STS3, 1, ®); + irq_sts |= reg << 16; + tps65912->read(tps65912, TPS65912_INT_STS4, 1, ®); + irq_sts |= reg << 24; + + tps65912->read(tps65912, TPS65912_INT_MSK, 1, ®); + irq_mask = reg; + tps65912->read(tps65912, TPS65912_INT_MSK2, 1, ®); + irq_mask |= reg << 8; + tps65912->read(tps65912, TPS65912_INT_MSK3, 1, ®); + irq_mask |= reg << 16; + tps65912->read(tps65912, TPS65912_INT_MSK4, 1, ®); + irq_mask |= reg << 24; + + irq_sts &= ~irq_mask; + if (!irq_sts) + return IRQ_NONE; + + for (i = 0; i < tps65912->irq_num; i++) { + if (!(irq_sts & (1 << i))) + continue; + + handle_nested_irq(tps65912->irq_base + i); + } + + /* Write the STS register back to clear IRQs we handled */ + reg = irq_sts & 0xFF; + irq_sts >>= 8; + if (reg) + tps65912->write(tps65912, TPS65912_INT_STS, 1, ®); + reg = irq_sts & 0xFF; + irq_sts >>= 8; + if (reg) + tps65912->write(tps65912, TPS65912_INT_STS2, 1, ®); + reg = irq_sts & 0xFF; + irq_sts >>= 8; + if (reg) + tps65912->write(tps65912, TPS65912_INT_STS3, 1, ®); + reg = irq_sts & 0xFF; + if (reg) + tps65912->write(tps65912, TPS65912_INT_STS4, 1, ®); + + return IRQ_HANDLED; +} + +static void tps65912_irq_lock(struct irq_data *data) +{ + struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data); + + mutex_lock(&tps65912->irq_lock); +} + +static void tps65912_irq_sync_unlock(struct irq_data *data) +{ + struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data); + u32 reg_mask; + u8 reg; + + tps65912->read(tps65912, TPS65912_INT_MSK, 1, ®); + reg_mask = reg; + tps65912->read(tps65912, TPS65912_INT_MSK2, 1, ®); + reg_mask |= reg << 8; + tps65912->read(tps65912, TPS65912_INT_MSK3, 1, ®); + reg_mask |= reg << 16; + tps65912->read(tps65912, TPS65912_INT_MSK4, 1, ®); + reg_mask |= reg << 24; + + if (tps65912->irq_mask != reg_mask) { + reg = tps65912->irq_mask & 0xFF; + tps65912->write(tps65912, TPS65912_INT_MSK, 1, ®); + reg = tps65912->irq_mask >> 8 & 0xFF; + tps65912->write(tps65912, TPS65912_INT_MSK2, 1, ®); + reg = tps65912->irq_mask >> 16 & 0xFF; + tps65912->write(tps65912, TPS65912_INT_MSK3, 1, ®); + reg = tps65912->irq_mask >> 24 & 0xFF; + tps65912->write(tps65912, TPS65912_INT_MSK4, 1, ®); + } + + mutex_unlock(&tps65912->irq_lock); +} + +static void tps65912_irq_enable(struct irq_data *data) +{ + struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data); + + tps65912->irq_mask &= ~(1 << irq_to_tps65912_irq(tps65912, data->irq)); +} + +static void tps65912_irq_disable(struct irq_data *data) +{ + struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data); + + tps65912->irq_mask |= (1 << irq_to_tps65912_irq(tps65912, data->irq)); +} + +static struct irq_chip tps65912_irq_chip = { + .name = "tps65912", + .irq_bus_lock = tps65912_irq_lock, + .irq_bus_sync_unlock = tps65912_irq_sync_unlock, + .irq_disable = tps65912_irq_disable, + .irq_enable = tps65912_irq_enable, +}; + +int tps65912_irq_init(struct tps65912 *tps65912, int irq, + struct tps65912_platform_data *pdata) +{ + int ret, cur_irq; + int flags = IRQF_ONESHOT; + u8 reg; + + if (!irq) { + dev_warn(tps65912->dev, "No interrupt support, no core IRQ\n"); + return 0; + } + + if (!pdata || !pdata->irq_base) { + dev_warn(tps65912->dev, "No interrupt support, no IRQ base\n"); + return 0; + } + + /* Clear unattended interrupts */ + tps65912->read(tps65912, TPS65912_INT_STS, 1, ®); + tps65912->write(tps65912, TPS65912_INT_STS, 1, ®); + tps65912->read(tps65912, TPS65912_INT_STS2, 1, ®); + tps65912->write(tps65912, TPS65912_INT_STS2, 1, ®); + tps65912->read(tps65912, TPS65912_INT_STS3, 1, ®); + tps65912->write(tps65912, TPS65912_INT_STS3, 1, ®); + tps65912->read(tps65912, TPS65912_INT_STS4, 1, ®); + tps65912->write(tps65912, TPS65912_INT_STS4, 1, ®); + + /* Mask top level interrupts */ + tps65912->irq_mask = 0xFFFFFFFF; + + mutex_init(&tps65912->irq_lock); + tps65912->chip_irq = irq; + tps65912->irq_base = pdata->irq_base; + + tps65912->irq_num = TPS65912_NUM_IRQ; + + /* Register with genirq */ + for (cur_irq = tps65912->irq_base; + cur_irq < tps65912->irq_num + tps65912->irq_base; + cur_irq++) { + irq_set_chip_data(cur_irq, tps65912); + irq_set_chip_and_handler(cur_irq, &tps65912_irq_chip, + handle_edge_irq); + irq_set_nested_thread(cur_irq, 1); + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + irq_set_noprobe(cur_irq); +#endif + } + + ret = request_threaded_irq(irq, NULL, tps65912_irq, flags, + "tps65912", tps65912); + + irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); + if (ret != 0) + dev_err(tps65912->dev, "Failed to request IRQ: %d\n", ret); + + return ret; +} + +int tps65912_irq_exit(struct tps65912 *tps65912) +{ + free_irq(tps65912->chip_irq, tps65912); + return 0; +} diff --git a/include/linux/mfd/tps65912.h b/include/linux/mfd/tps65912.h index 48d3b53316a9..be60fb23b583 100644 --- a/include/linux/mfd/tps65912.h +++ b/include/linux/mfd/tps65912.h @@ -273,6 +273,8 @@ struct tps65912_board { int is_dcdc2_avs; int is_dcdc3_avs; int is_dcdc4_avs; + int irq; + int irq_base; struct regulator_init_data *tps65912_pmic_init_data; }; @@ -306,6 +308,7 @@ struct tps65912 { }; struct tps65912_platform_data { + int irq; int irq_base; }; @@ -317,5 +320,7 @@ int tps65912_reg_read(struct tps65912 *tps65912, u8 reg); int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val); int tps65912_device_init(struct tps65912 *tps65912); void tps65912_device_exit(struct tps65912 *tps65912); +int tps65912_irq_init(struct tps65912 *tps65912, int irq, + struct tps65912_platform_data *pdata); #endif /* __LINUX_MFD_TPS65912_H */ -- cgit v1.2.3 From 668a6cc710ee054af2b059d27bbec746ead0fbca Mon Sep 17 00:00:00 2001 From: Margarita Olaya Date: Thu, 9 Jun 2011 14:50:19 -0500 Subject: tps65912: gpio: add gpio driver TPS65912 has five GPIOs that can be configured for different purposes. Signed-off-by: Margarita Olaya Cabrera Acked-by: Grant Likely Acked-by: Liam Girdwood Signed-off-by: Samuel Ortiz --- drivers/gpio/Kconfig | 6 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-tps65912.c | 156 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/tps65912.h | 1 + 4 files changed, 164 insertions(+) create mode 100644 drivers/gpio/gpio-tps65912.c (limited to 'include') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 363498697c2c..6778f56a4c64 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -280,6 +280,12 @@ config GPIO_TC3589X This enables support for the GPIOs found on the TC3589X I/O Expander. +config GPIO_TPS65912 + tristate "TI TPS65912 GPIO" + depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI) + help + This driver supports TPS65912 gpio chip + config GPIO_TWL4030 tristate "TWL4030, TWL5030, and TPS659x0 GPIOs" depends on TWL4030_CORE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 720711251391..4b81d4e1e709 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o +obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o obj-$(CONFIG_MACH_U300) += gpio-u300.o obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c new file mode 100644 index 000000000000..79e66c002350 --- /dev/null +++ b/drivers/gpio/gpio-tps65912.c @@ -0,0 +1,156 @@ +/* + * Copyright 2011 Texas Instruments Inc. + * + * Author: Margarita Olaya + * + * 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 driver is based on wm8350 implementation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tps65912_gpio_data { + struct tps65912 *tps65912; + struct gpio_chip gpio_chip; +}; + +static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio); + int val; + + val = tps65912_reg_read(tps65912, TPS65912_GPIO1 + offset); + + if (val & GPIO_STS_MASK) + return 1; + + return 0; +} + +static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio); + + if (value) + tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset, + GPIO_SET_MASK); + else + tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset, + GPIO_SET_MASK); +} + +static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio); + + /* Set the initial value */ + tps65912_gpio_set(gc, offset, value); + + return tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset, + GPIO_CFG_MASK); +} + +static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset) +{ + struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio); + + return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset, + GPIO_CFG_MASK); + +} + +static struct gpio_chip template_chip = { + .label = "tps65912", + .owner = THIS_MODULE, + .direction_input = tps65912_gpio_input, + .direction_output = tps65912_gpio_output, + .get = tps65912_gpio_get, + .set = tps65912_gpio_set, + .can_sleep = 1, + .ngpio = 5, + .base = -1, +}; + +static int __devinit tps65912_gpio_probe(struct platform_device *pdev) +{ + struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent); + struct tps65912_board *pdata = tps65912->dev->platform_data; + struct tps65912_gpio_data *tps65912_gpio; + int ret; + + tps65912_gpio = kzalloc(sizeof(*tps65912_gpio), GFP_KERNEL); + if (tps65912_gpio == NULL) + return -ENOMEM; + + tps65912_gpio->tps65912 = tps65912; + tps65912_gpio->gpio_chip = template_chip; + tps65912_gpio->gpio_chip.dev = &pdev->dev; + if (pdata && pdata->gpio_base) + tps65912_gpio->gpio_chip.base = pdata->gpio_base; + + ret = gpiochip_add(&tps65912_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret); + goto err; + } + + platform_set_drvdata(pdev, tps65912_gpio); + + return ret; + +err: + kfree(tps65912_gpio); + return ret; +} + +static int __devexit tps65912_gpio_remove(struct platform_device *pdev) +{ + struct tps65912_gpio_data *tps65912_gpio = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&tps65912_gpio->gpio_chip); + if (ret == 0) + kfree(tps65912_gpio); + + return ret; +} + +static struct platform_driver tps65912_gpio_driver = { + .driver = { + .name = "tps65912-gpio", + .owner = THIS_MODULE, + }, + .probe = tps65912_gpio_probe, + .remove = __devexit_p(tps65912_gpio_remove), +}; + +static int __init tps65912_gpio_init(void) +{ + return platform_driver_register(&tps65912_gpio_driver); +} +subsys_initcall(tps65912_gpio_init); + +static void __exit tps65912_gpio_exit(void) +{ + platform_driver_unregister(&tps65912_gpio_driver); +} +module_exit(tps65912_gpio_exit); + +MODULE_AUTHOR("Margarita Olaya Cabrera "); +MODULE_DESCRIPTION("GPIO interface for TPS65912 PMICs"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps65912-gpio"); diff --git a/include/linux/mfd/tps65912.h b/include/linux/mfd/tps65912.h index be60fb23b583..aaceab402ec5 100644 --- a/include/linux/mfd/tps65912.h +++ b/include/linux/mfd/tps65912.h @@ -275,6 +275,7 @@ struct tps65912_board { int is_dcdc4_avs; int irq; int irq_base; + int gpio_base; struct regulator_init_data *tps65912_pmic_init_data; }; -- cgit v1.2.3 From 0a1b089738cf2165ee8678e2483fce3c40389d4a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 9 Jun 2011 23:57:57 +0200 Subject: mfd: Clean-up ab8500 register file This adds a previously undefined test register and removed a number of double-defined accessory detect registers (they are already defined higher up in the file. Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- include/linux/mfd/ab8500.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/ab8500.h b/include/linux/mfd/ab8500.h index b31843075198..838c6b487cc5 100644 --- a/include/linux/mfd/ab8500.h +++ b/include/linux/mfd/ab8500.h @@ -28,6 +28,7 @@ #define AB8500_INTERRUPT 0xE #define AB8500_RTC 0xF #define AB8500_MISC 0x10 +#define AB8500_DEVELOPMENT 0x11 #define AB8500_DEBUG 0x12 #define AB8500_PROD_TEST 0x13 #define AB8500_OTP_EMUL 0x15 @@ -74,13 +75,6 @@ #define AB8500_INT_ACC_DETECT_21DB_F 37 #define AB8500_INT_ACC_DETECT_21DB_R 38 #define AB8500_INT_GP_SW_ADC_CONV_END 39 -#define AB8500_INT_ACC_DETECT_1DB_F 33 -#define AB8500_INT_ACC_DETECT_1DB_R 34 -#define AB8500_INT_ACC_DETECT_22DB_F 35 -#define AB8500_INT_ACC_DETECT_22DB_R 36 -#define AB8500_INT_ACC_DETECT_21DB_F 37 -#define AB8500_INT_ACC_DETECT_21DB_R 38 -#define AB8500_INT_GP_SW_ADC_CONV_END 39 #define AB8500_INT_GPIO6R 40 #define AB8500_INT_GPIO7R 41 #define AB8500_INT_GPIO8R 42 -- cgit v1.2.3 From ec2328c30bf09fe31e77889090eeb1a965325f53 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 20 Jun 2011 11:47:55 +0100 Subject: mfd: Implement tps65910 IRQ cleanup The tps65910_irq_exit() cleanup function was generating a warning from sparse due to the lack of a prototype. This wasn't causing GCC warnings as the driver wasn't cleaning up its IRQs on exit at all so there was no use of an unprototyped function. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65910.c | 1 + include/linux/mfd/tps65910.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index 2229e66d80db..837f0e6fbf0e 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -187,6 +187,7 @@ static int tps65910_i2c_remove(struct i2c_client *i2c) struct tps65910 *tps65910 = i2c_get_clientdata(i2c); mfd_remove_devices(tps65910->dev); + tps65910_irq_exit(tps65910); kfree(tps65910); return 0; diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index 73572c65d04f..82b4c8801a4f 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -791,6 +791,7 @@ int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask); void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base); int tps65910_irq_init(struct tps65910 *tps65910, int irq, struct tps65910_platform_data *pdata); +int tps65910_irq_exit(struct tps65910 *tps65910); static inline int tps65910_chip_id(struct tps65910 *tps65910) { -- cgit v1.2.3 From c7e1da477293b4b5e0bef3639b3734e28d5d55f7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 21 Jun 2011 11:12:23 +0100 Subject: mfd: Add WM831x clock control register definitions Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- include/linux/mfd/wm831x/core.h | 101 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index 9fa14cdc1590..8dda8ded5cda 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -235,6 +235,107 @@ #define WM831X_ON_PIN_TO_SHIFT 0 /* ON_PIN_TO - [1:0] */ #define WM831X_ON_PIN_TO_WIDTH 2 /* ON_PIN_TO - [1:0] */ +/* + * R16528 (0x4090) - Clock Control 1 + */ +#define WM831X_CLKOUT_ENA 0x8000 /* CLKOUT_ENA */ +#define WM831X_CLKOUT_ENA_MASK 0x8000 /* CLKOUT_ENA */ +#define WM831X_CLKOUT_ENA_SHIFT 15 /* CLKOUT_ENA */ +#define WM831X_CLKOUT_ENA_WIDTH 1 /* CLKOUT_ENA */ +#define WM831X_CLKOUT_OD 0x2000 /* CLKOUT_OD */ +#define WM831X_CLKOUT_OD_MASK 0x2000 /* CLKOUT_OD */ +#define WM831X_CLKOUT_OD_SHIFT 13 /* CLKOUT_OD */ +#define WM831X_CLKOUT_OD_WIDTH 1 /* CLKOUT_OD */ +#define WM831X_CLKOUT_SLOT_MASK 0x0700 /* CLKOUT_SLOT - [10:8] */ +#define WM831X_CLKOUT_SLOT_SHIFT 8 /* CLKOUT_SLOT - [10:8] */ +#define WM831X_CLKOUT_SLOT_WIDTH 3 /* CLKOUT_SLOT - [10:8] */ +#define WM831X_CLKOUT_SLPSLOT_MASK 0x0070 /* CLKOUT_SLPSLOT - [6:4] */ +#define WM831X_CLKOUT_SLPSLOT_SHIFT 4 /* CLKOUT_SLPSLOT - [6:4] */ +#define WM831X_CLKOUT_SLPSLOT_WIDTH 3 /* CLKOUT_SLPSLOT - [6:4] */ +#define WM831X_CLKOUT_SRC 0x0001 /* CLKOUT_SRC */ +#define WM831X_CLKOUT_SRC_MASK 0x0001 /* CLKOUT_SRC */ +#define WM831X_CLKOUT_SRC_SHIFT 0 /* CLKOUT_SRC */ +#define WM831X_CLKOUT_SRC_WIDTH 1 /* CLKOUT_SRC */ + +/* + * R16529 (0x4091) - Clock Control 2 + */ +#define WM831X_XTAL_INH 0x8000 /* XTAL_INH */ +#define WM831X_XTAL_INH_MASK 0x8000 /* XTAL_INH */ +#define WM831X_XTAL_INH_SHIFT 15 /* XTAL_INH */ +#define WM831X_XTAL_INH_WIDTH 1 /* XTAL_INH */ +#define WM831X_XTAL_ENA 0x2000 /* XTAL_ENA */ +#define WM831X_XTAL_ENA_MASK 0x2000 /* XTAL_ENA */ +#define WM831X_XTAL_ENA_SHIFT 13 /* XTAL_ENA */ +#define WM831X_XTAL_ENA_WIDTH 1 /* XTAL_ENA */ +#define WM831X_XTAL_BKUPENA 0x1000 /* XTAL_BKUPENA */ +#define WM831X_XTAL_BKUPENA_MASK 0x1000 /* XTAL_BKUPENA */ +#define WM831X_XTAL_BKUPENA_SHIFT 12 /* XTAL_BKUPENA */ +#define WM831X_XTAL_BKUPENA_WIDTH 1 /* XTAL_BKUPENA */ +#define WM831X_FLL_AUTO 0x0080 /* FLL_AUTO */ +#define WM831X_FLL_AUTO_MASK 0x0080 /* FLL_AUTO */ +#define WM831X_FLL_AUTO_SHIFT 7 /* FLL_AUTO */ +#define WM831X_FLL_AUTO_WIDTH 1 /* FLL_AUTO */ +#define WM831X_FLL_AUTO_FREQ_MASK 0x0007 /* FLL_AUTO_FREQ - [2:0] */ +#define WM831X_FLL_AUTO_FREQ_SHIFT 0 /* FLL_AUTO_FREQ - [2:0] */ +#define WM831X_FLL_AUTO_FREQ_WIDTH 3 /* FLL_AUTO_FREQ - [2:0] */ + +/* + * R16530 (0x4092) - FLL Control 1 + */ +#define WM831X_FLL_FRAC 0x0004 /* FLL_FRAC */ +#define WM831X_FLL_FRAC_MASK 0x0004 /* FLL_FRAC */ +#define WM831X_FLL_FRAC_SHIFT 2 /* FLL_FRAC */ +#define WM831X_FLL_FRAC_WIDTH 1 /* FLL_FRAC */ +#define WM831X_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */ +#define WM831X_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */ +#define WM831X_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */ +#define WM831X_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */ +#define WM831X_FLL_ENA 0x0001 /* FLL_ENA */ +#define WM831X_FLL_ENA_MASK 0x0001 /* FLL_ENA */ +#define WM831X_FLL_ENA_SHIFT 0 /* FLL_ENA */ +#define WM831X_FLL_ENA_WIDTH 1 /* FLL_ENA */ + +/* + * R16531 (0x4093) - FLL Control 2 + */ +#define WM831X_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */ +#define WM831X_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */ +#define WM831X_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */ +#define WM831X_FLL_CTRL_RATE_MASK 0x0070 /* FLL_CTRL_RATE - [6:4] */ +#define WM831X_FLL_CTRL_RATE_SHIFT 4 /* FLL_CTRL_RATE - [6:4] */ +#define WM831X_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [6:4] */ +#define WM831X_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */ +#define WM831X_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */ +#define WM831X_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */ + +/* + * R16532 (0x4094) - FLL Control 3 + */ +#define WM831X_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */ +#define WM831X_FLL_K_SHIFT 0 /* FLL_K - [15:0] */ +#define WM831X_FLL_K_WIDTH 16 /* FLL_K - [15:0] */ + +/* + * R16533 (0x4095) - FLL Control 4 + */ +#define WM831X_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */ +#define WM831X_FLL_N_SHIFT 5 /* FLL_N - [14:5] */ +#define WM831X_FLL_N_WIDTH 10 /* FLL_N - [14:5] */ +#define WM831X_FLL_GAIN_MASK 0x000F /* FLL_GAIN - [3:0] */ +#define WM831X_FLL_GAIN_SHIFT 0 /* FLL_GAIN - [3:0] */ +#define WM831X_FLL_GAIN_WIDTH 4 /* FLL_GAIN - [3:0] */ + +/* + * R16534 (0x4096) - FLL Control 5 + */ +#define WM831X_FLL_CLK_REF_DIV_MASK 0x0018 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM831X_FLL_CLK_REF_DIV_SHIFT 3 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM831X_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM831X_FLL_CLK_SRC_MASK 0x0003 /* FLL_CLK_SRC - [1:0] */ +#define WM831X_FLL_CLK_SRC_SHIFT 0 /* FLL_CLK_SRC - [1:0] */ +#define WM831X_FLL_CLK_SRC_WIDTH 2 /* FLL_CLK_SRC - [1:0] */ + struct regulator_dev; #define WM831X_NUM_IRQ_REGS 5 -- cgit v1.2.3 From 4dcaa6b6df354fa44b3072bed3cb13aad7e5fbd4 Mon Sep 17 00:00:00 2001 From: Om Prakash Date: Mon, 27 Jun 2011 09:54:22 +0200 Subject: mfd: Fix missing stmpe kerneldoc Generating kerneldoc for STMPE result in warnings, so fix this by adding missing documentation. Signed-off-by: Om Prakash Reviewed-by: Rabin Vincent Reviewed-by: Jonas Aberg Reviewed-by: Srinidhi Kasagar Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/stmpe.c | 2 +- drivers/mfd/stmpe.h | 1 + include/linux/mfd/stmpe.h | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 7ab7746631d4..2963689cf45c 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -228,7 +228,7 @@ int stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length, EXPORT_SYMBOL_GPL(stmpe_block_write); /** - * stmpe_set_altfunc: set the alternate function for STMPE pins + * stmpe_set_altfunc()- set the alternate function for STMPE pins * @stmpe: Device to configure * @pins: Bitmask of pins to affect * @block: block to enable alternate functions for diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h index 0dbdc4e8cd77..e4ee38956583 100644 --- a/drivers/mfd/stmpe.h +++ b/drivers/mfd/stmpe.h @@ -42,6 +42,7 @@ struct stmpe_variant_block { * @id_mask: bits valid in CHIPID register for comparison with id_val * @num_gpios: number of GPIOS * @af_bits: number of bits used to specify the alternate function + * @regs: variant specific registers. * @blocks: list of blocks present on this device * @num_blocks: number of blocks present on this device * @num_irqs: number of internal IRQs available on this device diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index e762c270d8d4..be1af7c42e57 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -57,6 +57,7 @@ struct stmpe_variant_info; * @irq_lock: IRQ bus lock * @dev: device, mostly for dev_dbg() * @i2c: i2c client + * @partnum: part number * @variant: the detected STMPE model number * @regs: list of addresses of registers which are at different addresses on * different variants. Indexed by one of STMPE_IDX_*. @@ -121,6 +122,8 @@ struct stmpe_keypad_platform_data { * @norequest_mask: bitmask specifying which GPIOs should _not_ be * requestable due to different usage (e.g. touch, keypad) * STMPE_GPIO_NOREQ_* macros can be used here. + * @setup: board specific setup callback. + * @remove: board specific remove callback */ struct stmpe_gpio_platform_data { int gpio_base; -- cgit v1.2.3 From 09d6292befba8c6319d9471803149573ea6ed170 Mon Sep 17 00:00:00 2001 From: Jin Park Date: Mon, 4 Jul 2011 19:48:12 +0200 Subject: mfd: Add AAT2870 mfd driver Add mfd core driver for AnalogicTech AAT2870. The AAT2870 is communication through I2C and contains backlight and regulator components. Signed-off-by: Jin Park Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/aat2870-core.c | 535 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/aat2870.h | 181 +++++++++++++++ 4 files changed, 727 insertions(+) create mode 100644 drivers/mfd/aat2870-core.c create mode 100644 include/linux/mfd/aat2870.h (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c26136443c05..21574bdf485f 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -760,6 +760,16 @@ config MFD_PM8XXX_IRQ config TPS65911_COMPARATOR tristate +config MFD_AAT2870_CORE + bool "Support for the AnalogicTech AAT2870" + select MFD_CORE + depends on I2C=y && GPIOLIB + help + If you say yes here you get support for the AAT2870. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. + endif # MFD_SUPPORT menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index ca1867db3db8..c58020303d18 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -101,3 +101,4 @@ obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o +obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c new file mode 100644 index 000000000000..345dc658ef06 --- /dev/null +++ b/drivers/mfd/aat2870-core.c @@ -0,0 +1,535 @@ +/* + * linux/drivers/mfd/aat2870-core.c + * + * Copyright (c) 2011, NVIDIA Corporation. + * Author: Jin Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct aat2870_register aat2870_regs[AAT2870_REG_NUM] = { + /* readable, writeable, value */ + { 0, 1, 0x00 }, /* 0x00 AAT2870_BL_CH_EN */ + { 0, 1, 0x16 }, /* 0x01 AAT2870_BLM */ + { 0, 1, 0x16 }, /* 0x02 AAT2870_BLS */ + { 0, 1, 0x56 }, /* 0x03 AAT2870_BL1 */ + { 0, 1, 0x56 }, /* 0x04 AAT2870_BL2 */ + { 0, 1, 0x56 }, /* 0x05 AAT2870_BL3 */ + { 0, 1, 0x56 }, /* 0x06 AAT2870_BL4 */ + { 0, 1, 0x56 }, /* 0x07 AAT2870_BL5 */ + { 0, 1, 0x56 }, /* 0x08 AAT2870_BL6 */ + { 0, 1, 0x56 }, /* 0x09 AAT2870_BL7 */ + { 0, 1, 0x56 }, /* 0x0A AAT2870_BL8 */ + { 0, 1, 0x00 }, /* 0x0B AAT2870_FLR */ + { 0, 1, 0x03 }, /* 0x0C AAT2870_FM */ + { 0, 1, 0x03 }, /* 0x0D AAT2870_FS */ + { 0, 1, 0x10 }, /* 0x0E AAT2870_ALS_CFG0 */ + { 0, 1, 0x06 }, /* 0x0F AAT2870_ALS_CFG1 */ + { 0, 1, 0x00 }, /* 0x10 AAT2870_ALS_CFG2 */ + { 1, 0, 0x00 }, /* 0x11 AAT2870_AMB */ + { 0, 1, 0x00 }, /* 0x12 AAT2870_ALS0 */ + { 0, 1, 0x00 }, /* 0x13 AAT2870_ALS1 */ + { 0, 1, 0x00 }, /* 0x14 AAT2870_ALS2 */ + { 0, 1, 0x00 }, /* 0x15 AAT2870_ALS3 */ + { 0, 1, 0x00 }, /* 0x16 AAT2870_ALS4 */ + { 0, 1, 0x00 }, /* 0x17 AAT2870_ALS5 */ + { 0, 1, 0x00 }, /* 0x18 AAT2870_ALS6 */ + { 0, 1, 0x00 }, /* 0x19 AAT2870_ALS7 */ + { 0, 1, 0x00 }, /* 0x1A AAT2870_ALS8 */ + { 0, 1, 0x00 }, /* 0x1B AAT2870_ALS9 */ + { 0, 1, 0x00 }, /* 0x1C AAT2870_ALSA */ + { 0, 1, 0x00 }, /* 0x1D AAT2870_ALSB */ + { 0, 1, 0x00 }, /* 0x1E AAT2870_ALSC */ + { 0, 1, 0x00 }, /* 0x1F AAT2870_ALSD */ + { 0, 1, 0x00 }, /* 0x20 AAT2870_ALSE */ + { 0, 1, 0x00 }, /* 0x21 AAT2870_ALSF */ + { 0, 1, 0x00 }, /* 0x22 AAT2870_SUB_SET */ + { 0, 1, 0x00 }, /* 0x23 AAT2870_SUB_CTRL */ + { 0, 1, 0x00 }, /* 0x24 AAT2870_LDO_AB */ + { 0, 1, 0x00 }, /* 0x25 AAT2870_LDO_CD */ + { 0, 1, 0x00 }, /* 0x26 AAT2870_LDO_EN */ +}; + +static struct mfd_cell aat2870_devs[] = { + { + .name = "aat2870-backlight", + .id = AAT2870_ID_BL, + .pdata_size = sizeof(struct aat2870_bl_platform_data), + }, + { + .name = "aat2870-regulator", + .id = AAT2870_ID_LDOA, + .pdata_size = sizeof(struct regulator_init_data), + }, + { + .name = "aat2870-regulator", + .id = AAT2870_ID_LDOB, + .pdata_size = sizeof(struct regulator_init_data), + }, + { + .name = "aat2870-regulator", + .id = AAT2870_ID_LDOC, + .pdata_size = sizeof(struct regulator_init_data), + }, + { + .name = "aat2870-regulator", + .id = AAT2870_ID_LDOD, + .pdata_size = sizeof(struct regulator_init_data), + }, +}; + +static int __aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val) +{ + int ret; + + if (addr >= AAT2870_REG_NUM) { + dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr); + return -EINVAL; + } + + if (!aat2870->reg_cache[addr].readable) { + *val = aat2870->reg_cache[addr].value; + goto out; + } + + ret = i2c_master_send(aat2870->client, &addr, 1); + if (ret < 0) + return ret; + if (ret != 1) + return -EIO; + + ret = i2c_master_recv(aat2870->client, val, 1); + if (ret < 0) + return ret; + if (ret != 1) + return -EIO; + +out: + dev_dbg(aat2870->dev, "read: addr=0x%02x, val=0x%02x\n", addr, *val); + return 0; +} + +static int __aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val) +{ + u8 msg[2]; + int ret; + + if (addr >= AAT2870_REG_NUM) { + dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr); + return -EINVAL; + } + + if (!aat2870->reg_cache[addr].writeable) { + dev_err(aat2870->dev, "Address 0x%02x is not writeable\n", + addr); + return -EINVAL; + } + + msg[0] = addr; + msg[1] = val; + ret = i2c_master_send(aat2870->client, msg, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + aat2870->reg_cache[addr].value = val; + + dev_dbg(aat2870->dev, "write: addr=0x%02x, val=0x%02x\n", addr, val); + return 0; +} + +static int aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val) +{ + int ret; + + mutex_lock(&aat2870->io_lock); + ret = __aat2870_read(aat2870, addr, val); + mutex_unlock(&aat2870->io_lock); + + return ret; +} + +static int aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val) +{ + int ret; + + mutex_lock(&aat2870->io_lock); + ret = __aat2870_write(aat2870, addr, val); + mutex_unlock(&aat2870->io_lock); + + return ret; +} + +static int aat2870_update(struct aat2870_data *aat2870, u8 addr, u8 mask, + u8 val) +{ + int change; + u8 old_val, new_val; + int ret; + + mutex_lock(&aat2870->io_lock); + + ret = __aat2870_read(aat2870, addr, &old_val); + if (ret) + goto out_unlock; + + new_val = (old_val & ~mask) | (val & mask); + change = old_val != new_val; + if (change) + ret = __aat2870_write(aat2870, addr, new_val); + +out_unlock: + mutex_unlock(&aat2870->io_lock); + + return ret; +} + +static inline void aat2870_enable(struct aat2870_data *aat2870) +{ + if (aat2870->en_pin >= 0) + gpio_set_value(aat2870->en_pin, 1); + + aat2870->is_enable = 1; +} + +static inline void aat2870_disable(struct aat2870_data *aat2870) +{ + if (aat2870->en_pin >= 0) + gpio_set_value(aat2870->en_pin, 0); + + aat2870->is_enable = 0; +} + +#ifdef CONFIG_DEBUG_FS +static ssize_t aat2870_dump_reg(struct aat2870_data *aat2870, char *buf) +{ + u8 addr, val; + ssize_t count = 0; + int ret; + + count += sprintf(buf, "aat2870 registers\n"); + for (addr = 0; addr < AAT2870_REG_NUM; addr++) { + count += sprintf(buf + count, "0x%02x: ", addr); + if (count >= PAGE_SIZE - 1) + break; + + ret = aat2870->read(aat2870, addr, &val); + if (ret == 0) + count += snprintf(buf + count, PAGE_SIZE - count, + "0x%02x", val); + else + count += snprintf(buf + count, PAGE_SIZE - count, + "", ret); + + if (count >= PAGE_SIZE - 1) + break; + + count += snprintf(buf + count, PAGE_SIZE - count, "\n"); + if (count >= PAGE_SIZE - 1) + break; + } + + /* Truncate count; min() would cause a warning */ + if (count >= PAGE_SIZE) + count = PAGE_SIZE - 1; + + return count; +} + +static int aat2870_reg_open_file(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t aat2870_reg_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct aat2870_data *aat2870 = file->private_data; + char *buf; + ssize_t ret; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = aat2870_dump_reg(aat2870, buf); + if (ret >= 0) + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + + kfree(buf); + + return ret; +} + +static ssize_t aat2870_reg_write_file(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct aat2870_data *aat2870 = file->private_data; + char buf[32]; + int buf_size; + char *start = buf; + unsigned long addr, val; + int ret; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) { + dev_err(aat2870->dev, "Failed to copy from user\n"); + return -EFAULT; + } + buf[buf_size] = 0; + + while (*start == ' ') + start++; + + addr = simple_strtoul(start, &start, 16); + if (addr >= AAT2870_REG_NUM) { + dev_err(aat2870->dev, "Invalid address, 0x%lx\n", addr); + return -EINVAL; + } + + while (*start == ' ') + start++; + + if (strict_strtoul(start, 16, &val)) + return -EINVAL; + + ret = aat2870->write(aat2870, (u8)addr, (u8)val); + if (ret) + return ret; + + return buf_size; +} + +static const struct file_operations aat2870_reg_fops = { + .open = aat2870_reg_open_file, + .read = aat2870_reg_read_file, + .write = aat2870_reg_write_file, +}; + +static void aat2870_init_debugfs(struct aat2870_data *aat2870) +{ + aat2870->dentry_root = debugfs_create_dir("aat2870", NULL); + if (!aat2870->dentry_root) { + dev_warn(aat2870->dev, + "Failed to create debugfs root directory\n"); + return; + } + + aat2870->dentry_reg = debugfs_create_file("regs", 0644, + aat2870->dentry_root, + aat2870, &aat2870_reg_fops); + if (!aat2870->dentry_reg) + dev_warn(aat2870->dev, + "Failed to create debugfs register file\n"); +} + +static void aat2870_uninit_debugfs(struct aat2870_data *aat2870) +{ + debugfs_remove_recursive(aat2870->dentry_root); +} +#else +static inline void aat2870_init_debugfs(struct aat2870_data *aat2870) +{ +} + +static inline void aat2870_uninit_debugfs(struct aat2870_data *aat2870) +{ +} +#endif /* CONFIG_DEBUG_FS */ + +static int aat2870_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct aat2870_platform_data *pdata = client->dev.platform_data; + struct aat2870_data *aat2870; + int i, j; + int ret = 0; + + aat2870 = kzalloc(sizeof(struct aat2870_data), GFP_KERNEL); + if (!aat2870) { + dev_err(&client->dev, + "Failed to allocate memory for aat2870\n"); + ret = -ENOMEM; + goto out; + } + + aat2870->dev = &client->dev; + dev_set_drvdata(aat2870->dev, aat2870); + + aat2870->client = client; + i2c_set_clientdata(client, aat2870); + + aat2870->reg_cache = aat2870_regs; + + if (pdata->en_pin < 0) + aat2870->en_pin = -1; + else + aat2870->en_pin = pdata->en_pin; + + aat2870->init = pdata->init; + aat2870->uninit = pdata->uninit; + aat2870->read = aat2870_read; + aat2870->write = aat2870_write; + aat2870->update = aat2870_update; + + mutex_init(&aat2870->io_lock); + + if (aat2870->init) + aat2870->init(aat2870); + + if (aat2870->en_pin >= 0) { + ret = gpio_request(aat2870->en_pin, "aat2870-en"); + if (ret < 0) { + dev_err(&client->dev, + "Failed to request GPIO %d\n", aat2870->en_pin); + goto out_kfree; + } + gpio_direction_output(aat2870->en_pin, 1); + } + + aat2870_enable(aat2870); + + for (i = 0; i < pdata->num_subdevs; i++) { + for (j = 0; j < ARRAY_SIZE(aat2870_devs); j++) { + if ((pdata->subdevs[i].id == aat2870_devs[j].id) && + !strcmp(pdata->subdevs[i].name, + aat2870_devs[j].name)) { + aat2870_devs[j].platform_data = + pdata->subdevs[i].platform_data; + break; + } + } + } + + ret = mfd_add_devices(aat2870->dev, 0, aat2870_devs, + ARRAY_SIZE(aat2870_devs), NULL, 0); + if (ret != 0) { + dev_err(aat2870->dev, "Failed to add subdev: %d\n", ret); + goto out_disable; + } + + aat2870_init_debugfs(aat2870); + + return 0; + +out_disable: + aat2870_disable(aat2870); + if (aat2870->en_pin >= 0) + gpio_free(aat2870->en_pin); +out_kfree: + kfree(aat2870); +out: + return ret; +} + +static int aat2870_i2c_remove(struct i2c_client *client) +{ + struct aat2870_data *aat2870 = i2c_get_clientdata(client); + + aat2870_uninit_debugfs(aat2870); + + mfd_remove_devices(aat2870->dev); + aat2870_disable(aat2870); + if (aat2870->en_pin >= 0) + gpio_free(aat2870->en_pin); + if (aat2870->uninit) + aat2870->uninit(aat2870); + kfree(aat2870); + + return 0; +} + +#ifdef CONFIG_PM +static int aat2870_i2c_suspend(struct i2c_client *client, pm_message_t state) +{ + struct aat2870_data *aat2870 = i2c_get_clientdata(client); + + aat2870_disable(aat2870); + + return 0; +} + +static int aat2870_i2c_resume(struct i2c_client *client) +{ + struct aat2870_data *aat2870 = i2c_get_clientdata(client); + struct aat2870_register *reg = NULL; + int i; + + aat2870_enable(aat2870); + + /* restore registers */ + for (i = 0; i < AAT2870_REG_NUM; i++) { + reg = &aat2870->reg_cache[i]; + if (reg->writeable) + aat2870->write(aat2870, i, reg->value); + } + + return 0; +} +#else +#define aat2870_i2c_suspend NULL +#define aat2870_i2c_resume NULL +#endif /* CONFIG_PM */ + +static struct i2c_device_id aat2870_i2c_id_table[] = { + { "aat2870", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aat2870_i2c_id_table); + +static struct i2c_driver aat2870_i2c_driver = { + .driver = { + .name = "aat2870", + .owner = THIS_MODULE, + }, + .probe = aat2870_i2c_probe, + .remove = aat2870_i2c_remove, + .suspend = aat2870_i2c_suspend, + .resume = aat2870_i2c_resume, + .id_table = aat2870_i2c_id_table, +}; + +static int __init aat2870_init(void) +{ + return i2c_add_driver(&aat2870_i2c_driver); +} +subsys_initcall(aat2870_init); + +static void __exit aat2870_exit(void) +{ + i2c_del_driver(&aat2870_i2c_driver); +} +module_exit(aat2870_exit); + +MODULE_DESCRIPTION("Core support for the AnalogicTech AAT2870"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jin Park "); diff --git a/include/linux/mfd/aat2870.h b/include/linux/mfd/aat2870.h new file mode 100644 index 000000000000..89212df05622 --- /dev/null +++ b/include/linux/mfd/aat2870.h @@ -0,0 +1,181 @@ +/* + * linux/include/linux/mfd/aat2870.h + * + * Copyright (c) 2011, NVIDIA Corporation. + * Author: Jin Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __LINUX_MFD_AAT2870_H +#define __LINUX_MFD_AAT2870_H + +#include +#include + +/* Register offsets */ +#define AAT2870_BL_CH_EN 0x00 +#define AAT2870_BLM 0x01 +#define AAT2870_BLS 0x02 +#define AAT2870_BL1 0x03 +#define AAT2870_BL2 0x04 +#define AAT2870_BL3 0x05 +#define AAT2870_BL4 0x06 +#define AAT2870_BL5 0x07 +#define AAT2870_BL6 0x08 +#define AAT2870_BL7 0x09 +#define AAT2870_BL8 0x0A +#define AAT2870_FLR 0x0B +#define AAT2870_FM 0x0C +#define AAT2870_FS 0x0D +#define AAT2870_ALS_CFG0 0x0E +#define AAT2870_ALS_CFG1 0x0F +#define AAT2870_ALS_CFG2 0x10 +#define AAT2870_AMB 0x11 +#define AAT2870_ALS0 0x12 +#define AAT2870_ALS1 0x13 +#define AAT2870_ALS2 0x14 +#define AAT2870_ALS3 0x15 +#define AAT2870_ALS4 0x16 +#define AAT2870_ALS5 0x17 +#define AAT2870_ALS6 0x18 +#define AAT2870_ALS7 0x19 +#define AAT2870_ALS8 0x1A +#define AAT2870_ALS9 0x1B +#define AAT2870_ALSA 0x1C +#define AAT2870_ALSB 0x1D +#define AAT2870_ALSC 0x1E +#define AAT2870_ALSD 0x1F +#define AAT2870_ALSE 0x20 +#define AAT2870_ALSF 0x21 +#define AAT2870_SUB_SET 0x22 +#define AAT2870_SUB_CTRL 0x23 +#define AAT2870_LDO_AB 0x24 +#define AAT2870_LDO_CD 0x25 +#define AAT2870_LDO_EN 0x26 +#define AAT2870_REG_NUM 0x27 + +/* Device IDs */ +enum aat2870_id { + AAT2870_ID_BL, + AAT2870_ID_LDOA, + AAT2870_ID_LDOB, + AAT2870_ID_LDOC, + AAT2870_ID_LDOD +}; + +/* Backlight channels */ +#define AAT2870_BL_CH1 0x01 +#define AAT2870_BL_CH2 0x02 +#define AAT2870_BL_CH3 0x04 +#define AAT2870_BL_CH4 0x08 +#define AAT2870_BL_CH5 0x10 +#define AAT2870_BL_CH6 0x20 +#define AAT2870_BL_CH7 0x40 +#define AAT2870_BL_CH8 0x80 +#define AAT2870_BL_CH_ALL 0xFF + +/* Backlight current magnitude (mA) */ +enum aat2870_current { + AAT2870_CURRENT_0_45, + AAT2870_CURRENT_0_90, + AAT2870_CURRENT_1_80, + AAT2870_CURRENT_2_70, + AAT2870_CURRENT_3_60, + AAT2870_CURRENT_4_50, + AAT2870_CURRENT_5_40, + AAT2870_CURRENT_6_30, + AAT2870_CURRENT_7_20, + AAT2870_CURRENT_8_10, + AAT2870_CURRENT_9_00, + AAT2870_CURRENT_9_90, + AAT2870_CURRENT_10_8, + AAT2870_CURRENT_11_7, + AAT2870_CURRENT_12_6, + AAT2870_CURRENT_13_5, + AAT2870_CURRENT_14_4, + AAT2870_CURRENT_15_3, + AAT2870_CURRENT_16_2, + AAT2870_CURRENT_17_1, + AAT2870_CURRENT_18_0, + AAT2870_CURRENT_18_9, + AAT2870_CURRENT_19_8, + AAT2870_CURRENT_20_7, + AAT2870_CURRENT_21_6, + AAT2870_CURRENT_22_5, + AAT2870_CURRENT_23_4, + AAT2870_CURRENT_24_3, + AAT2870_CURRENT_25_2, + AAT2870_CURRENT_26_1, + AAT2870_CURRENT_27_0, + AAT2870_CURRENT_27_9 +}; + +struct aat2870_register { + bool readable; + bool writeable; + u8 value; +}; + +struct aat2870_data { + struct device *dev; + struct i2c_client *client; + + struct mutex io_lock; + struct aat2870_register *reg_cache; /* register cache */ + int en_pin; /* enable GPIO pin (if < 0, ignore this value) */ + bool is_enable; + + /* init and uninit for platform specified */ + int (*init)(struct aat2870_data *aat2870); + void (*uninit)(struct aat2870_data *aat2870); + + /* i2c io funcntions */ + int (*read)(struct aat2870_data *aat2870, u8 addr, u8 *val); + int (*write)(struct aat2870_data *aat2870, u8 addr, u8 val); + int (*update)(struct aat2870_data *aat2870, u8 addr, u8 mask, u8 val); + + /* for debugfs */ + struct dentry *dentry_root; + struct dentry *dentry_reg; +}; + +struct aat2870_subdev_info { + int id; + const char *name; + void *platform_data; +}; + +struct aat2870_platform_data { + int en_pin; /* enable GPIO pin (if < 0, ignore this value) */ + + struct aat2870_subdev_info *subdevs; + int num_subdevs; + + /* init and uninit for platform specified */ + int (*init)(struct aat2870_data *aat2870); + void (*uninit)(struct aat2870_data *aat2870); +}; + +struct aat2870_bl_platform_data { + /* backlight channels, default is AAT2870_BL_CH_ALL */ + int channels; + /* backlight current magnitude, default is AAT2870_CURRENT_27_9 */ + int max_current; + /* maximum brightness, default is 255 */ + int max_brightness; +}; + +#endif /* __LINUX_MFD_AAT2870_H */ -- cgit v1.2.3 From a00ed25cce6fe856388f89c7cd40da0eee7666a6 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 31 Jul 2011 16:39:04 -0400 Subject: NFS: Re-enable compilation of nfs with !CONFIG_NFS_V4 || !CONFIG_NFS_V4_1 Fix two recently introduced compile problems: Fix a typo in fs/nfs/pnfs.h Move the pnfs_blksize declaration outside the CONFIG_NFS_V4 section in struct nfs_server. Reported-by: Jens Axboe Signed-off-by: Trond Myklebust Signed-off-by: Linus Torvalds --- fs/nfs/pnfs.h | 2 +- include/linux/nfs_fs_sb.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index e0b5d80a43f6..01cbfd54f3cb 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -380,7 +380,7 @@ pnfs_roc_drain(struct inode *ino, u32 *barrier) } static inline void set_pnfs_layoutdriver(struct nfs_server *s, - const struct nfs_fh *mntfh, u32 id); + const struct nfs_fh *mntfh, u32 id) { } diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 82fdfc7987d6..b5479df8378d 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -131,6 +131,7 @@ struct nfs_server { struct fscache_cookie *fscache; /* superblock cookie */ #endif + u32 pnfs_blksize; /* layout_blksize attr */ #ifdef CONFIG_NFS_V4 u32 attr_bitmask[3];/* V4 bitmask representing the set of attributes supported on this @@ -145,7 +146,6 @@ struct nfs_server { filesystem */ struct pnfs_layoutdriver_type *pnfs_curr_ld; /* Active layout driver */ struct rpc_wait_queue roc_rpcwaitq; - u32 pnfs_blksize; /* layout_blksize attr */ void *pnfs_ld_data; /* per mount point data */ /* the following fields are protected by nfs_client->cl_lock */ -- cgit v1.2.3 From f2ee7abf4c40c8e6bffced923a7c01ea2d1f6c97 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 28 Jul 2011 06:41:09 +0200 Subject: vfs: avoid taking inode_hash_lock on pipes and sockets Some inodes (pipes, sockets, ...) are not hashed, no need to take contended inode_hash_lock at dismantle time. nice speedup on SMP machines on socket intensive workloads. Signed-off-by: Eric Dumazet Signed-off-by: Al Viro --- fs/inode.c | 6 +++--- include/linux/fs.h | 9 ++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/inode.c b/fs/inode.c index 9dab13ae6ef7..e445be2a18f9 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -399,12 +399,12 @@ void __insert_inode_hash(struct inode *inode, unsigned long hashval) EXPORT_SYMBOL(__insert_inode_hash); /** - * remove_inode_hash - remove an inode from the hash + * __remove_inode_hash - remove an inode from the hash * @inode: inode to unhash * * Remove an inode from the superblock. */ -void remove_inode_hash(struct inode *inode) +void __remove_inode_hash(struct inode *inode) { spin_lock(&inode_hash_lock); spin_lock(&inode->i_lock); @@ -412,7 +412,7 @@ void remove_inode_hash(struct inode *inode) spin_unlock(&inode->i_lock); spin_unlock(&inode_hash_lock); } -EXPORT_SYMBOL(remove_inode_hash); +EXPORT_SYMBOL(__remove_inode_hash); void end_writeback(struct inode *inode) { diff --git a/include/linux/fs.h b/include/linux/fs.h index f23bcb77260c..786b3b1113cf 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2317,11 +2317,18 @@ extern int should_remove_suid(struct dentry *); extern int file_remove_suid(struct file *); extern void __insert_inode_hash(struct inode *, unsigned long hashval); -extern void remove_inode_hash(struct inode *); static inline void insert_inode_hash(struct inode *inode) { __insert_inode_hash(inode, inode->i_ino); } + +extern void __remove_inode_hash(struct inode *); +static inline void remove_inode_hash(struct inode *inode) +{ + if (!inode_unhashed(inode)) + __remove_inode_hash(inode); +} + extern void inode_sb_list_add(struct inode *inode); #ifdef CONFIG_BLOCK -- cgit v1.2.3 From d3fb612076eebec6f67257db0c7a9666ac7e5892 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 23 Jul 2011 18:37:50 -0400 Subject: switch posix_acl_create() to umode_t * so we can pass &inode->i_mode to it Signed-off-by: Al Viro --- fs/9p/acl.c | 4 ++-- fs/9p/acl.h | 4 ++-- fs/9p/vfs_inode_dotl.c | 6 +++--- fs/btrfs/acl.c | 5 +---- fs/ext2/acl.c | 4 +--- fs/ext3/acl.c | 5 +---- fs/ext4/acl.c | 5 +---- fs/generic_acl.c | 7 +++---- fs/gfs2/acl.c | 4 ++-- fs/jffs2/acl.c | 2 +- fs/jffs2/acl.h | 2 +- fs/jffs2/fs.c | 2 +- fs/jffs2/os-linux.h | 2 +- fs/jfs/acl.c | 4 +--- fs/nfs/nfs3acl.c | 2 +- fs/nfs/nfs3proc.c | 6 +++--- fs/ocfs2/acl.c | 2 +- fs/posix_acl.c | 6 +++--- fs/reiserfs/xattr_acl.c | 6 +----- fs/xfs/linux-2.6/xfs_acl.c | 4 ++-- include/linux/nfs_fs.h | 4 ++-- include/linux/posix_acl.h | 2 +- 22 files changed, 35 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/fs/9p/acl.c b/fs/9p/acl.c index e9cb57f07546..ad734e3220cf 100644 --- a/fs/9p/acl.c +++ b/fs/9p/acl.c @@ -182,11 +182,11 @@ int v9fs_set_create_acl(struct dentry *dentry, return 0; } -int v9fs_acl_mode(struct inode *dir, mode_t *modep, +int v9fs_acl_mode(struct inode *dir, umode_t *modep, struct posix_acl **dpacl, struct posix_acl **pacl) { int retval = 0; - mode_t mode = *modep; + umode_t mode = *modep; struct posix_acl *acl = NULL; if (!S_ISLNK(mode)) { diff --git a/fs/9p/acl.h b/fs/9p/acl.h index ddb7ae19d971..559556411965 100644 --- a/fs/9p/acl.h +++ b/fs/9p/acl.h @@ -20,7 +20,7 @@ extern struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type); extern int v9fs_acl_chmod(struct dentry *); extern int v9fs_set_create_acl(struct dentry *, struct posix_acl **, struct posix_acl **); -extern int v9fs_acl_mode(struct inode *dir, mode_t *modep, +extern int v9fs_acl_mode(struct inode *dir, umode_t *modep, struct posix_acl **dpacl, struct posix_acl **pacl); #else #define v9fs_iop_get_acl NULL @@ -38,7 +38,7 @@ static inline int v9fs_set_create_acl(struct dentry *dentry, { return 0; } -static inline int v9fs_acl_mode(struct inode *dir, mode_t *modep, +static inline int v9fs_acl_mode(struct inode *dir, umode_t *modep, struct posix_acl **dpacl, struct posix_acl **pacl) { diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 9a26dce5a99f..b6c8ed205192 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -206,7 +206,7 @@ v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int omode, int err = 0; gid_t gid; int flags; - mode_t mode; + umode_t mode; char *name = NULL; struct file *filp; struct p9_qid qid; @@ -348,7 +348,7 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir, struct p9_fid *fid = NULL, *dfid = NULL; gid_t gid; char *name; - mode_t mode; + umode_t mode; struct inode *inode; struct p9_qid qid; struct dentry *dir_dentry; @@ -751,7 +751,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode, int err; gid_t gid; char *name; - mode_t mode; + umode_t mode; struct v9fs_session_info *v9ses; struct p9_fid *fid = NULL, *dfid = NULL; struct inode *inode; diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index 65a735d8f6e4..59086142c14d 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -222,19 +222,16 @@ int btrfs_init_acl(struct btrfs_trans_handle *trans, } if (IS_POSIXACL(dir) && acl) { - mode_t mode = inode->i_mode; - if (S_ISDIR(inode->i_mode)) { ret = btrfs_set_acl(trans, inode, acl, ACL_TYPE_DEFAULT); if (ret) goto failed; } - ret = posix_acl_create(&acl, GFP_NOFS, &mode); + ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); if (ret < 0) return ret; - inode->i_mode = mode; if (ret > 0) { /* we need an acl */ ret = btrfs_set_acl(trans, inode, acl, ACL_TYPE_ACCESS); diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c index 52c053763942..0ce740489ab1 100644 --- a/fs/ext2/acl.c +++ b/fs/ext2/acl.c @@ -253,16 +253,14 @@ ext2_init_acl(struct inode *inode, struct inode *dir) inode->i_mode &= ~current_umask(); } if (test_opt(inode->i_sb, POSIX_ACL) && acl) { - mode_t mode = inode->i_mode; if (S_ISDIR(inode->i_mode)) { error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl); if (error) goto cleanup; } - error = posix_acl_create(&acl, GFP_KERNEL, &mode); + error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode); if (error < 0) return error; - inode->i_mode = mode; if (error > 0) { /* This is an extended ACL */ error = ext2_set_acl(inode, ACL_TYPE_ACCESS, acl); diff --git a/fs/ext3/acl.c b/fs/ext3/acl.c index 6c29bf0df04a..74a3c6486f8f 100644 --- a/fs/ext3/acl.c +++ b/fs/ext3/acl.c @@ -261,19 +261,16 @@ ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) inode->i_mode &= ~current_umask(); } if (test_opt(inode->i_sb, POSIX_ACL) && acl) { - mode_t mode = inode->i_mode; - if (S_ISDIR(inode->i_mode)) { error = ext3_set_acl(handle, inode, ACL_TYPE_DEFAULT, acl); if (error) goto cleanup; } - error = posix_acl_create(&acl, GFP_NOFS, &mode); + error = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); if (error < 0) return error; - inode->i_mode = mode; if (error > 0) { /* This is an extended ACL */ error = ext3_set_acl(handle, inode, ACL_TYPE_ACCESS, acl); diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c index dca2d1ded931..74e469ccdf50 100644 --- a/fs/ext4/acl.c +++ b/fs/ext4/acl.c @@ -259,19 +259,16 @@ ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) inode->i_mode &= ~current_umask(); } if (test_opt(inode->i_sb, POSIX_ACL) && acl) { - mode_t mode = inode->i_mode; - if (S_ISDIR(inode->i_mode)) { error = ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT, acl); if (error) goto cleanup; } - error = posix_acl_create(&acl, GFP_NOFS, &mode); + error = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); if (error < 0) return error; - inode->i_mode = mode; if (error > 0) { /* This is an extended ACL */ error = ext4_set_acl(handle, inode, ACL_TYPE_ACCESS, acl); diff --git a/fs/generic_acl.c b/fs/generic_acl.c index d5e33a077a67..2dd434d6ff29 100644 --- a/fs/generic_acl.c +++ b/fs/generic_acl.c @@ -125,21 +125,20 @@ int generic_acl_init(struct inode *inode, struct inode *dir) { struct posix_acl *acl = NULL; - mode_t mode = inode->i_mode; int error; - inode->i_mode = mode & ~current_umask(); if (!S_ISLNK(inode->i_mode)) acl = get_cached_acl(dir, ACL_TYPE_DEFAULT); if (acl) { if (S_ISDIR(inode->i_mode)) set_cached_acl(inode, ACL_TYPE_DEFAULT, acl); - error = posix_acl_create(&acl, GFP_KERNEL, &mode); + error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode); if (error < 0) return error; - inode->i_mode = mode; if (error > 0) set_cached_acl(inode, ACL_TYPE_ACCESS, acl); + } else { + inode->i_mode &= ~current_umask(); } error = 0; diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c index 884c9af0542f..0ac3c53f928f 100644 --- a/fs/gfs2/acl.c +++ b/fs/gfs2/acl.c @@ -72,7 +72,7 @@ struct posix_acl *gfs2_get_acl(struct inode *inode, int type) return gfs2_acl_get(GFS2_I(inode), type); } -static int gfs2_set_mode(struct inode *inode, mode_t mode) +static int gfs2_set_mode(struct inode *inode, umode_t mode) { int error = 0; @@ -117,7 +117,7 @@ int gfs2_acl_create(struct gfs2_inode *dip, struct inode *inode) { struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct posix_acl *acl; - mode_t mode = inode->i_mode; + umode_t mode = inode->i_mode; int error = 0; if (!sdp->sd_args.ar_posix_acl) diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 27c511a1cf05..6372a84728d7 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -259,7 +259,7 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl) return rc; } -int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, mode_t *i_mode) +int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, umode_t *i_mode) { struct posix_acl *acl; int rc; diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h index b3421c78d9f8..9b477246f2a6 100644 --- a/fs/jffs2/acl.h +++ b/fs/jffs2/acl.h @@ -28,7 +28,7 @@ struct jffs2_acl_header { struct posix_acl *jffs2_get_acl(struct inode *inode, int type); extern int jffs2_acl_chmod(struct inode *); -extern int jffs2_init_acl_pre(struct inode *, struct inode *, mode_t *); +extern int jffs2_init_acl_pre(struct inode *, struct inode *, umode_t *); extern int jffs2_init_acl_post(struct inode *); extern const struct xattr_handler jffs2_acl_access_xattr_handler; diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index b81b35ddf4e4..bbcb9755dd2b 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -406,7 +406,7 @@ int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) /* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, fill in the raw_inode while you're at it. */ -struct inode *jffs2_new_inode (struct inode *dir_i, mode_t mode, struct jffs2_raw_inode *ri) +struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_raw_inode *ri) { struct inode *inode; struct super_block *sb = dir_i->i_sb; diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 526979c607b6..6c1755c59c0f 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -173,7 +173,7 @@ int jffs2_do_setattr (struct inode *, struct iattr *); struct inode *jffs2_iget(struct super_block *, unsigned long); void jffs2_evict_inode (struct inode *); void jffs2_dirty_inode(struct inode *inode, int flags); -struct inode *jffs2_new_inode (struct inode *dir_i, mode_t mode, +struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_raw_inode *ri); int jffs2_statfs (struct dentry *, struct kstatfs *); int jffs2_remount_fs (struct super_block *, int *, char *); diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c index b3a32caf2b45..45559dc3ea2f 100644 --- a/fs/jfs/acl.c +++ b/fs/jfs/acl.c @@ -127,16 +127,14 @@ int jfs_init_acl(tid_t tid, struct inode *inode, struct inode *dir) return PTR_ERR(acl); if (acl) { - mode_t mode = inode->i_mode; if (S_ISDIR(inode->i_mode)) { rc = jfs_set_acl(tid, inode, ACL_TYPE_DEFAULT, acl); if (rc) goto cleanup; } - rc = posix_acl_create(&acl, GFP_KERNEL, &mode); + rc = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode); if (rc < 0) goto cleanup; /* posix_acl_release(NULL) is no-op */ - inode->i_mode = mode; if (rc > 0) rc = jfs_set_acl(tid, inode, ACL_TYPE_ACCESS, acl); cleanup: diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c index e49e73107e62..7ef23979896d 100644 --- a/fs/nfs/nfs3acl.c +++ b/fs/nfs/nfs3acl.c @@ -415,7 +415,7 @@ fail: } int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode, - mode_t mode) + umode_t mode) { struct posix_acl *dfacl, *acl; int error = 0; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 38053d823eb0..85f1690ca08c 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -316,7 +316,7 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, int flags, struct nfs_open_context *ctx) { struct nfs3_createdata *data; - mode_t mode = sattr->ia_mode; + umode_t mode = sattr->ia_mode; int status = -ENOMEM; dprintk("NFS call create %s\n", dentry->d_name.name); @@ -562,7 +562,7 @@ static int nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) { struct nfs3_createdata *data; - int mode = sattr->ia_mode; + umode_t mode = sattr->ia_mode; int status = -ENOMEM; dprintk("NFS call mkdir %s\n", dentry->d_name.name); @@ -681,7 +681,7 @@ nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, dev_t rdev) { struct nfs3_createdata *data; - mode_t mode = sattr->ia_mode; + umode_t mode = sattr->ia_mode; int status = -ENOMEM; dprintk("NFS call mknod %s %u:%u\n", dentry->d_name.name, diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c index 783c58d9daf1..fbafc6e36e25 100644 --- a/fs/ocfs2/acl.c +++ b/fs/ocfs2/acl.c @@ -351,7 +351,7 @@ int ocfs2_init_acl(handle_t *handle, struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); struct posix_acl *acl = NULL; int ret = 0, ret2; - mode_t mode; + umode_t mode; if (!S_ISLNK(inode->i_mode)) { if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) { diff --git a/fs/posix_acl.c b/fs/posix_acl.c index d43729a760e2..f0a017edee1e 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -279,11 +279,11 @@ check_perm: * system calls. All permissions that are not granted by the acl are removed. * The permissions in the acl are changed to reflect the mode_p parameter. */ -static int posix_acl_create_masq(struct posix_acl *acl, mode_t *mode_p) +static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p) { struct posix_acl_entry *pa, *pe; struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; - mode_t mode = *mode_p; + umode_t mode = *mode_p; int not_equiv = 0; /* assert(atomic_read(acl->a_refcount) == 1); */ @@ -382,7 +382,7 @@ static int posix_acl_chmod_masq(struct posix_acl *acl, mode_t mode) } int -posix_acl_create(struct posix_acl **acl, gfp_t gfp, mode_t *mode_p) +posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p) { struct posix_acl *clone = posix_acl_clone(*acl, gfp); int err = -ENOMEM; diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c index 7362cf4c946a..89ebc77e0e9a 100644 --- a/fs/reiserfs/xattr_acl.c +++ b/fs/reiserfs/xattr_acl.c @@ -354,8 +354,6 @@ reiserfs_inherit_default_acl(struct reiserfs_transaction_handle *th, return PTR_ERR(acl); if (acl) { - mode_t mode = inode->i_mode; - /* Copy the default ACL to the default ACL of a new directory */ if (S_ISDIR(inode->i_mode)) { err = reiserfs_set_acl(th, inode, ACL_TYPE_DEFAULT, @@ -366,12 +364,10 @@ reiserfs_inherit_default_acl(struct reiserfs_transaction_handle *th, /* Now we reconcile the new ACL and the mode, potentially modifying both */ - err = posix_acl_create(&acl, GFP_NOFS, &mode); + err = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); if (err < 0) return err; - inode->i_mode = mode; - /* If we need an ACL.. */ if (err > 0) err = reiserfs_set_acl(th, inode, ACL_TYPE_ACCESS, acl); diff --git a/fs/xfs/linux-2.6/xfs_acl.c b/fs/xfs/linux-2.6/xfs_acl.c index 44ce51656804..bb85500e0b88 100644 --- a/fs/xfs/linux-2.6/xfs_acl.c +++ b/fs/xfs/linux-2.6/xfs_acl.c @@ -221,7 +221,7 @@ xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) } static int -xfs_set_mode(struct inode *inode, mode_t mode) +xfs_set_mode(struct inode *inode, umode_t mode) { int error = 0; @@ -267,7 +267,7 @@ posix_acl_default_exists(struct inode *inode) int xfs_inherit_acl(struct inode *inode, struct posix_acl *acl) { - mode_t mode = inode->i_mode; + umode_t mode = inode->i_mode; int error = 0, inherit = 0; if (S_ISDIR(inode->i_mode)) { diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 8b579beb6358..dda2ac8a5f7b 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -568,12 +568,12 @@ extern struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type); extern int nfs3_proc_setacl(struct inode *inode, int type, struct posix_acl *acl); extern int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode, - mode_t mode); + umode_t mode); extern void nfs3_forget_cached_acls(struct inode *inode); #else static inline int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode, - mode_t mode) + umode_t mode) { return 0; } diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h index 9a53b99818e2..bd8d0050d725 100644 --- a/include/linux/posix_acl.h +++ b/include/linux/posix_acl.h @@ -77,7 +77,7 @@ extern int posix_acl_valid(const struct posix_acl *); extern int posix_acl_permission(struct inode *, const struct posix_acl *, int); extern struct posix_acl *posix_acl_from_mode(mode_t, gfp_t); extern int posix_acl_equiv_mode(const struct posix_acl *, mode_t *); -extern int posix_acl_create(struct posix_acl **, gfp_t, mode_t *); +extern int posix_acl_create(struct posix_acl **, gfp_t, umode_t *); extern int posix_acl_chmod(struct posix_acl **, gfp_t, mode_t); extern struct posix_acl *get_posix_acl(struct inode *, int); -- cgit v1.2.3 From d6952123b53cc8b334df69bba2cd0063b0d88f68 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 23 Jul 2011 18:56:36 -0400 Subject: switch posix_acl_equiv_mode() to umode_t * ... so that &inode->i_mode could be passed to it Signed-off-by: Al Viro --- fs/9p/acl.c | 2 +- fs/btrfs/acl.c | 5 +---- fs/ext2/acl.c | 4 +--- fs/ext3/acl.c | 4 +--- fs/ext4/acl.c | 4 +--- fs/generic_acl.c | 6 +----- fs/gfs2/acl.c | 2 +- fs/jffs2/acl.c | 2 +- fs/jfs/xattr.c | 4 +--- fs/ocfs2/acl.c | 2 +- fs/posix_acl.c | 4 ++-- fs/reiserfs/xattr_acl.c | 4 +--- fs/xfs/linux-2.6/xfs_acl.c | 2 +- include/linux/posix_acl.h | 2 +- 14 files changed, 15 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/fs/9p/acl.c b/fs/9p/acl.c index ad734e3220cf..9a1d42630751 100644 --- a/fs/9p/acl.c +++ b/fs/9p/acl.c @@ -319,7 +319,7 @@ static int v9fs_xattr_set_acl(struct dentry *dentry, const char *name, case ACL_TYPE_ACCESS: name = POSIX_ACL_XATTR_ACCESS; if (acl) { - mode_t mode = inode->i_mode; + umode_t mode = inode->i_mode; retval = posix_acl_equiv_mode(acl, &mode); if (retval < 0) goto err_out; diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index 59086142c14d..4cc5c0164ed6 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -111,7 +111,6 @@ static int btrfs_set_acl(struct btrfs_trans_handle *trans, int ret, size = 0; const char *name; char *value = NULL; - mode_t mode; if (acl) { ret = posix_acl_valid(acl); @@ -122,13 +121,11 @@ static int btrfs_set_acl(struct btrfs_trans_handle *trans, switch (type) { case ACL_TYPE_ACCESS: - mode = inode->i_mode; name = POSIX_ACL_XATTR_ACCESS; if (acl) { - ret = posix_acl_equiv_mode(acl, &mode); + ret = posix_acl_equiv_mode(acl, &inode->i_mode); if (ret < 0) return ret; - inode->i_mode = mode; } ret = 0; break; diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c index 0ce740489ab1..35d6a3cfd9ff 100644 --- a/fs/ext2/acl.c +++ b/fs/ext2/acl.c @@ -194,12 +194,10 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl) case ACL_TYPE_ACCESS: name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; if (acl) { - mode_t mode = inode->i_mode; - error = posix_acl_equiv_mode(acl, &mode); + error = posix_acl_equiv_mode(acl, &inode->i_mode); if (error < 0) return error; else { - inode->i_mode = mode; inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); if (error == 0) diff --git a/fs/ext3/acl.c b/fs/ext3/acl.c index 74a3c6486f8f..3091f62e55b6 100644 --- a/fs/ext3/acl.c +++ b/fs/ext3/acl.c @@ -199,12 +199,10 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type, case ACL_TYPE_ACCESS: name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS; if (acl) { - mode_t mode = inode->i_mode; - error = posix_acl_equiv_mode(acl, &mode); + error = posix_acl_equiv_mode(acl, &inode->i_mode); if (error < 0) return error; else { - inode->i_mode = mode; inode->i_ctime = CURRENT_TIME_SEC; ext3_mark_inode_dirty(handle, inode); if (error == 0) diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c index 74e469ccdf50..a5c29bb3b835 100644 --- a/fs/ext4/acl.c +++ b/fs/ext4/acl.c @@ -198,12 +198,10 @@ ext4_set_acl(handle_t *handle, struct inode *inode, int type, case ACL_TYPE_ACCESS: name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; if (acl) { - mode_t mode = inode->i_mode; - error = posix_acl_equiv_mode(acl, &mode); + error = posix_acl_equiv_mode(acl, &inode->i_mode); if (error < 0) return error; else { - inode->i_mode = mode; inode->i_ctime = ext4_current_time(inode); ext4_mark_inode_dirty(handle, inode); if (error == 0) diff --git a/fs/generic_acl.c b/fs/generic_acl.c index 2dd434d6ff29..d0dddaceac59 100644 --- a/fs/generic_acl.c +++ b/fs/generic_acl.c @@ -82,18 +82,14 @@ generic_acl_set(struct dentry *dentry, const char *name, const void *value, return PTR_ERR(acl); } if (acl) { - mode_t mode; - error = posix_acl_valid(acl); if (error) goto failed; switch (type) { case ACL_TYPE_ACCESS: - mode = inode->i_mode; - error = posix_acl_equiv_mode(acl, &mode); + error = posix_acl_equiv_mode(acl, &inode->i_mode); if (error < 0) goto failed; - inode->i_mode = mode; inode->i_ctime = CURRENT_TIME; if (error == 0) { posix_acl_release(acl); diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c index 0ac3c53f928f..34501b64bc47 100644 --- a/fs/gfs2/acl.c +++ b/fs/gfs2/acl.c @@ -276,7 +276,7 @@ static int gfs2_xattr_system_set(struct dentry *dentry, const char *name, goto out_release; if (type == ACL_TYPE_ACCESS) { - mode_t mode = inode->i_mode; + umode_t mode = inode->i_mode; error = posix_acl_equiv_mode(acl, &mode); if (error <= 0) { diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 6372a84728d7..926d02068a14 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -227,7 +227,7 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl) case ACL_TYPE_ACCESS: xprefix = JFFS2_XPREFIX_ACL_ACCESS; if (acl) { - mode_t mode = inode->i_mode; + umode_t mode = inode->i_mode; rc = posix_acl_equiv_mode(acl, &mode); if (rc < 0) return rc; diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index 24838f1eeee5..e87fedef23db 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -693,8 +693,7 @@ static int can_set_system_xattr(struct inode *inode, const char *name, return rc; } if (acl) { - mode_t mode = inode->i_mode; - rc = posix_acl_equiv_mode(acl, &mode); + rc = posix_acl_equiv_mode(acl, &inode->i_mode); posix_acl_release(acl); if (rc < 0) { printk(KERN_ERR @@ -702,7 +701,6 @@ static int can_set_system_xattr(struct inode *inode, const char *name, rc); return rc; } - inode->i_mode = mode; mark_inode_dirty(inode); } /* diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c index fbafc6e36e25..a7219075b4de 100644 --- a/fs/ocfs2/acl.c +++ b/fs/ocfs2/acl.c @@ -247,7 +247,7 @@ static int ocfs2_set_acl(handle_t *handle, case ACL_TYPE_ACCESS: name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS; if (acl) { - mode_t mode = inode->i_mode; + umode_t mode = inode->i_mode; ret = posix_acl_equiv_mode(acl, &mode); if (ret < 0) return ret; diff --git a/fs/posix_acl.c b/fs/posix_acl.c index f0a017edee1e..3d943be6761c 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -149,10 +149,10 @@ posix_acl_valid(const struct posix_acl *acl) * file mode permission bits, or else 1. Returns -E... on error. */ int -posix_acl_equiv_mode(const struct posix_acl *acl, mode_t *mode_p) +posix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p) { const struct posix_acl_entry *pa, *pe; - mode_t mode = 0; + umode_t mode = 0; int not_equiv = 0; FOREACH_ACL_ENTRY(pa, acl, pe) { diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c index 89ebc77e0e9a..6da0396e5052 100644 --- a/fs/reiserfs/xattr_acl.c +++ b/fs/reiserfs/xattr_acl.c @@ -272,12 +272,10 @@ reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode, case ACL_TYPE_ACCESS: name = POSIX_ACL_XATTR_ACCESS; if (acl) { - mode_t mode = inode->i_mode; - error = posix_acl_equiv_mode(acl, &mode); + error = posix_acl_equiv_mode(acl, &inode->i_mode); if (error < 0) return error; else { - inode->i_mode = mode; if (error == 0) acl = NULL; } diff --git a/fs/xfs/linux-2.6/xfs_acl.c b/fs/xfs/linux-2.6/xfs_acl.c index bb85500e0b88..b6c4b3795c4a 100644 --- a/fs/xfs/linux-2.6/xfs_acl.c +++ b/fs/xfs/linux-2.6/xfs_acl.c @@ -381,7 +381,7 @@ xfs_xattr_acl_set(struct dentry *dentry, const char *name, goto out_release; if (type == ACL_TYPE_ACCESS) { - mode_t mode = inode->i_mode; + umode_t mode = inode->i_mode; error = posix_acl_equiv_mode(acl, &mode); if (error <= 0) { diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h index bd8d0050d725..529c32ad58c0 100644 --- a/include/linux/posix_acl.h +++ b/include/linux/posix_acl.h @@ -76,7 +76,7 @@ extern struct posix_acl *posix_acl_alloc(int, gfp_t); extern int posix_acl_valid(const struct posix_acl *); extern int posix_acl_permission(struct inode *, const struct posix_acl *, int); extern struct posix_acl *posix_acl_from_mode(mode_t, gfp_t); -extern int posix_acl_equiv_mode(const struct posix_acl *, mode_t *); +extern int posix_acl_equiv_mode(const struct posix_acl *, umode_t *); extern int posix_acl_create(struct posix_acl **, gfp_t, umode_t *); extern int posix_acl_chmod(struct posix_acl **, gfp_t, mode_t); -- cgit v1.2.3 From 3a5fba19b080b365d67866db38e32e6a4a2089e8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 23 Jul 2011 19:01:48 -0400 Subject: switch posix_acl_from_mode() to umode_t ... seeing that this is what all callers pass to it anyway. Signed-off-by: Al Viro --- fs/posix_acl.c | 2 +- include/linux/posix_acl.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 3d943be6761c..4e16e8001982 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -188,7 +188,7 @@ posix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p) * Create an ACL representing the file mode permission bits of an inode. */ struct posix_acl * -posix_acl_from_mode(mode_t mode, gfp_t flags) +posix_acl_from_mode(umode_t mode, gfp_t flags) { struct posix_acl *acl = posix_acl_alloc(3, flags); if (!acl) diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h index 529c32ad58c0..16ecfb485d28 100644 --- a/include/linux/posix_acl.h +++ b/include/linux/posix_acl.h @@ -75,7 +75,7 @@ extern void posix_acl_init(struct posix_acl *, int); extern struct posix_acl *posix_acl_alloc(int, gfp_t); extern int posix_acl_valid(const struct posix_acl *); extern int posix_acl_permission(struct inode *, const struct posix_acl *, int); -extern struct posix_acl *posix_acl_from_mode(mode_t, gfp_t); +extern struct posix_acl *posix_acl_from_mode(umode_t, gfp_t); extern int posix_acl_equiv_mode(const struct posix_acl *, umode_t *); extern int posix_acl_create(struct posix_acl **, gfp_t, umode_t *); extern int posix_acl_chmod(struct posix_acl **, gfp_t, mode_t); -- cgit v1.2.3 From 86bc704db0ab7e69230f79bc7d124e063259abc6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 23 Jul 2011 19:03:11 -0400 Subject: switch posix_acl_chmod() to umode_t again, that's what all callers pass to it Signed-off-by: Al Viro --- fs/posix_acl.c | 4 ++-- include/linux/posix_acl.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 4e16e8001982..10027b42b7e2 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -336,7 +336,7 @@ static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p) /* * Modify the ACL for the chmod syscall. */ -static int posix_acl_chmod_masq(struct posix_acl *acl, mode_t mode) +static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode) { struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; struct posix_acl_entry *pa, *pe; @@ -400,7 +400,7 @@ posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p) EXPORT_SYMBOL(posix_acl_create); int -posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, mode_t mode) +posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode) { struct posix_acl *clone = posix_acl_clone(*acl, gfp); int err = -ENOMEM; diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h index 16ecfb485d28..951bba82d50d 100644 --- a/include/linux/posix_acl.h +++ b/include/linux/posix_acl.h @@ -78,7 +78,7 @@ extern int posix_acl_permission(struct inode *, const struct posix_acl *, int); extern struct posix_acl *posix_acl_from_mode(umode_t, gfp_t); extern int posix_acl_equiv_mode(const struct posix_acl *, umode_t *); extern int posix_acl_create(struct posix_acl **, gfp_t, umode_t *); -extern int posix_acl_chmod(struct posix_acl **, gfp_t, mode_t); +extern int posix_acl_chmod(struct posix_acl **, gfp_t, umode_t); extern struct posix_acl *get_posix_acl(struct inode *, int); extern int set_posix_acl(struct inode *, int, struct posix_acl *); -- cgit v1.2.3 From 82c21bfab41a77bc01affe21bea9727d776774a7 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 1 Aug 2011 11:10:33 +0000 Subject: doc: Update the email address for Paul Moore in various source files My @hp.com will no longer be valid starting August 5, 2011 so an update is necessary. My new email address is employer independent so we don't have to worry about doing this again any time soon. Signed-off-by: Paul Moore Signed-off-by: Paul Moore Signed-off-by: David S. Miller --- include/net/cipso_ipv4.h | 2 +- include/net/netlabel.h | 2 +- net/netlabel/Makefile | 2 -- net/netlabel/netlabel_addrlist.c | 2 +- net/netlabel/netlabel_addrlist.h | 2 +- net/netlabel/netlabel_cipso_v4.c | 2 +- net/netlabel/netlabel_cipso_v4.h | 2 +- net/netlabel/netlabel_domainhash.c | 2 +- net/netlabel/netlabel_domainhash.h | 2 +- net/netlabel/netlabel_kapi.c | 2 +- net/netlabel/netlabel_mgmt.c | 2 +- net/netlabel/netlabel_mgmt.h | 2 +- net/netlabel/netlabel_unlabeled.c | 2 +- net/netlabel/netlabel_unlabeled.h | 2 +- net/netlabel/netlabel_user.c | 2 +- net/netlabel/netlabel_user.h | 2 +- security/selinux/hooks.c | 2 +- security/selinux/include/netif.h | 2 +- security/selinux/include/netlabel.h | 2 +- security/selinux/include/netnode.h | 2 +- security/selinux/include/netport.h | 2 +- security/selinux/netif.c | 2 +- security/selinux/netlabel.c | 2 +- security/selinux/netnode.c | 2 +- security/selinux/netport.c | 2 +- security/selinux/selinuxfs.c | 2 +- security/selinux/ss/ebitmap.c | 2 +- security/selinux/ss/mls.c | 2 +- security/selinux/ss/mls.h | 2 +- security/selinux/ss/policydb.c | 2 +- security/selinux/ss/services.c | 2 +- security/smack/smack_lsm.c | 2 +- 32 files changed, 31 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h index 3b938743514b..9808877c2ab9 100644 --- a/include/net/cipso_ipv4.h +++ b/include/net/cipso_ipv4.h @@ -8,7 +8,7 @@ * have chosen to adopt the protocol and over the years it has become a * de-facto standard for labeled networking. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/include/net/netlabel.h b/include/net/netlabel.h index f21a16ee3705..f67440970d7e 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -4,7 +4,7 @@ * The NetLabel system manages static and dynamic label mappings for network * protocols such as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/Makefile b/net/netlabel/Makefile index ea750e9df65f..d2732fc952e2 100644 --- a/net/netlabel/Makefile +++ b/net/netlabel/Makefile @@ -1,8 +1,6 @@ # # Makefile for the NetLabel subsystem. # -# Feb 9, 2006, Paul Moore -# # base objects obj-y := netlabel_user.o netlabel_kapi.o diff --git a/net/netlabel/netlabel_addrlist.c b/net/netlabel/netlabel_addrlist.c index c0519139679e..96b749dacc34 100644 --- a/net/netlabel/netlabel_addrlist.c +++ b/net/netlabel/netlabel_addrlist.c @@ -6,7 +6,7 @@ * system manages static and dynamic label mappings for network protocols such * as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/netlabel_addrlist.h b/net/netlabel/netlabel_addrlist.h index 2b9644e19de0..fdbc1d2c7352 100644 --- a/net/netlabel/netlabel_addrlist.h +++ b/net/netlabel/netlabel_addrlist.h @@ -6,7 +6,7 @@ * system manages static and dynamic label mappings for network protocols such * as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c index dd53a36d89af..6bf878335d94 100644 --- a/net/netlabel/netlabel_cipso_v4.c +++ b/net/netlabel/netlabel_cipso_v4.c @@ -5,7 +5,7 @@ * NetLabel system manages static and dynamic label mappings for network * protocols such as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/netlabel_cipso_v4.h b/net/netlabel/netlabel_cipso_v4.h index af7f3355103e..d24d774bfd62 100644 --- a/net/netlabel/netlabel_cipso_v4.h +++ b/net/netlabel/netlabel_cipso_v4.h @@ -5,7 +5,7 @@ * NetLabel system manages static and dynamic label mappings for network * protocols such as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c index 2aa975e5452d..7d8083cde34f 100644 --- a/net/netlabel/netlabel_domainhash.c +++ b/net/netlabel/netlabel_domainhash.c @@ -6,7 +6,7 @@ * system manages static and dynamic label mappings for network protocols such * as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h index 0261dda3f2d2..bfcc0f7024c5 100644 --- a/net/netlabel/netlabel_domainhash.h +++ b/net/netlabel/netlabel_domainhash.h @@ -6,7 +6,7 @@ * system manages static and dynamic label mappings for network protocols such * as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index b528dd928d3c..58107d060846 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -5,7 +5,7 @@ * system manages static and dynamic label mappings for network protocols such * as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c index dff8a0809245..bfa555869775 100644 --- a/net/netlabel/netlabel_mgmt.c +++ b/net/netlabel/netlabel_mgmt.c @@ -5,7 +5,7 @@ * NetLabel system manages static and dynamic label mappings for network * protocols such as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/netlabel_mgmt.h b/net/netlabel/netlabel_mgmt.h index 8db37f4c10f7..5a9f31ce5799 100644 --- a/net/netlabel/netlabel_mgmt.h +++ b/net/netlabel/netlabel_mgmt.h @@ -5,7 +5,7 @@ * NetLabel system manages static and dynamic label mappings for network * protocols such as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index f1ecf848e3ac..e6e823656f9d 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -5,7 +5,7 @@ * NetLabel system. The NetLabel system manages static and dynamic label * mappings for network protocols such as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/netlabel_unlabeled.h b/net/netlabel/netlabel_unlabeled.h index 0bc8dc3f9e3c..700af49022a0 100644 --- a/net/netlabel/netlabel_unlabeled.h +++ b/net/netlabel/netlabel_unlabeled.h @@ -5,7 +5,7 @@ * NetLabel system. The NetLabel system manages static and dynamic label * mappings for network protocols such as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/netlabel_user.c b/net/netlabel/netlabel_user.c index a3fd75ac3fa5..9fae63f10298 100644 --- a/net/netlabel/netlabel_user.c +++ b/net/netlabel/netlabel_user.c @@ -5,7 +5,7 @@ * NetLabel system manages static and dynamic label mappings for network * protocols such as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/net/netlabel/netlabel_user.h b/net/netlabel/netlabel_user.h index f4fc4c9ad567..81969785e279 100644 --- a/net/netlabel/netlabel_user.h +++ b/net/netlabel/netlabel_user.h @@ -5,7 +5,7 @@ * NetLabel system manages static and dynamic label mappings for network * protocols such as CIPSO and RIPSO. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index a38316b2e3f6..266a2292451d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -14,7 +14,7 @@ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * * Copyright (C) 2006, 2007, 2009 Hewlett-Packard Development Company, L.P. - * Paul Moore + * Paul Moore * Copyright (C) 2007 Hitachi Software Engineering Co., Ltd. * Yuichi Nakamura * diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h index ce23edd128b3..43d507242b42 100644 --- a/security/selinux/include/netif.h +++ b/security/selinux/include/netif.h @@ -8,7 +8,7 @@ * * Copyright (C) 2003 Red Hat, Inc., James Morris * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. - * Paul Moore, + * Paul Moore * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index cf2f628e6e28..8c59b8f150e8 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h @@ -1,7 +1,7 @@ /* * SELinux interface to the NetLabel subsystem * - * Author : Paul Moore + * Author: Paul Moore * */ diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h index 1b94450d11d2..df7a5ed6c694 100644 --- a/security/selinux/include/netnode.h +++ b/security/selinux/include/netnode.h @@ -6,7 +6,7 @@ * needed to reduce the lookup overhead since most of these queries happen on * a per-packet basis. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/security/selinux/include/netport.h b/security/selinux/include/netport.h index 8991752eaf93..4d965b83d735 100644 --- a/security/selinux/include/netport.h +++ b/security/selinux/include/netport.h @@ -5,7 +5,7 @@ * mapping is maintained as part of the normal policy but a fast cache is * needed to reduce the lookup overhead. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/security/selinux/netif.c b/security/selinux/netif.c index 58cc481c93d5..326f22cbe405 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -8,7 +8,7 @@ * * Copyright (C) 2003 Red Hat, Inc., James Morris * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. - * Paul Moore + * Paul Moore * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index c3bf3ed07b06..da4b8b233280 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -4,7 +4,7 @@ * This file provides the necessary glue to tie NetLabel into the SELinux * subsystem. * - * Author: Paul Moore + * Author: Paul Moore * */ diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index 8b691a863186..3bf46abaa688 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -6,7 +6,7 @@ * needed to reduce the lookup overhead since most of these queries happen on * a per-packet basis. * - * Author: Paul Moore + * Author: Paul Moore * * This code is heavily based on the "netif" concept originally developed by * James Morris diff --git a/security/selinux/netport.c b/security/selinux/netport.c index ae76e298de7d..0b62bd112461 100644 --- a/security/selinux/netport.c +++ b/security/selinux/netport.c @@ -5,7 +5,7 @@ * mapping is maintained as part of the normal policy but a fast cache is * needed to reduce the lookup overhead. * - * Author: Paul Moore + * Author: Paul Moore * * This code is heavily based on the "netif" concept originally developed by * James Morris diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index de7900ef53da..55d92cbb177a 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -2,7 +2,7 @@ * * Added conditional policy language extensions * - * Updated: Hewlett-Packard + * Updated: Hewlett-Packard * * Added support for the policy capability bitmap * diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index d42951fcbe87..30f119b1d1ec 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -4,7 +4,7 @@ * Author : Stephen Smalley, */ /* - * Updated: Hewlett-Packard + * Updated: Hewlett-Packard * * Added support to import/export the NetLabel category bitmap * diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index e96174216bc9..fbf9c5816c71 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -11,7 +11,7 @@ * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. */ /* - * Updated: Hewlett-Packard + * Updated: Hewlett-Packard * * Added support to import/export the MLS label from NetLabel * diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index 037bf9d82d41..e4369e3e6366 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h @@ -11,7 +11,7 @@ * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. */ /* - * Updated: Hewlett-Packard + * Updated: Hewlett-Packard * * Added support to import/export the MLS label from NetLabel * diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index d246aca3f4fb..2381d0ded228 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -13,7 +13,7 @@ * * Added conditional policy language extensions * - * Updated: Hewlett-Packard + * Updated: Hewlett-Packard * * Added support for the policy capability bitmap * diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 973e00e34fa9..f6917bc0aa05 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -13,7 +13,7 @@ * * Added conditional policy language extensions * - * Updated: Hewlett-Packard + * Updated: Hewlett-Packard * * Added support for NetLabel * Added support for the policy capability bitmap diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index f375eb2e1957..b9c5e149903b 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -9,7 +9,7 @@ * * Copyright (C) 2007 Casey Schaufler * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. - * Paul Moore + * Paul Moore * Copyright (C) 2010 Nokia Corporation * * This program is free software; you can redistribute it and/or modify -- cgit v1.2.3 From a6e50b409d3f9e0833e69c3c9cca822e8fa4adbb Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Tue, 2 Aug 2011 12:32:04 +0100 Subject: dm snapshot: skip reading origin when overwriting complete chunk If we write a full chunk in the snapshot, skip reading the origin device because the whole chunk will be overwritten anyway. This patch changes the snapshot write logic when a full chunk is written. In this case: 1. allocate the exception 2. dispatch the bio (but don't report the bio completion to device mapper) 3. write the exception record 4. report bio completed Callbacks must be done through the kcopyd thread, because callbacks must not race with each other. So we create two new functions: dm_kcopyd_prepare_callback: allocate a job structure and prepare the callback. (This function must not be called from interrupt context.) dm_kcopyd_do_callback: submit callback. (This function may be called from interrupt context.) Performance test (on snapshots with 4k chunk size): without the patch: non-direct-io sequential write (dd): 17.7MB/s direct-io sequential write (dd): 20.9MB/s non-direct-io random write (mkfs.ext2): 0.44s with the patch: non-direct-io sequential write (dd): 26.5MB/s direct-io sequential write (dd): 33.2MB/s non-direct-io random write (mkfs.ext2): 0.27s Signed-off-by: Mikulas Patocka Signed-off-by: Alasdair G Kergon --- drivers/md/dm-kcopyd.c | 31 ++++++++++++++++++++++++ drivers/md/dm-snap.c | 60 ++++++++++++++++++++++++++++++++++++++++++++--- include/linux/dm-kcopyd.h | 15 ++++++++++++ 3 files changed, 103 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index 98725e119324..f82147029636 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c @@ -617,6 +617,37 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from, } EXPORT_SYMBOL(dm_kcopyd_copy); +void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc, + dm_kcopyd_notify_fn fn, void *context) +{ + struct kcopyd_job *job; + + job = mempool_alloc(kc->job_pool, GFP_NOIO); + + memset(job, 0, sizeof(struct kcopyd_job)); + job->kc = kc; + job->fn = fn; + job->context = context; + + atomic_inc(&kc->nr_jobs); + + return job; +} +EXPORT_SYMBOL(dm_kcopyd_prepare_callback); + +void dm_kcopyd_do_callback(void *j, int read_err, unsigned long write_err) +{ + struct kcopyd_job *job = j; + struct dm_kcopyd_client *kc = job->kc; + + job->read_err = read_err; + job->write_err = write_err; + + push(&kc->complete_jobs, job); + wake(kc); +} +EXPORT_SYMBOL(dm_kcopyd_do_callback); + /* * Cancels a kcopyd job, eg. someone might be deactivating a * mirror. diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 94dee05dd28e..6f758870fc19 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -170,6 +170,13 @@ struct dm_snap_pending_exception { * kcopyd. */ int started; + + /* + * For writing a complete chunk, bypassing the copy. + */ + struct bio *full_bio; + bio_end_io_t *full_bio_end_io; + void *full_bio_private; }; /* @@ -1369,6 +1376,7 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success) struct dm_snapshot *s = pe->snap; struct bio *origin_bios = NULL; struct bio *snapshot_bios = NULL; + struct bio *full_bio = NULL; int error = 0; if (!success) { @@ -1408,6 +1416,11 @@ out: dm_remove_exception(&pe->e); snapshot_bios = bio_list_get(&pe->snapshot_bios); origin_bios = bio_list_get(&pe->origin_bios); + full_bio = pe->full_bio; + if (full_bio) { + full_bio->bi_end_io = pe->full_bio_end_io; + full_bio->bi_private = pe->full_bio_private; + } free_pending_exception(pe); increment_pending_exceptions_done_count(); @@ -1415,10 +1428,15 @@ out: up_write(&s->lock); /* Submit any pending write bios */ - if (error) + if (error) { + if (full_bio) + bio_io_error(full_bio); error_bios(snapshot_bios); - else + } else { + if (full_bio) + bio_endio(full_bio, 0); flush_bios(snapshot_bios); + } retry_origin_bios(s, origin_bios); } @@ -1472,6 +1490,32 @@ static void start_copy(struct dm_snap_pending_exception *pe) dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, copy_callback, pe); } +static void full_bio_end_io(struct bio *bio, int error) +{ + void *callback_data = bio->bi_private; + + dm_kcopyd_do_callback(callback_data, 0, error ? 1 : 0); +} + +static void start_full_bio(struct dm_snap_pending_exception *pe, + struct bio *bio) +{ + struct dm_snapshot *s = pe->snap; + void *callback_data; + + pe->full_bio = bio; + pe->full_bio_end_io = bio->bi_end_io; + pe->full_bio_private = bio->bi_private; + + callback_data = dm_kcopyd_prepare_callback(s->kcopyd_client, + copy_callback, pe); + + bio->bi_end_io = full_bio_end_io; + bio->bi_private = callback_data; + + generic_make_request(bio); +} + static struct dm_snap_pending_exception * __lookup_pending_exception(struct dm_snapshot *s, chunk_t chunk) { @@ -1507,6 +1551,7 @@ __find_pending_exception(struct dm_snapshot *s, bio_list_init(&pe->origin_bios); bio_list_init(&pe->snapshot_bios); pe->started = 0; + pe->full_bio = NULL; if (s->store->type->prepare_exception(s->store, &pe->e)) { free_pending_exception(pe); @@ -1600,10 +1645,19 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio, } remap_exception(s, &pe->e, bio, chunk); - bio_list_add(&pe->snapshot_bios, bio); r = DM_MAPIO_SUBMITTED; + if (!pe->started && + bio->bi_size == (s->store->chunk_size << SECTOR_SHIFT)) { + pe->started = 1; + up_write(&s->lock); + start_full_bio(pe, bio); + goto out; + } + + bio_list_add(&pe->snapshot_bios, bio); + if (!pe->started) { /* this is protected by snap->lock */ pe->started = 1; diff --git a/include/linux/dm-kcopyd.h b/include/linux/dm-kcopyd.h index 298d587e349b..5e54458e920f 100644 --- a/include/linux/dm-kcopyd.h +++ b/include/linux/dm-kcopyd.h @@ -42,5 +42,20 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from, unsigned num_dests, struct dm_io_region *dests, unsigned flags, dm_kcopyd_notify_fn fn, void *context); +/* + * Prepare a callback and submit it via the kcopyd thread. + * + * dm_kcopyd_prepare_callback allocates a callback structure and returns it. + * It must not be called from interrupt context. + * The returned value should be passed into dm_kcopyd_do_callback. + * + * dm_kcopyd_do_callback submits the callback. + * It may be called from interrupt context. + * The callback is issued from the kcopyd thread. + */ +void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc, + dm_kcopyd_notify_fn fn, void *context); +void dm_kcopyd_do_callback(void *job, int read_err, unsigned long write_err); + #endif /* __KERNEL__ */ #endif /* _LINUX_DM_KCOPYD_H */ -- cgit v1.2.3 From 498f0103ea13123e007660def9072a0b7dd1c599 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Tue, 2 Aug 2011 12:32:04 +0100 Subject: dm table: share target argument parsing functions Move multipath target argument parsing code into dm-table so other targets can share it. Signed-off-by: Mike Snitzer Signed-off-by: Alasdair G Kergon --- drivers/md/dm-crypt.c | 7 +- drivers/md/dm-mpath.c | 152 ++++++++++++------------------------------ drivers/md/dm-table.c | 57 ++++++++++++++++ include/linux/device-mapper.h | 43 ++++++++++++ 4 files changed, 147 insertions(+), 112 deletions(-) (limited to 'include') diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index f5406766ece3..b79e7472a9b3 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -30,7 +30,6 @@ #include #define DM_MSG_PREFIX "crypt" -#define MESG_STR(x) x, sizeof(x) /* * context holding the current state of a multi-part conversion @@ -1770,12 +1769,12 @@ static int crypt_message(struct dm_target *ti, unsigned argc, char **argv) if (argc < 2) goto error; - if (!strnicmp(argv[0], MESG_STR("key"))) { + if (!strcasecmp(argv[0], "key")) { if (!test_bit(DM_CRYPT_SUSPENDED, &cc->flags)) { DMWARN("not suspended during key manipulation."); return -EINVAL; } - if (argc == 3 && !strnicmp(argv[1], MESG_STR("set"))) { + if (argc == 3 && !strcasecmp(argv[1], "set")) { ret = crypt_set_key(cc, argv[2]); if (ret) return ret; @@ -1783,7 +1782,7 @@ static int crypt_message(struct dm_target *ti, unsigned argc, char **argv) ret = cc->iv_gen_ops->init(cc); return ret; } - if (argc == 2 && !strnicmp(argv[1], MESG_STR("wipe"))) { + if (argc == 2 && !strcasecmp(argv[1], "wipe")) { if (cc->iv_gen_ops && cc->iv_gen_ops->wipe) { ret = cc->iv_gen_ops->wipe(cc); if (ret) diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index adf851a081bd..5e0090ef4182 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -22,7 +22,6 @@ #include #define DM_MSG_PREFIX "multipath" -#define MESG_STR(x) x, sizeof(x) #define DM_PG_INIT_DELAY_MSECS 2000 #define DM_PG_INIT_DELAY_DEFAULT ((unsigned) -1) @@ -505,80 +504,29 @@ static void trigger_event(struct work_struct *work) * <#paths> <#per-path selector args> * [ []* ]+ ]+ *---------------------------------------------------------------*/ -struct param { - unsigned min; - unsigned max; - char *error; -}; - -static int read_param(struct param *param, char *str, unsigned *v, char **error) -{ - if (!str || - (sscanf(str, "%u", v) != 1) || - (*v < param->min) || - (*v > param->max)) { - *error = param->error; - return -EINVAL; - } - - return 0; -} - -struct arg_set { - unsigned argc; - char **argv; -}; - -static char *shift(struct arg_set *as) -{ - char *r; - - if (as->argc) { - as->argc--; - r = *as->argv; - as->argv++; - return r; - } - - return NULL; -} - -static void consume(struct arg_set *as, unsigned n) -{ - BUG_ON (as->argc < n); - as->argc -= n; - as->argv += n; -} - -static int parse_path_selector(struct arg_set *as, struct priority_group *pg, +static int parse_path_selector(struct dm_arg_set *as, struct priority_group *pg, struct dm_target *ti) { int r; struct path_selector_type *pst; unsigned ps_argc; - static struct param _params[] = { + static struct dm_arg _args[] = { {0, 1024, "invalid number of path selector args"}, }; - pst = dm_get_path_selector(shift(as)); + pst = dm_get_path_selector(dm_shift_arg(as)); if (!pst) { ti->error = "unknown path selector type"; return -EINVAL; } - r = read_param(_params, shift(as), &ps_argc, &ti->error); + r = dm_read_arg_group(_args, as, &ps_argc, &ti->error); if (r) { dm_put_path_selector(pst); return -EINVAL; } - if (ps_argc > as->argc) { - dm_put_path_selector(pst); - ti->error = "not enough arguments for path selector"; - return -EINVAL; - } - r = pst->create(&pg->ps, ps_argc, as->argv); if (r) { dm_put_path_selector(pst); @@ -587,12 +535,12 @@ static int parse_path_selector(struct arg_set *as, struct priority_group *pg, } pg->ps.type = pst; - consume(as, ps_argc); + dm_consume_args(as, ps_argc); return 0; } -static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps, +static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps, struct dm_target *ti) { int r; @@ -609,7 +557,7 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps, if (!p) return ERR_PTR(-ENOMEM); - r = dm_get_device(ti, shift(as), dm_table_get_mode(ti->table), + r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table), &p->path.dev); if (r) { ti->error = "error getting device"; @@ -660,16 +608,16 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps, return ERR_PTR(r); } -static struct priority_group *parse_priority_group(struct arg_set *as, +static struct priority_group *parse_priority_group(struct dm_arg_set *as, struct multipath *m) { - static struct param _params[] = { + static struct dm_arg _args[] = { {1, 1024, "invalid number of paths"}, {0, 1024, "invalid number of selector args"} }; int r; - unsigned i, nr_selector_args, nr_params; + unsigned i, nr_selector_args, nr_args; struct priority_group *pg; struct dm_target *ti = m->ti; @@ -693,26 +641,26 @@ static struct priority_group *parse_priority_group(struct arg_set *as, /* * read the paths */ - r = read_param(_params, shift(as), &pg->nr_pgpaths, &ti->error); + r = dm_read_arg(_args, as, &pg->nr_pgpaths, &ti->error); if (r) goto bad; - r = read_param(_params + 1, shift(as), &nr_selector_args, &ti->error); + r = dm_read_arg(_args + 1, as, &nr_selector_args, &ti->error); if (r) goto bad; - nr_params = 1 + nr_selector_args; + nr_args = 1 + nr_selector_args; for (i = 0; i < pg->nr_pgpaths; i++) { struct pgpath *pgpath; - struct arg_set path_args; + struct dm_arg_set path_args; - if (as->argc < nr_params) { + if (as->argc < nr_args) { ti->error = "not enough path parameters"; r = -EINVAL; goto bad; } - path_args.argc = nr_params; + path_args.argc = nr_args; path_args.argv = as->argv; pgpath = parse_path(&path_args, &pg->ps, ti); @@ -723,7 +671,7 @@ static struct priority_group *parse_priority_group(struct arg_set *as, pgpath->pg = pg; list_add_tail(&pgpath->list, &pg->pgpaths); - consume(as, nr_params); + dm_consume_args(as, nr_args); } return pg; @@ -733,28 +681,23 @@ static struct priority_group *parse_priority_group(struct arg_set *as, return ERR_PTR(r); } -static int parse_hw_handler(struct arg_set *as, struct multipath *m) +static int parse_hw_handler(struct dm_arg_set *as, struct multipath *m) { unsigned hw_argc; int ret; struct dm_target *ti = m->ti; - static struct param _params[] = { + static struct dm_arg _args[] = { {0, 1024, "invalid number of hardware handler args"}, }; - if (read_param(_params, shift(as), &hw_argc, &ti->error)) + if (dm_read_arg_group(_args, as, &hw_argc, &ti->error)) return -EINVAL; if (!hw_argc) return 0; - if (hw_argc > as->argc) { - ti->error = "not enough arguments for hardware handler"; - return -EINVAL; - } - - m->hw_handler_name = kstrdup(shift(as), GFP_KERNEL); + m->hw_handler_name = kstrdup(dm_shift_arg(as), GFP_KERNEL); request_module("scsi_dh_%s", m->hw_handler_name); if (scsi_dh_handler_exist(m->hw_handler_name) == 0) { ti->error = "unknown hardware handler type"; @@ -778,7 +721,7 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m) for (i = 0, p+=j+1; i <= hw_argc - 2; i++, p+=j+1) j = sprintf(p, "%s", as->argv[i]); } - consume(as, hw_argc - 1); + dm_consume_args(as, hw_argc - 1); return 0; fail: @@ -787,52 +730,45 @@ fail: return ret; } -static int parse_features(struct arg_set *as, struct multipath *m) +static int parse_features(struct dm_arg_set *as, struct multipath *m) { int r; unsigned argc; struct dm_target *ti = m->ti; - const char *param_name; + const char *arg_name; - static struct param _params[] = { + static struct dm_arg _args[] = { {0, 5, "invalid number of feature args"}, {1, 50, "pg_init_retries must be between 1 and 50"}, {0, 60000, "pg_init_delay_msecs must be between 0 and 60000"}, }; - r = read_param(_params, shift(as), &argc, &ti->error); + r = dm_read_arg_group(_args, as, &argc, &ti->error); if (r) return -EINVAL; if (!argc) return 0; - if (argc > as->argc) { - ti->error = "not enough arguments for features"; - return -EINVAL; - } - do { - param_name = shift(as); + arg_name = dm_shift_arg(as); argc--; - if (!strnicmp(param_name, MESG_STR("queue_if_no_path"))) { + if (!strcasecmp(arg_name, "queue_if_no_path")) { r = queue_if_no_path(m, 1, 0); continue; } - if (!strnicmp(param_name, MESG_STR("pg_init_retries")) && + if (!strcasecmp(arg_name, "pg_init_retries") && (argc >= 1)) { - r = read_param(_params + 1, shift(as), - &m->pg_init_retries, &ti->error); + r = dm_read_arg(_args + 1, as, &m->pg_init_retries, &ti->error); argc--; continue; } - if (!strnicmp(param_name, MESG_STR("pg_init_delay_msecs")) && + if (!strcasecmp(arg_name, "pg_init_delay_msecs") && (argc >= 1)) { - r = read_param(_params + 2, shift(as), - &m->pg_init_delay_msecs, &ti->error); + r = dm_read_arg(_args + 2, as, &m->pg_init_delay_msecs, &ti->error); argc--; continue; } @@ -847,15 +783,15 @@ static int parse_features(struct arg_set *as, struct multipath *m) static int multipath_ctr(struct dm_target *ti, unsigned int argc, char **argv) { - /* target parameters */ - static struct param _params[] = { + /* target arguments */ + static struct dm_arg _args[] = { {0, 1024, "invalid number of priority groups"}, {0, 1024, "invalid initial priority group number"}, }; int r; struct multipath *m; - struct arg_set as; + struct dm_arg_set as; unsigned pg_count = 0; unsigned next_pg_num; @@ -876,11 +812,11 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc, if (r) goto bad; - r = read_param(_params, shift(&as), &m->nr_priority_groups, &ti->error); + r = dm_read_arg(_args, &as, &m->nr_priority_groups, &ti->error); if (r) goto bad; - r = read_param(_params + 1, shift(&as), &next_pg_num, &ti->error); + r = dm_read_arg(_args + 1, &as, &next_pg_num, &ti->error); if (r) goto bad; @@ -1510,10 +1446,10 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv) } if (argc == 1) { - if (!strnicmp(argv[0], MESG_STR("queue_if_no_path"))) { + if (!strcasecmp(argv[0], "queue_if_no_path")) { r = queue_if_no_path(m, 1, 0); goto out; - } else if (!strnicmp(argv[0], MESG_STR("fail_if_no_path"))) { + } else if (!strcasecmp(argv[0], "fail_if_no_path")) { r = queue_if_no_path(m, 0, 0); goto out; } @@ -1524,18 +1460,18 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv) goto out; } - if (!strnicmp(argv[0], MESG_STR("disable_group"))) { + if (!strcasecmp(argv[0], "disable_group")) { r = bypass_pg_num(m, argv[1], 1); goto out; - } else if (!strnicmp(argv[0], MESG_STR("enable_group"))) { + } else if (!strcasecmp(argv[0], "enable_group")) { r = bypass_pg_num(m, argv[1], 0); goto out; - } else if (!strnicmp(argv[0], MESG_STR("switch_group"))) { + } else if (!strcasecmp(argv[0], "switch_group")) { r = switch_pg_num(m, argv[1]); goto out; - } else if (!strnicmp(argv[0], MESG_STR("reinstate_path"))) + } else if (!strcasecmp(argv[0], "reinstate_path")) action = reinstate_path; - else if (!strnicmp(argv[0], MESG_STR("fail_path"))) + else if (!strcasecmp(argv[0], "fail_path")) action = fail_path; else { DMWARN("Unrecognised multipath message received."); diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 03516c7f5be1..259ce99302fc 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -797,6 +797,63 @@ int dm_table_add_target(struct dm_table *t, const char *type, return r; } +/* + * Target argument parsing helpers. + */ +static int validate_next_arg(struct dm_arg *arg, struct dm_arg_set *arg_set, + unsigned *value, char **error, unsigned grouped) +{ + const char *arg_str = dm_shift_arg(arg_set); + + if (!arg_str || + (sscanf(arg_str, "%u", value) != 1) || + (*value < arg->min) || + (*value > arg->max) || + (grouped && arg_set->argc < *value)) { + *error = arg->error; + return -EINVAL; + } + + return 0; +} + +int dm_read_arg(struct dm_arg *arg, struct dm_arg_set *arg_set, + unsigned *value, char **error) +{ + return validate_next_arg(arg, arg_set, value, error, 0); +} +EXPORT_SYMBOL(dm_read_arg); + +int dm_read_arg_group(struct dm_arg *arg, struct dm_arg_set *arg_set, + unsigned *value, char **error) +{ + return validate_next_arg(arg, arg_set, value, error, 1); +} +EXPORT_SYMBOL(dm_read_arg_group); + +const char *dm_shift_arg(struct dm_arg_set *as) +{ + char *r; + + if (as->argc) { + as->argc--; + r = *as->argv; + as->argv++; + return r; + } + + return NULL; +} +EXPORT_SYMBOL(dm_shift_arg); + +void dm_consume_args(struct dm_arg_set *as, unsigned num_args) +{ + BUG_ON(as->argc < num_args); + as->argc -= num_args; + as->argv += num_args; +} +EXPORT_SYMBOL(dm_consume_args); + static int dm_table_set_type(struct dm_table *t) { unsigned i; diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 4427e0454051..3fa1f3d90ce0 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -208,6 +208,49 @@ struct dm_target_callbacks { int dm_register_target(struct target_type *t); void dm_unregister_target(struct target_type *t); +/* + * Target argument parsing. + */ +struct dm_arg_set { + unsigned argc; + char **argv; +}; + +/* + * The minimum and maximum value of a numeric argument, together with + * the error message to use if the number is found to be outside that range. + */ +struct dm_arg { + unsigned min; + unsigned max; + char *error; +}; + +/* + * Validate the next argument, either returning it as *value or, if invalid, + * returning -EINVAL and setting *error. + */ +int dm_read_arg(struct dm_arg *arg, struct dm_arg_set *arg_set, + unsigned *value, char **error); + +/* + * Process the next argument as the start of a group containing between + * arg->min and arg->max further arguments. Either return the size as + * *num_args or, if invalid, return -EINVAL and set *error. + */ +int dm_read_arg_group(struct dm_arg *arg, struct dm_arg_set *arg_set, + unsigned *num_args, char **error); + +/* + * Return the current argument and shift to the next. + */ +const char *dm_shift_arg(struct dm_arg_set *as); + +/* + * Move through num_args arguments. + */ +void dm_consume_args(struct dm_arg_set *as, unsigned num_args); + /*----------------------------------------------------------------- * Functions for creating and manipulating mapped devices. * Drop the reference with dm_put when you finish with the object. -- cgit v1.2.3 From 0ddf9644cc26e74ed671525e61a17bdbebf18da6 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Tue, 2 Aug 2011 12:32:06 +0100 Subject: dm ioctl: fill in device parameters in more ioctls Move parameter filling from find_device to __find_device_hash_cell. This patch causes ioctls using __find_device_hash_cell (DM_DEV_REMOVE_CMD, DM_DEV_SUSPEND_CMD - resume, DM_TABLE_CLEAR_CMD) to return device parameters, bringing them into line with the other ioctls. Signed-off-by: Mikulas Patocka Signed-off-by: Alasdair G Kergon --- drivers/md/dm-ioctl.c | 63 +++++++++++++++++++++++++++--------------------- include/linux/dm-ioctl.h | 4 +-- 2 files changed, 38 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 1622a6bc0bfd..99d38a6925a4 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -719,24 +719,49 @@ static int dev_create(struct dm_ioctl *param, size_t param_size) static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) { struct mapped_device *md; - void *mdptr = NULL; + struct hash_cell *hc = NULL; - if (*param->uuid) - return __get_uuid_cell(param->uuid); + if (*param->uuid) { + hc = __get_uuid_cell(param->uuid); + if (!hc) + return NULL; + goto fill_params; + } - if (*param->name) - return __get_name_cell(param->name); + if (*param->name) { + hc = __get_name_cell(param->name); + if (!hc) + return NULL; + goto fill_params; + } md = dm_get_md(huge_decode_dev(param->dev)); if (!md) - goto out; + return NULL; - mdptr = dm_get_mdptr(md); - if (!mdptr) + hc = dm_get_mdptr(md); + if (!hc) { dm_put(md); + return NULL; + } -out: - return mdptr; +fill_params: + /* + * Sneakily write in both the name and the uuid + * while we have the cell. + */ + strlcpy(param->name, hc->name, sizeof(param->name)); + if (hc->uuid) + strlcpy(param->uuid, hc->uuid, sizeof(param->uuid)); + else + param->uuid[0] = '\0'; + + if (hc->new_map) + param->flags |= DM_INACTIVE_PRESENT_FLAG; + else + param->flags &= ~DM_INACTIVE_PRESENT_FLAG; + + return hc; } static struct mapped_device *find_device(struct dm_ioctl *param) @@ -746,24 +771,8 @@ static struct mapped_device *find_device(struct dm_ioctl *param) down_read(&_hash_lock); hc = __find_device_hash_cell(param); - if (hc) { + if (hc) md = hc->md; - - /* - * Sneakily write in both the name and the uuid - * while we have the cell. - */ - strlcpy(param->name, hc->name, sizeof(param->name)); - if (hc->uuid) - strlcpy(param->uuid, hc->uuid, sizeof(param->uuid)); - else - param->uuid[0] = '\0'; - - if (hc->new_map) - param->flags |= DM_INACTIVE_PRESENT_FLAG; - else - param->flags &= ~DM_INACTIVE_PRESENT_FLAG; - } up_read(&_hash_lock); return md; diff --git a/include/linux/dm-ioctl.h b/include/linux/dm-ioctl.h index 3708455ee6c3..0cb8eff76bd6 100644 --- a/include/linux/dm-ioctl.h +++ b/include/linux/dm-ioctl.h @@ -267,9 +267,9 @@ enum { #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) #define DM_VERSION_MAJOR 4 -#define DM_VERSION_MINOR 20 +#define DM_VERSION_MINOR 21 #define DM_VERSION_PATCHLEVEL 0 -#define DM_VERSION_EXTRA "-ioctl (2011-02-02)" +#define DM_VERSION_EXTRA "-ioctl (2011-07-06)" /* Status bits */ #define DM_READONLY_FLAG (1 << 0) /* In/Out */ -- cgit v1.2.3 From aac285c6cb9622f1cc05ed162604bf5ad2da9a8d Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Tue, 2 Aug 2011 15:45:07 +0100 Subject: of: constify property name parameters for helper functions The helper functions for reading u32 integers, u32 arrays and strings should have the property name as a const pointer. Cc: Grant Likely Signed-off-by: Jamie Iles Signed-off-by: Grant Likely --- drivers/of/base.c | 7 ++++--- include/linux/of.h | 15 +++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/of/base.c b/drivers/of/base.c index 02ed36719def..3ff22e32b602 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -610,8 +610,9 @@ EXPORT_SYMBOL(of_find_node_by_phandle); * * The out_value is modified only if a valid u32 value can be decoded. */ -int of_property_read_u32_array(const struct device_node *np, char *propname, - u32 *out_values, size_t sz) +int of_property_read_u32_array(const struct device_node *np, + const char *propname, u32 *out_values, + size_t sz) { struct property *prop = of_find_property(np, propname, NULL); const __be32 *val; @@ -645,7 +646,7 @@ EXPORT_SYMBOL_GPL(of_property_read_u32_array); * * The out_string pointer is modified only if a valid string can be decoded. */ -int of_property_read_string(struct device_node *np, char *propname, +int of_property_read_string(struct device_node *np, const char *propname, const char **out_string) { struct property *prop = of_find_property(np, propname, NULL); diff --git a/include/linux/of.h b/include/linux/of.h index bd716f8908de..0085bb01c041 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -196,12 +196,13 @@ extern struct property *of_find_property(const struct device_node *np, const char *name, int *lenp); extern int of_property_read_u32_array(const struct device_node *np, - char *propname, + const char *propname, u32 *out_values, size_t sz); -extern int of_property_read_string(struct device_node *np, char *propname, - const char **out_string); +extern int of_property_read_string(struct device_node *np, + const char *propname, + const char **out_string); extern int of_device_is_compatible(const struct device_node *device, const char *); extern int of_device_is_available(const struct device_node *device); @@ -242,13 +243,15 @@ static inline bool of_have_populated_dt(void) } static inline int of_property_read_u32_array(const struct device_node *np, - char *propname, u32 *out_values, size_t sz) + const char *propname, + u32 *out_values, size_t sz) { return -ENOSYS; } static inline int of_property_read_string(struct device_node *np, - char *propname, const char **out_string) + const char *propname, + const char **out_string) { return -ENOSYS; } @@ -256,7 +259,7 @@ static inline int of_property_read_string(struct device_node *np, #endif /* CONFIG_OF */ static inline int of_property_read_u32(const struct device_node *np, - char *propname, + const char *propname, u32 *out_value) { return of_property_read_u32_array(np, propname, out_value, 1); -- cgit v1.2.3 From 31f5396ad3bde23c8416e8d23ba425e27f413314 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Jul 2011 13:48:42 -0700 Subject: thermal: make THERMAL_HWMON implementation fully internal THERMAL_HWMON is implemented inside the thermal_sys driver and has no effect on drivers implementing thermal zones, so they shouldn't see anything related to it in . Making the THERMAL_HWMON implementation fully internal has two advantages beyond the cleaner design: * This avoids rebuilding all thermal drivers if the THERMAL_HWMON implementation changes, or if CONFIG_THERMAL_HWMON gets enabled or disabled. * This avoids breaking the thermal kABI in these cases too, which should make distributions happy. The only drawback I can see is slightly higher memory fragmentation, as the number of kzalloc() calls will increase by one per thermal zone. But I doubt it will be a problem in practice, as I've never seen a system with more than two thermal zones. Signed-off-by: Jean Delvare Cc: Rene Herman Acked-by: Guenter Roeck Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/thermal/thermal_sys.c | 117 +++++++++++++++++++++++++++++++++--------- include/linux/thermal.h | 22 -------- 2 files changed, 92 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index b6353d61fac5..708f8e92771a 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -420,6 +420,29 @@ thermal_cooling_device_trip_point_show(struct device *dev, /* hwmon sys I/F */ #include + +/* thermal zone devices with the same type share one hwmon device */ +struct thermal_hwmon_device { + char type[THERMAL_NAME_LENGTH]; + struct device *device; + int count; + struct list_head tz_list; + struct list_head node; +}; + +struct thermal_hwmon_attr { + struct device_attribute attr; + char name[16]; +}; + +/* one temperature input for each thermal zone */ +struct thermal_hwmon_temp { + struct list_head hwmon_node; + struct thermal_zone_device *tz; + struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ + struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */ +}; + static LIST_HEAD(thermal_hwmon_list); static ssize_t @@ -437,9 +460,10 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf) int ret; struct thermal_hwmon_attr *hwmon_attr = container_of(attr, struct thermal_hwmon_attr, attr); - struct thermal_zone_device *tz - = container_of(hwmon_attr, struct thermal_zone_device, + struct thermal_hwmon_temp *temp + = container_of(hwmon_attr, struct thermal_hwmon_temp, temp_input); + struct thermal_zone_device *tz = temp->tz; ret = tz->ops->get_temp(tz, &temperature); @@ -455,9 +479,10 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, { struct thermal_hwmon_attr *hwmon_attr = container_of(attr, struct thermal_hwmon_attr, attr); - struct thermal_zone_device *tz - = container_of(hwmon_attr, struct thermal_zone_device, + struct thermal_hwmon_temp *temp + = container_of(hwmon_attr, struct thermal_hwmon_temp, temp_crit); + struct thermal_zone_device *tz = temp->tz; long temperature; int ret; @@ -485,10 +510,29 @@ thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz) return NULL; } +/* Find the temperature input matching a given thermal zone */ +static struct thermal_hwmon_temp * +thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon, + const struct thermal_zone_device *tz) +{ + struct thermal_hwmon_temp *temp; + + mutex_lock(&thermal_list_lock); + list_for_each_entry(temp, &hwmon->tz_list, hwmon_node) + if (temp->tz == tz) { + mutex_unlock(&thermal_list_lock); + return temp; + } + mutex_unlock(&thermal_list_lock); + + return NULL; +} + static int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) { struct thermal_hwmon_device *hwmon; + struct thermal_hwmon_temp *temp; int new_hwmon_device = 1; int result; @@ -515,30 +559,36 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) goto free_mem; register_sys_interface: - tz->hwmon = hwmon; + temp = kzalloc(sizeof(struct thermal_hwmon_temp), GFP_KERNEL); + if (!temp) { + result = -ENOMEM; + goto unregister_name; + } + + temp->tz = tz; hwmon->count++; - snprintf(tz->temp_input.name, THERMAL_NAME_LENGTH, + snprintf(temp->temp_input.name, THERMAL_NAME_LENGTH, "temp%d_input", hwmon->count); - tz->temp_input.attr.attr.name = tz->temp_input.name; - tz->temp_input.attr.attr.mode = 0444; - tz->temp_input.attr.show = temp_input_show; - sysfs_attr_init(&tz->temp_input.attr.attr); - result = device_create_file(hwmon->device, &tz->temp_input.attr); + temp->temp_input.attr.attr.name = temp->temp_input.name; + temp->temp_input.attr.attr.mode = 0444; + temp->temp_input.attr.show = temp_input_show; + sysfs_attr_init(&temp->temp_input.attr.attr); + result = device_create_file(hwmon->device, &temp->temp_input.attr); if (result) - goto unregister_name; + goto free_temp_mem; if (tz->ops->get_crit_temp) { unsigned long temperature; if (!tz->ops->get_crit_temp(tz, &temperature)) { - snprintf(tz->temp_crit.name, THERMAL_NAME_LENGTH, + snprintf(temp->temp_crit.name, THERMAL_NAME_LENGTH, "temp%d_crit", hwmon->count); - tz->temp_crit.attr.attr.name = tz->temp_crit.name; - tz->temp_crit.attr.attr.mode = 0444; - tz->temp_crit.attr.show = temp_crit_show; - sysfs_attr_init(&tz->temp_crit.attr.attr); + temp->temp_crit.attr.attr.name = temp->temp_crit.name; + temp->temp_crit.attr.attr.mode = 0444; + temp->temp_crit.attr.show = temp_crit_show; + sysfs_attr_init(&temp->temp_crit.attr.attr); result = device_create_file(hwmon->device, - &tz->temp_crit.attr); + &temp->temp_crit.attr); if (result) goto unregister_input; } @@ -547,13 +597,15 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) mutex_lock(&thermal_list_lock); if (new_hwmon_device) list_add_tail(&hwmon->node, &thermal_hwmon_list); - list_add_tail(&tz->hwmon_node, &hwmon->tz_list); + list_add_tail(&temp->hwmon_node, &hwmon->tz_list); mutex_unlock(&thermal_list_lock); return 0; unregister_input: - device_remove_file(hwmon->device, &tz->temp_input.attr); + device_remove_file(hwmon->device, &temp->temp_input.attr); + free_temp_mem: + kfree(temp); unregister_name: if (new_hwmon_device) { device_remove_file(hwmon->device, &dev_attr_name); @@ -569,15 +621,30 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) static void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) { - struct thermal_hwmon_device *hwmon = tz->hwmon; + struct thermal_hwmon_device *hwmon; + struct thermal_hwmon_temp *temp; + + hwmon = thermal_hwmon_lookup_by_type(tz); + if (unlikely(!hwmon)) { + /* Should never happen... */ + dev_dbg(&tz->device, "hwmon device lookup failed!\n"); + return; + } + + temp = thermal_hwmon_lookup_temp(hwmon, tz); + if (unlikely(!temp)) { + /* Should never happen... */ + dev_dbg(&tz->device, "temperature input lookup failed!\n"); + return; + } - tz->hwmon = NULL; - device_remove_file(hwmon->device, &tz->temp_input.attr); + device_remove_file(hwmon->device, &temp->temp_input.attr); if (tz->ops->get_crit_temp) - device_remove_file(hwmon->device, &tz->temp_crit.attr); + device_remove_file(hwmon->device, &temp->temp_crit.attr); mutex_lock(&thermal_list_lock); - list_del(&tz->hwmon_node); + list_del(&temp->hwmon_node); + kfree(temp); if (!list_empty(&hwmon->tz_list)) { mutex_unlock(&thermal_list_lock); return; diff --git a/include/linux/thermal.h b/include/linux/thermal.h index d3ec89fb4122..47b4a27e6e97 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -85,22 +85,6 @@ struct thermal_cooling_device { ((long)t-2732+5)/10 : ((long)t-2732-5)/10) #define CELSIUS_TO_KELVIN(t) ((t)*10+2732) -#if defined(CONFIG_THERMAL_HWMON) -/* thermal zone devices with the same type share one hwmon device */ -struct thermal_hwmon_device { - char type[THERMAL_NAME_LENGTH]; - struct device *device; - int count; - struct list_head tz_list; - struct list_head node; -}; - -struct thermal_hwmon_attr { - struct device_attribute attr; - char name[16]; -}; -#endif - struct thermal_zone_device { int id; char type[THERMAL_NAME_LENGTH]; @@ -120,12 +104,6 @@ struct thermal_zone_device { struct mutex lock; /* protect cooling devices list */ struct list_head node; struct delayed_work poll_queue; -#if defined(CONFIG_THERMAL_HWMON) - struct list_head hwmon_node; - struct thermal_hwmon_device *hwmon; - struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ - struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */ -#endif }; /* Adding event notification support elements */ #define THERMAL_GENL_FAMILY_NAME "thermal_event" -- cgit v1.2.3 From f52e00c668669c9c290e84adf859c76db6d92a5a Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Thu, 28 Jul 2011 13:48:43 -0700 Subject: ACPI: remove NID_INVAL b552a8c56db8 ("ACPI: remove NID_INVAL") removed the left over uses of NID_INVAL, but didn't actually remove the definition. Remove it. Signed-off-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- include/linux/acpi.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 1deb2a73c2da..2312e850aab8 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -238,7 +238,6 @@ extern int acpi_paddr_to_node(u64 start_addr, u64 size); extern int pnpacpi_disabled; #define PXM_INVAL (-1) -#define NID_INVAL (-1) int acpi_check_resource_conflict(const struct resource *res); -- cgit v1.2.3 From 951c0d660a7c35286e401ca6d6ef38c9d49643c7 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 3 Aug 2011 00:47:21 -0400 Subject: get rid of boilerplate switches in posix_acl.h the only potentially subtle thing here: get_cached_acl() is never called with the second argument other than ACL_TYPE_{ACCESS,DEFAULT}. IOW, that return ERR_PTR(-EINVAL) in there might as well be BUG(). Signed-off-by: Al Viro --- include/linux/posix_acl.h | 62 +++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h index 951bba82d50d..a9c2fb29be96 100644 --- a/include/linux/posix_acl.h +++ b/include/linux/posix_acl.h @@ -84,20 +84,22 @@ extern struct posix_acl *get_posix_acl(struct inode *, int); extern int set_posix_acl(struct inode *, int, struct posix_acl *); #ifdef CONFIG_FS_POSIX_ACL -static inline struct posix_acl *get_cached_acl(struct inode *inode, int type) +static inline struct posix_acl **acl_by_type(struct inode *inode, int type) { - struct posix_acl **p, *acl; switch (type) { case ACL_TYPE_ACCESS: - p = &inode->i_acl; - break; + return &inode->i_acl; case ACL_TYPE_DEFAULT: - p = &inode->i_default_acl; - break; + return &inode->i_default_acl; default: - return ERR_PTR(-EINVAL); + BUG(); } - acl = ACCESS_ONCE(*p); +} + +static inline struct posix_acl *get_cached_acl(struct inode *inode, int type) +{ + struct posix_acl **p = acl_by_type(inode, type); + struct posix_acl *acl = ACCESS_ONCE(*p); if (acl) { spin_lock(&inode->i_lock); acl = *p; @@ -110,18 +112,8 @@ static inline struct posix_acl *get_cached_acl(struct inode *inode, int type) static inline int negative_cached_acl(struct inode *inode, int type) { - struct posix_acl **p, *acl; - switch (type) { - case ACL_TYPE_ACCESS: - p = &inode->i_acl; - break; - case ACL_TYPE_DEFAULT: - p = &inode->i_default_acl; - break; - default: - BUG(); - } - acl = ACCESS_ONCE(*p); + struct posix_acl **p = acl_by_type(inode, type); + struct posix_acl *acl = ACCESS_ONCE(*p); if (acl) return 0; return 1; @@ -131,18 +123,11 @@ static inline void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl) { - struct posix_acl *old = NULL; + struct posix_acl **p = acl_by_type(inode, type); + struct posix_acl *old; spin_lock(&inode->i_lock); - switch (type) { - case ACL_TYPE_ACCESS: - old = inode->i_acl; - inode->i_acl = posix_acl_dup(acl); - break; - case ACL_TYPE_DEFAULT: - old = inode->i_default_acl; - inode->i_default_acl = posix_acl_dup(acl); - break; - } + old = *p; + *p = posix_dup_acl(acl); spin_unlock(&inode->i_lock); if (old != ACL_NOT_CACHED) posix_acl_release(old); @@ -150,18 +135,11 @@ static inline void set_cached_acl(struct inode *inode, static inline void forget_cached_acl(struct inode *inode, int type) { - struct posix_acl *old = NULL; + struct posix_acl **p = acl_by_type(inode, type); + struct posix_acl *old; spin_lock(&inode->i_lock); - switch (type) { - case ACL_TYPE_ACCESS: - old = inode->i_acl; - inode->i_acl = ACL_NOT_CACHED; - break; - case ACL_TYPE_DEFAULT: - old = inode->i_default_acl; - inode->i_default_acl = ACL_NOT_CACHED; - break; - } + old = *p; + *p = ACL_NOT_CACHED; spin_unlock(&inode->i_lock); if (old != ACL_NOT_CACHED) posix_acl_release(old); -- cgit v1.2.3 From 3567866bf26190d1e734c975c907eb06e923ba23 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 2 Aug 2011 21:32:13 -0400 Subject: RCUify freeing acls, let check_acl() go ahead in RCU mode if acl is cached Signed-off-by: Al Viro --- fs/namei.c | 17 ++++++----------- include/linux/posix_acl.h | 18 +++++++++--------- 2 files changed, 15 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/fs/namei.c b/fs/namei.c index 445fd5da11fa..3d607bd80e09 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -179,19 +179,14 @@ static int check_acl(struct inode *inode, int mask) #ifdef CONFIG_FS_POSIX_ACL struct posix_acl *acl; - /* - * Under RCU walk, we cannot even do a "get_cached_acl()", - * because that involves locking and getting a refcount on - * a cached ACL. - * - * So the only case we handle during RCU walking is the - * case of a cached "no ACL at all", which needs no locks - * or refcounts. - */ if (mask & MAY_NOT_BLOCK) { - if (negative_cached_acl(inode, ACL_TYPE_ACCESS)) + acl = get_cached_acl_rcu(inode, ACL_TYPE_ACCESS); + if (!acl) return -EAGAIN; - return -ECHILD; + /* no ->get_acl() calls in RCU mode... */ + if (acl == ACL_NOT_CACHED) + return -ECHILD; + return posix_acl_permission(inode, acl, mask); } acl = get_cached_acl(inode, ACL_TYPE_ACCESS); diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h index a9c2fb29be96..b7681102a4b9 100644 --- a/include/linux/posix_acl.h +++ b/include/linux/posix_acl.h @@ -9,6 +9,7 @@ #define __LINUX_POSIX_ACL_H #include +#include #define ACL_UNDEFINED_ID (-1) @@ -38,7 +39,10 @@ struct posix_acl_entry { }; struct posix_acl { - atomic_t a_refcount; + union { + atomic_t a_refcount; + struct rcu_head a_rcu; + }; unsigned int a_count; struct posix_acl_entry a_entries[0]; }; @@ -65,7 +69,7 @@ static inline void posix_acl_release(struct posix_acl *acl) { if (acl && atomic_dec_and_test(&acl->a_refcount)) - kfree(acl); + kfree_rcu(acl, a_rcu); } @@ -110,13 +114,9 @@ static inline struct posix_acl *get_cached_acl(struct inode *inode, int type) return acl; } -static inline int negative_cached_acl(struct inode *inode, int type) +static inline struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type) { - struct posix_acl **p = acl_by_type(inode, type); - struct posix_acl *acl = ACCESS_ONCE(*p); - if (acl) - return 0; - return 1; + return rcu_dereference(*acl_by_type(inode, type)); } static inline void set_cached_acl(struct inode *inode, @@ -127,7 +127,7 @@ static inline void set_cached_acl(struct inode *inode, struct posix_acl *old; spin_lock(&inode->i_lock); old = *p; - *p = posix_dup_acl(acl); + rcu_assign_pointer(*p, posix_acl_dup(acl)); spin_unlock(&inode->i_lock); if (old != ACL_NOT_CACHED) posix_acl_release(old); -- cgit v1.2.3 From f2c31e32b378a6653f8de606149d963baf11d7d3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 29 Jul 2011 19:00:53 +0000 Subject: net: fix NULL dereferences in check_peer_redir() Gergely Kalman reported crashes in check_peer_redir(). It appears commit f39925dbde778 (ipv4: Cache learned redirect information in inetpeer.) added a race, leading to possible NULL ptr dereference. Since we can now change dst neighbour, we should make sure a reader can safely use a neighbour. Add RCU protection to dst neighbour, and make sure check_peer_redir() can be called safely by different cpus in parallel. As neighbours are already freed after one RCU grace period, this patch should not add typical RCU penalty (cache cold effects) Many thanks to Gergely for providing a pretty report pointing to the bug. Reported-by: Gergely Kalman Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/dst.h | 17 +++++++++++++---- net/ipv4/ip_output.c | 10 ++++++++-- net/ipv4/route.c | 14 ++++++++------ net/ipv6/addrconf.c | 2 +- net/ipv6/ip6_fib.c | 2 +- net/ipv6/ip6_output.c | 13 +++++++++++-- net/ipv6/route.c | 35 +++++++++++++++++++++++++---------- 7 files changed, 67 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 29e255796ce1..13d507d69ddb 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -37,7 +37,7 @@ struct dst_entry { unsigned long _metrics; unsigned long expires; struct dst_entry *path; - struct neighbour *_neighbour; + struct neighbour __rcu *_neighbour; #ifdef CONFIG_XFRM struct xfrm_state *xfrm; #else @@ -88,12 +88,17 @@ struct dst_entry { static inline struct neighbour *dst_get_neighbour(struct dst_entry *dst) { - return dst->_neighbour; + return rcu_dereference(dst->_neighbour); +} + +static inline struct neighbour *dst_get_neighbour_raw(struct dst_entry *dst) +{ + return rcu_dereference_raw(dst->_neighbour); } static inline void dst_set_neighbour(struct dst_entry *dst, struct neighbour *neigh) { - dst->_neighbour = neigh; + rcu_assign_pointer(dst->_neighbour, neigh); } extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old); @@ -382,8 +387,12 @@ static inline void dst_rcu_free(struct rcu_head *head) static inline void dst_confirm(struct dst_entry *dst) { if (dst) { - struct neighbour *n = dst_get_neighbour(dst); + struct neighbour *n; + + rcu_read_lock(); + n = dst_get_neighbour(dst); neigh_confirm(n); + rcu_read_unlock(); } } diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index ccaaa851ab42..77d3eded665a 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -204,9 +204,15 @@ static inline int ip_finish_output2(struct sk_buff *skb) skb = skb2; } + rcu_read_lock(); neigh = dst_get_neighbour(dst); - if (neigh) - return neigh_output(neigh, skb); + if (neigh) { + int res = neigh_output(neigh, skb); + + rcu_read_unlock(); + return res; + } + rcu_read_unlock(); if (net_ratelimit()) printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n"); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 1730689f560e..6afc4eb50591 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1628,16 +1628,18 @@ static int check_peer_redir(struct dst_entry *dst, struct inet_peer *peer) { struct rtable *rt = (struct rtable *) dst; __be32 orig_gw = rt->rt_gateway; - struct neighbour *n; + struct neighbour *n, *old_n; dst_confirm(&rt->dst); - neigh_release(dst_get_neighbour(&rt->dst)); - dst_set_neighbour(&rt->dst, NULL); - rt->rt_gateway = peer->redirect_learned.a4; - rt_bind_neighbour(rt); - n = dst_get_neighbour(&rt->dst); + + n = ipv4_neigh_lookup(&rt->dst, &rt->rt_gateway); + if (IS_ERR(n)) + return PTR_ERR(n); + old_n = xchg(&rt->dst._neighbour, n); + if (old_n) + neigh_release(old_n); if (!n || !(n->nud_state & NUD_VALID)) { if (n) neigh_event_send(n, NULL); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index a55500cc0b29..f012ebd87b43 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -656,7 +656,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, * layer address of our nexhop router */ - if (dst_get_neighbour(&rt->dst) == NULL) + if (dst_get_neighbour_raw(&rt->dst) == NULL) ifa->flags &= ~IFA_F_OPTIMISTIC; ifa->idev = idev; diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 54a4678955bf..320d91d20ad7 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1455,7 +1455,7 @@ static int fib6_age(struct rt6_info *rt, void *arg) RT6_TRACE("aging clone %p\n", rt); return -1; } else if ((rt->rt6i_flags & RTF_GATEWAY) && - (!(dst_get_neighbour(&rt->dst)->flags & NTF_ROUTER))) { + (!(dst_get_neighbour_raw(&rt->dst)->flags & NTF_ROUTER))) { RT6_TRACE("purging route %p via non-router but gateway\n", rt); return -1; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 32e5339db0c8..4c882cf4e8a1 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -135,10 +135,15 @@ static int ip6_finish_output2(struct sk_buff *skb) skb->len); } + rcu_read_lock(); neigh = dst_get_neighbour(dst); - if (neigh) - return neigh_output(neigh, skb); + if (neigh) { + int res = neigh_output(neigh, skb); + rcu_read_unlock(); + return res; + } + rcu_read_unlock(); IP6_INC_STATS_BH(dev_net(dst->dev), ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); kfree_skb(skb); @@ -975,12 +980,14 @@ static int ip6_dst_lookup_tail(struct sock *sk, * dst entry and replace it instead with the * dst entry of the nexthop router */ + rcu_read_lock(); n = dst_get_neighbour(*dst); if (n && !(n->nud_state & NUD_VALID)) { struct inet6_ifaddr *ifp; struct flowi6 fl_gw6; int redirect; + rcu_read_unlock(); ifp = ipv6_get_ifaddr(net, &fl6->saddr, (*dst)->dev, 1); @@ -1000,6 +1007,8 @@ static int ip6_dst_lookup_tail(struct sock *sk, if ((err = (*dst)->error)) goto out_err_release; } + } else { + rcu_read_unlock(); } #endif diff --git a/net/ipv6/route.c b/net/ipv6/route.c index e8987da06667..9e69eb0ec6dd 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -364,7 +364,7 @@ out: #ifdef CONFIG_IPV6_ROUTER_PREF static void rt6_probe(struct rt6_info *rt) { - struct neighbour *neigh = rt ? dst_get_neighbour(&rt->dst) : NULL; + struct neighbour *neigh; /* * Okay, this does not seem to be appropriate * for now, however, we need to check if it @@ -373,8 +373,10 @@ static void rt6_probe(struct rt6_info *rt) * Router Reachability Probe MUST be rate-limited * to no more than one per minute. */ + rcu_read_lock(); + neigh = rt ? dst_get_neighbour(&rt->dst) : NULL; if (!neigh || (neigh->nud_state & NUD_VALID)) - return; + goto out; read_lock_bh(&neigh->lock); if (!(neigh->nud_state & NUD_VALID) && time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { @@ -387,8 +389,11 @@ static void rt6_probe(struct rt6_info *rt) target = (struct in6_addr *)&neigh->primary_key; addrconf_addr_solict_mult(target, &mcaddr); ndisc_send_ns(rt->rt6i_dev, NULL, target, &mcaddr, NULL); - } else + } else { read_unlock_bh(&neigh->lock); + } +out: + rcu_read_unlock(); } #else static inline void rt6_probe(struct rt6_info *rt) @@ -412,8 +417,11 @@ static inline int rt6_check_dev(struct rt6_info *rt, int oif) static inline int rt6_check_neigh(struct rt6_info *rt) { - struct neighbour *neigh = dst_get_neighbour(&rt->dst); + struct neighbour *neigh; int m; + + rcu_read_lock(); + neigh = dst_get_neighbour(&rt->dst); if (rt->rt6i_flags & RTF_NONEXTHOP || !(rt->rt6i_flags & RTF_GATEWAY)) m = 1; @@ -430,6 +438,7 @@ static inline int rt6_check_neigh(struct rt6_info *rt) read_unlock_bh(&neigh->lock); } else m = 0; + rcu_read_unlock(); return m; } @@ -769,7 +778,7 @@ static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, rt->rt6i_dst.plen = 128; rt->rt6i_flags |= RTF_CACHE; rt->dst.flags |= DST_HOST; - dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour(&ort->dst))); + dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_raw(&ort->dst))); } return rt; } @@ -803,7 +812,7 @@ restart: dst_hold(&rt->dst); read_unlock_bh(&table->tb6_lock); - if (!dst_get_neighbour(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP)) + if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP)) nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); else if (!(rt->dst.flags & DST_HOST)) nrt = rt6_alloc_clone(rt, &fl6->daddr); @@ -1587,7 +1596,7 @@ void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, dst_confirm(&rt->dst); /* Duplicate redirect: silently ignore. */ - if (neigh == dst_get_neighbour(&rt->dst)) + if (neigh == dst_get_neighbour_raw(&rt->dst)) goto out; nrt = ip6_rt_copy(rt, dest); @@ -1682,7 +1691,7 @@ again: 1. It is connected route. Action: COW 2. It is gatewayed route or NONEXTHOP route. Action: clone it. */ - if (!dst_get_neighbour(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP)) + if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP)) nrt = rt6_alloc_cow(rt, daddr, saddr); else nrt = rt6_alloc_clone(rt, daddr); @@ -2326,6 +2335,7 @@ static int rt6_fill_node(struct net *net, struct nlmsghdr *nlh; long expires; u32 table; + struct neighbour *n; if (prefix) { /* user wants prefix routes only */ if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { @@ -2414,8 +2424,11 @@ static int rt6_fill_node(struct net *net, if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) goto nla_put_failure; - if (dst_get_neighbour(&rt->dst)) - NLA_PUT(skb, RTA_GATEWAY, 16, &dst_get_neighbour(&rt->dst)->primary_key); + rcu_read_lock(); + n = dst_get_neighbour(&rt->dst); + if (n) + NLA_PUT(skb, RTA_GATEWAY, 16, &n->primary_key); + rcu_read_unlock(); if (rt->dst.dev) NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex); @@ -2608,12 +2621,14 @@ static int rt6_info_route(struct rt6_info *rt, void *p_arg) #else seq_puts(m, "00000000000000000000000000000000 00 "); #endif + rcu_read_lock(); n = dst_get_neighbour(&rt->dst); if (n) { seq_printf(m, "%pi6", n->primary_key); } else { seq_puts(m, "00000000000000000000000000000000"); } + rcu_read_unlock(); seq_printf(m, " %08x %08x %08x %08x %8s\n", rt->rt6i_metric, atomic_read(&rt->dst.__refcnt), rt->dst.__use, rt->rt6i_flags, -- cgit v1.2.3 From 750f463a749e28464151ad26938d11b07b1c43cb Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 3 Aug 2011 11:28:14 +0100 Subject: dt: add of_alias_scan and of_alias_get_id The patch adds function of_alias_scan to populate a global lookup table with the properties of 'aliases' node and function of_alias_get_id for drivers to find alias id from the lookup table. Signed-off-by: Shawn Guo [grant.likely: add locking and rework parse loop] Signed-off-by: Grant Likely --- drivers/of/base.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/of/fdt.c | 4 +- include/linux/of.h | 8 +++ include/linux/of_fdt.h | 1 - 4 files changed, 141 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/of/base.c b/drivers/of/base.c index 3ff22e32b602..fb28b5af733b 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -17,14 +17,39 @@ * 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 +/** + * struct alias_prop - Alias property in 'aliases' node + * @link: List node to link the structure in aliases_lookup list + * @alias: Alias property name + * @np: Pointer to device_node that the alias stands for + * @id: Index value from end of alias name + * @stem: Alias string without the index + * + * The structure represents one alias property of 'aliases' node as + * an entry in aliases_lookup list. + */ +struct alias_prop { + struct list_head link; + const char *alias; + struct device_node *np; + int id; + char stem[0]; +}; + +static LIST_HEAD(aliases_lookup); + struct device_node *allnodes; struct device_node *of_chosen; +struct device_node *of_aliases; + +static DEFINE_MUTEX(of_aliases_mutex); /* use when traversing tree through the allnext, child, sibling, * or parent members of struct device_node. @@ -988,3 +1013,108 @@ out_unlock: } #endif /* defined(CONFIG_OF_DYNAMIC) */ +static void of_alias_add(struct alias_prop *ap, struct device_node *np, + int id, const char *stem, int stem_len) +{ + ap->id = id; + ap->np = np; + strncpy(ap->stem, stem, stem_len); + ap->stem[stem_len] = 0; + list_add_tail(&ap->link, &aliases_lookup); + pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n", + ap->alias, ap->stem, ap->id, np ? np->full_name : NULL); +} + +/** + * of_alias_scan() - Scan all properties of 'aliases' node + * + * The function scans all the properties of 'aliases' node and populate + * the global lookup table with the properties. It returns the + * number of alias_prop found, or error code in error case. + */ +__init void of_alias_scan(void) +{ + struct property *pp; + + if (!of_aliases) + return; + + for_each_property(pp, of_aliases->properties) { + const char *start = pp->name; + const char *end = start + strlen(start); + struct device_node *np; + struct alias_prop *ap; + int id, len; + + /* Skip those we do not want to proceed */ + if (!strcmp(pp->name, "name") || + !strcmp(pp->name, "phandle") || + !strcmp(pp->name, "linux,phandle")) + continue; + + np = of_find_node_by_path(pp->value); + if (!np) + continue; + + /* walk alias backwards to extract the id and 'stem' string */ + while (isdigit(*(end-1)) && end > start) + end--; + len = end - start; + id = strlen(end) ? simple_strtoul(end, NULL, 10) : -1; + + /* Allocate an alias_prop with enough space for the stem */ + ap = early_init_dt_alloc_memory_arch(sizeof(*ap) + len + 1, 4); + if (!ap) + continue; + ap->alias = start; + of_alias_add(ap, np, id, start, len); + } +} + +/** + * of_alias_get_id() - Get alias id for the given device_node + * @np: Pointer to the given device_node + * @stem: Alias stem of the given device_node + * + * The function travels the lookup table to get alias id for the given + * device_node and alias stem. It returns the alias id if find it. + * If not, dynamically creates one in the lookup table and returns it, + * or returns error code if fail to create. + */ +int of_alias_get_id(struct device_node *np, const char *stem) +{ + struct alias_prop *app; + int id = 0; + bool found = false; + + mutex_lock(&of_aliases_mutex); + list_for_each_entry(app, &aliases_lookup, link) { + if (strcmp(app->stem, stem) != 0) + continue; + + if (np == app->np) { + found = true; + id = app->id; + break; + } + + if (id <= app->id) + id = app->id + 1; + } + + /* If an id is not found, then allocate a new one */ + if (!found) { + app = kzalloc(sizeof(*app) + strlen(stem) + 1, 4); + if (!app) { + id = -ENODEV; + goto out; + } + of_alias_add(app, np, id, stem, strlen(stem)); + } + + out: + mutex_unlock(&of_aliases_mutex); + + return id; +} +EXPORT_SYMBOL_GPL(of_alias_get_id); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 65200af29c52..13d6d3a96b31 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -707,10 +707,12 @@ void __init unflatten_device_tree(void) __unflatten_device_tree(initial_boot_params, &allnodes, early_init_dt_alloc_memory_arch); - /* Get pointer to OF "/chosen" node for use everywhere */ + /* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */ of_chosen = of_find_node_by_path("/chosen"); if (of_chosen == NULL) of_chosen = of_find_node_by_path("/chosen@0"); + of_aliases = of_find_node_by_path("/aliases"); + of_alias_scan(); } #endif /* CONFIG_OF_EARLY_FLATTREE */ diff --git a/include/linux/of.h b/include/linux/of.h index 0085bb01c041..bc3dc6399547 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -68,6 +68,7 @@ struct device_node { /* Pointer for first entry in chain of all nodes. */ extern struct device_node *allnodes; extern struct device_node *of_chosen; +extern struct device_node *of_aliases; extern rwlock_t devtree_lock; static inline bool of_have_populated_dt(void) @@ -209,6 +210,9 @@ extern int of_device_is_available(const struct device_node *device); extern const void *of_get_property(const struct device_node *node, const char *name, int *lenp); +#define for_each_property(pp, properties) \ + for (pp = properties; pp != NULL; pp = pp->next) + extern int of_n_addr_cells(struct device_node *np); extern int of_n_size_cells(struct device_node *np); extern const struct of_device_id *of_match_node( @@ -221,6 +225,10 @@ extern int of_parse_phandles_with_args(struct device_node *np, const char *list_name, const char *cells_name, int index, struct device_node **out_node, const void **out_args); +extern void *early_init_dt_alloc_memory_arch(u64 size, u64 align); +extern void of_alias_scan(void); +extern int of_alias_get_id(struct device_node *np, const char *stem); + extern int of_machine_is_compatible(const char *compat); extern int prom_add_property(struct device_node* np, struct property* prop); diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index c84d900fbbb3..b74b74ffe0e7 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -97,7 +97,6 @@ extern void early_init_dt_check_for_initrd(unsigned long node); extern int early_init_dt_scan_memory(unsigned long node, const char *uname, int depth, void *data); extern void early_init_dt_add_memory_arch(u64 base, u64 size); -extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align); extern u64 dt_mem_next_cell(int s, __be32 **cellp); /* -- cgit v1.2.3 From f49f23abf3dd786ddcac1c1e7db3c2013b07413f Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Wed, 13 Jul 2011 13:14:23 +0800 Subject: lib, Add lock-less NULL terminated single list Cmpxchg is used to implement adding new entry to the list, deleting all entries from the list, deleting first entry of the list and some other operations. Because this is a single list, so the tail can not be accessed in O(1). If there are multiple producers and multiple consumers, llist_add can be used in producers and llist_del_all can be used in consumers. They can work simultaneously without lock. But llist_del_first can not be used here. Because llist_del_first depends on list->first->next does not changed if list->first is not changed during its operation, but llist_del_first, llist_add, llist_add (or llist_del_all, llist_add, llist_add) sequence in another consumer may violate that. If there are multiple producers and one consumer, llist_add can be used in producers and llist_del_all or llist_del_first can be used in the consumer. This can be summarized as follow: | add | del_first | del_all add | - | - | - del_first | | L | L del_all | | | - Where "-" stands for no lock is needed, while "L" stands for lock is needed. The list entries deleted via llist_del_all can be traversed with traversing function such as llist_for_each etc. But the list entries can not be traversed safely before deleted from the list. The order of deleted entries is from the newest to the oldest added one. If you want to traverse from the oldest to the newest, you must reverse the order by yourself before traversing. The basic atomic operation of this list is cmpxchg on long. On architectures that don't have NMI-safe cmpxchg implementation, the list can NOT be used in NMI handler. So code uses the list in NMI handler should depend on CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG. Signed-off-by: Huang Ying Reviewed-by: Andi Kleen Reviewed-by: Mathieu Desnoyers Cc: Andrew Morton Signed-off-by: Len Brown --- include/linux/llist.h | 126 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/Kconfig | 3 ++ lib/Makefile | 2 + lib/llist.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 include/linux/llist.h create mode 100644 lib/llist.c (limited to 'include') diff --git a/include/linux/llist.h b/include/linux/llist.h new file mode 100644 index 000000000000..aa0c8b5b3cd0 --- /dev/null +++ b/include/linux/llist.h @@ -0,0 +1,126 @@ +#ifndef LLIST_H +#define LLIST_H +/* + * Lock-less NULL terminated single linked list + * + * If there are multiple producers and multiple consumers, llist_add + * can be used in producers and llist_del_all can be used in + * consumers. They can work simultaneously without lock. But + * llist_del_first can not be used here. Because llist_del_first + * depends on list->first->next does not changed if list->first is not + * changed during its operation, but llist_del_first, llist_add, + * llist_add (or llist_del_all, llist_add, llist_add) sequence in + * another consumer may violate that. + * + * If there are multiple producers and one consumer, llist_add can be + * used in producers and llist_del_all or llist_del_first can be used + * in the consumer. + * + * This can be summarized as follow: + * + * | add | del_first | del_all + * add | - | - | - + * del_first | | L | L + * del_all | | | - + * + * Where "-" stands for no lock is needed, while "L" stands for lock + * is needed. + * + * The list entries deleted via llist_del_all can be traversed with + * traversing function such as llist_for_each etc. But the list + * entries can not be traversed safely before deleted from the list. + * The order of deleted entries is from the newest to the oldest added + * one. If you want to traverse from the oldest to the newest, you + * must reverse the order by yourself before traversing. + * + * The basic atomic operation of this list is cmpxchg on long. On + * architectures that don't have NMI-safe cmpxchg implementation, the + * list can NOT be used in NMI handler. So code uses the list in NMI + * handler should depend on CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG. + */ + +struct llist_head { + struct llist_node *first; +}; + +struct llist_node { + struct llist_node *next; +}; + +#define LLIST_HEAD_INIT(name) { NULL } +#define LLIST_HEAD(name) struct llist_head name = LLIST_HEAD_INIT(name) + +/** + * init_llist_head - initialize lock-less list head + * @head: the head for your lock-less list + */ +static inline void init_llist_head(struct llist_head *list) +{ + list->first = NULL; +} + +/** + * llist_entry - get the struct of this entry + * @ptr: the &struct llist_node pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the llist_node within the struct. + */ +#define llist_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * llist_for_each - iterate over some deleted entries of a lock-less list + * @pos: the &struct llist_node to use as a loop cursor + * @node: the first entry of deleted list entries + * + * In general, some entries of the lock-less list can be traversed + * safely only after being deleted from list, so start with an entry + * instead of list head. + * + * If being used on entries deleted from lock-less list directly, the + * traverse order is from the newest to the oldest added entry. If + * you want to traverse from the oldest to the newest, you must + * reverse the order by yourself before traversing. + */ +#define llist_for_each(pos, node) \ + for ((pos) = (node); pos; (pos) = (pos)->next) + +/** + * llist_for_each_entry - iterate over some deleted entries of lock-less list of given type + * @pos: the type * to use as a loop cursor. + * @node: the fist entry of deleted list entries. + * @member: the name of the llist_node with the struct. + * + * In general, some entries of the lock-less list can be traversed + * safely only after being removed from list, so start with an entry + * instead of list head. + * + * If being used on entries deleted from lock-less list directly, the + * traverse order is from the newest to the oldest added entry. If + * you want to traverse from the oldest to the newest, you must + * reverse the order by yourself before traversing. + */ +#define llist_for_each_entry(pos, node, member) \ + for ((pos) = llist_entry((node), typeof(*(pos)), member); \ + &(pos)->member != NULL; \ + (pos) = llist_entry((pos)->member.next, typeof(*(pos)), member)) + +/** + * llist_empty - tests whether a lock-less list is empty + * @head: the list to test + * + * Not guaranteed to be accurate or up to date. Just a quick way to + * test whether the list is empty without deleting something from the + * list. + */ +static inline int llist_empty(const struct llist_head *head) +{ + return ACCESS_ONCE(head->first) == NULL; +} + +void llist_add(struct llist_node *new, struct llist_head *head); +void llist_add_batch(struct llist_node *new_first, struct llist_node *new_last, + struct llist_head *head); +struct llist_node *llist_del_first(struct llist_head *head); +struct llist_node *llist_del_all(struct llist_head *head); +#endif /* LLIST_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 830181cc7a83..25c19678a30f 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -262,4 +262,7 @@ config AVERAGE If unsure, say N. +config LLIST + bool + endmenu diff --git a/lib/Makefile b/lib/Makefile index 6b597fdb1898..d770b817202e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -112,6 +112,8 @@ obj-$(CONFIG_AVERAGE) += average.o obj-$(CONFIG_CPU_RMAP) += cpu_rmap.o +obj-$(CONFIG_LLIST) += llist.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/llist.c b/lib/llist.c new file mode 100644 index 000000000000..da445724fa1f --- /dev/null +++ b/lib/llist.c @@ -0,0 +1,129 @@ +/* + * Lock-less NULL terminated single linked list + * + * The basic atomic operation of this list is cmpxchg on long. On + * architectures that don't have NMI-safe cmpxchg implementation, the + * list can NOT be used in NMI handler. So code uses the list in NMI + * handler should depend on CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG. + * + * Copyright 2010,2011 Intel Corp. + * Author: Huang Ying + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include + +#include + +/** + * llist_add - add a new entry + * @new: new entry to be added + * @head: the head for your lock-less list + */ +void llist_add(struct llist_node *new, struct llist_head *head) +{ + struct llist_node *entry, *old_entry; + +#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG + BUG_ON(in_nmi()); +#endif + + entry = head->first; + do { + old_entry = entry; + new->next = entry; + cpu_relax(); + } while ((entry = cmpxchg(&head->first, old_entry, new)) != old_entry); +} +EXPORT_SYMBOL_GPL(llist_add); + +/** + * llist_add_batch - add several linked entries in batch + * @new_first: first entry in batch to be added + * @new_last: last entry in batch to be added + * @head: the head for your lock-less list + */ +void llist_add_batch(struct llist_node *new_first, struct llist_node *new_last, + struct llist_head *head) +{ + struct llist_node *entry, *old_entry; + +#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG + BUG_ON(in_nmi()); +#endif + + entry = head->first; + do { + old_entry = entry; + new_last->next = entry; + cpu_relax(); + } while ((entry = cmpxchg(&head->first, old_entry, new_first)) != old_entry); +} +EXPORT_SYMBOL_GPL(llist_add_batch); + +/** + * llist_del_first - delete the first entry of lock-less list + * @head: the head for your lock-less list + * + * If list is empty, return NULL, otherwise, return the first entry + * deleted, this is the newest added one. + * + * Only one llist_del_first user can be used simultaneously with + * multiple llist_add users without lock. Because otherwise + * llist_del_first, llist_add, llist_add (or llist_del_all, llist_add, + * llist_add) sequence in another user may change @head->first->next, + * but keep @head->first. If multiple consumers are needed, please + * use llist_del_all or use lock between consumers. + */ +struct llist_node *llist_del_first(struct llist_head *head) +{ + struct llist_node *entry, *old_entry, *next; + +#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG + BUG_ON(in_nmi()); +#endif + + entry = head->first; + do { + if (entry == NULL) + return NULL; + old_entry = entry; + next = entry->next; + cpu_relax(); + } while ((entry = cmpxchg(&head->first, old_entry, next)) != old_entry); + + return entry; +} +EXPORT_SYMBOL_GPL(llist_del_first); + +/** + * llist_del_all - delete all entries from lock-less list + * @head: the head of lock-less list to delete all entries + * + * If list is empty, return NULL, otherwise, delete all entries and + * return the pointer to the first entry. The order of entries + * deleted is from the newest to the oldest added one. + */ +struct llist_node *llist_del_all(struct llist_head *head) +{ +#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG + BUG_ON(in_nmi()); +#endif + + return xchg(&head->first, NULL); +} +EXPORT_SYMBOL_GPL(llist_del_all); -- cgit v1.2.3 From 7f184275aa306046fe7edcbef3229754f0d97402 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Wed, 13 Jul 2011 13:14:24 +0800 Subject: lib, Make gen_pool memory allocator lockless This version of the gen_pool memory allocator supports lockless operation. This makes it safe to use in NMI handlers and other special unblockable contexts that could otherwise deadlock on locks. This is implemented by using atomic operations and retries on any conflicts. The disadvantage is that there may be livelocks in extreme cases. For better scalability, one gen_pool allocator can be used for each CPU. The lockless operation only works if there is enough memory available. If new memory is added to the pool a lock has to be still taken. So any user relying on locklessness has to ensure that sufficient memory is preallocated. The basic atomic operation of this allocator is cmpxchg on long. On architectures that don't have NMI-safe cmpxchg implementation, the allocator can NOT be used in NMI handler. So code uses the allocator in NMI handler should depend on CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG. Signed-off-by: Huang Ying Reviewed-by: Andi Kleen Reviewed-by: Mathieu Desnoyers Cc: Andrew Morton Signed-off-by: Len Brown --- include/linux/bitmap.h | 1 + include/linux/genalloc.h | 34 +++++- lib/bitmap.c | 2 - lib/genalloc.c | 300 ++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 272 insertions(+), 65 deletions(-) (limited to 'include') diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index dcafe0bf0005..907dd58aa228 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -145,6 +145,7 @@ extern void bitmap_release_region(unsigned long *bitmap, int pos, int order); extern int bitmap_allocate_region(unsigned long *bitmap, int pos, int order); extern void bitmap_copy_le(void *dst, const unsigned long *src, int nbits); +#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG)) #define BITMAP_LAST_WORD_MASK(nbits) \ ( \ ((nbits) % BITS_PER_LONG) ? \ diff --git a/include/linux/genalloc.h b/include/linux/genalloc.h index 5bbebda78b02..5e98eeb2af3b 100644 --- a/include/linux/genalloc.h +++ b/include/linux/genalloc.h @@ -1,8 +1,26 @@ /* - * Basic general purpose allocator for managing special purpose memory - * not managed by the regular kmalloc/kfree interface. - * Uses for this includes on-device special memory, uncached memory - * etc. + * Basic general purpose allocator for managing special purpose + * memory, for example, memory that is not managed by the regular + * kmalloc/kfree interface. Uses for this includes on-device special + * memory, uncached memory etc. + * + * It is safe to use the allocator in NMI handlers and other special + * unblockable contexts that could otherwise deadlock on locks. This + * is implemented by using atomic operations and retries on any + * conflicts. The disadvantage is that there may be livelocks in + * extreme cases. For better scalability, one allocator can be used + * for each CPU. + * + * The lockless operation only works if there is enough memory + * available. If new memory is added to the pool a lock has to be + * still taken. So any user relying on locklessness has to ensure + * that sufficient memory is preallocated. + * + * The basic atomic operation of this allocator is cmpxchg on long. + * On architectures that don't have NMI-safe cmpxchg implementation, + * the allocator can NOT be used in NMI handler. So code uses the + * allocator in NMI handler should depend on + * CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG. * * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. @@ -15,7 +33,7 @@ * General purpose special memory pool descriptor. */ struct gen_pool { - rwlock_t lock; + spinlock_t lock; struct list_head chunks; /* list of chunks in this pool */ int min_alloc_order; /* minimum allocation order */ }; @@ -24,8 +42,8 @@ struct gen_pool { * General purpose special memory pool chunk descriptor. */ struct gen_pool_chunk { - spinlock_t lock; struct list_head next_chunk; /* next chunk in pool */ + atomic_t avail; phys_addr_t phys_addr; /* physical starting address of memory chunk */ unsigned long start_addr; /* starting address of memory chunk */ unsigned long end_addr; /* ending address of memory chunk */ @@ -56,4 +74,8 @@ static inline int gen_pool_add(struct gen_pool *pool, unsigned long addr, extern void gen_pool_destroy(struct gen_pool *); extern unsigned long gen_pool_alloc(struct gen_pool *, size_t); extern void gen_pool_free(struct gen_pool *, unsigned long, size_t); +extern void gen_pool_for_each_chunk(struct gen_pool *, + void (*)(struct gen_pool *, struct gen_pool_chunk *, void *), void *); +extern size_t gen_pool_avail(struct gen_pool *); +extern size_t gen_pool_size(struct gen_pool *); #endif /* __GENALLOC_H__ */ diff --git a/lib/bitmap.c b/lib/bitmap.c index 3f3b68199d74..e3c9e999501e 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c @@ -271,8 +271,6 @@ int __bitmap_weight(const unsigned long *bitmap, int bits) } EXPORT_SYMBOL(__bitmap_weight); -#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG)) - void bitmap_set(unsigned long *map, int start, int nr) { unsigned long *p = map + BIT_WORD(start); diff --git a/lib/genalloc.c b/lib/genalloc.c index 577ddf805975..f352cc42f4f8 100644 --- a/lib/genalloc.c +++ b/lib/genalloc.c @@ -1,8 +1,26 @@ /* - * Basic general purpose allocator for managing special purpose memory - * not managed by the regular kmalloc/kfree interface. - * Uses for this includes on-device special memory, uncached memory - * etc. + * Basic general purpose allocator for managing special purpose + * memory, for example, memory that is not managed by the regular + * kmalloc/kfree interface. Uses for this includes on-device special + * memory, uncached memory etc. + * + * It is safe to use the allocator in NMI handlers and other special + * unblockable contexts that could otherwise deadlock on locks. This + * is implemented by using atomic operations and retries on any + * conflicts. The disadvantage is that there may be livelocks in + * extreme cases. For better scalability, one allocator can be used + * for each CPU. + * + * The lockless operation only works if there is enough memory + * available. If new memory is added to the pool a lock has to be + * still taken. So any user relying on locklessness has to ensure + * that sufficient memory is preallocated. + * + * The basic atomic operation of this allocator is cmpxchg on long. + * On architectures that don't have NMI-safe cmpxchg implementation, + * the allocator can NOT be used in NMI handler. So code uses the + * allocator in NMI handler should depend on + * CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG. * * Copyright 2005 (C) Jes Sorensen * @@ -13,8 +31,109 @@ #include #include #include +#include +#include #include +static int set_bits_ll(unsigned long *addr, unsigned long mask_to_set) +{ + unsigned long val, nval; + + nval = *addr; + do { + val = nval; + if (val & mask_to_set) + return -EBUSY; + cpu_relax(); + } while ((nval = cmpxchg(addr, val, val | mask_to_set)) != val); + + return 0; +} + +static int clear_bits_ll(unsigned long *addr, unsigned long mask_to_clear) +{ + unsigned long val, nval; + + nval = *addr; + do { + val = nval; + if ((val & mask_to_clear) != mask_to_clear) + return -EBUSY; + cpu_relax(); + } while ((nval = cmpxchg(addr, val, val & ~mask_to_clear)) != val); + + return 0; +} + +/* + * bitmap_set_ll - set the specified number of bits at the specified position + * @map: pointer to a bitmap + * @start: a bit position in @map + * @nr: number of bits to set + * + * Set @nr bits start from @start in @map lock-lessly. Several users + * can set/clear the same bitmap simultaneously without lock. If two + * users set the same bit, one user will return remain bits, otherwise + * return 0. + */ +static int bitmap_set_ll(unsigned long *map, int start, int nr) +{ + unsigned long *p = map + BIT_WORD(start); + const int size = start + nr; + int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG); + unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start); + + while (nr - bits_to_set >= 0) { + if (set_bits_ll(p, mask_to_set)) + return nr; + nr -= bits_to_set; + bits_to_set = BITS_PER_LONG; + mask_to_set = ~0UL; + p++; + } + if (nr) { + mask_to_set &= BITMAP_LAST_WORD_MASK(size); + if (set_bits_ll(p, mask_to_set)) + return nr; + } + + return 0; +} + +/* + * bitmap_clear_ll - clear the specified number of bits at the specified position + * @map: pointer to a bitmap + * @start: a bit position in @map + * @nr: number of bits to set + * + * Clear @nr bits start from @start in @map lock-lessly. Several users + * can set/clear the same bitmap simultaneously without lock. If two + * users clear the same bit, one user will return remain bits, + * otherwise return 0. + */ +static int bitmap_clear_ll(unsigned long *map, int start, int nr) +{ + unsigned long *p = map + BIT_WORD(start); + const int size = start + nr; + int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); + unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); + + while (nr - bits_to_clear >= 0) { + if (clear_bits_ll(p, mask_to_clear)) + return nr; + nr -= bits_to_clear; + bits_to_clear = BITS_PER_LONG; + mask_to_clear = ~0UL; + p++; + } + if (nr) { + mask_to_clear &= BITMAP_LAST_WORD_MASK(size); + if (clear_bits_ll(p, mask_to_clear)) + return nr; + } + + return 0; +} /** * gen_pool_create - create a new special memory pool @@ -30,7 +149,7 @@ struct gen_pool *gen_pool_create(int min_alloc_order, int nid) pool = kmalloc_node(sizeof(struct gen_pool), GFP_KERNEL, nid); if (pool != NULL) { - rwlock_init(&pool->lock); + spin_lock_init(&pool->lock); INIT_LIST_HEAD(&pool->chunks); pool->min_alloc_order = min_alloc_order; } @@ -63,14 +182,14 @@ int gen_pool_add_virt(struct gen_pool *pool, unsigned long virt, phys_addr_t phy if (unlikely(chunk == NULL)) return -ENOMEM; - spin_lock_init(&chunk->lock); chunk->phys_addr = phys; chunk->start_addr = virt; chunk->end_addr = virt + size; + atomic_set(&chunk->avail, size); - write_lock(&pool->lock); - list_add(&chunk->next_chunk, &pool->chunks); - write_unlock(&pool->lock); + spin_lock(&pool->lock); + list_add_rcu(&chunk->next_chunk, &pool->chunks); + spin_unlock(&pool->lock); return 0; } @@ -85,19 +204,19 @@ EXPORT_SYMBOL(gen_pool_add_virt); */ phys_addr_t gen_pool_virt_to_phys(struct gen_pool *pool, unsigned long addr) { - struct list_head *_chunk; struct gen_pool_chunk *chunk; + phys_addr_t paddr = -1; - read_lock(&pool->lock); - list_for_each(_chunk, &pool->chunks) { - chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); - - if (addr >= chunk->start_addr && addr < chunk->end_addr) - return chunk->phys_addr + addr - chunk->start_addr; + rcu_read_lock(); + list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) { + if (addr >= chunk->start_addr && addr < chunk->end_addr) { + paddr = chunk->phys_addr + (addr - chunk->start_addr); + break; + } } - read_unlock(&pool->lock); + rcu_read_unlock(); - return -1; + return paddr; } EXPORT_SYMBOL(gen_pool_virt_to_phys); @@ -115,7 +234,6 @@ void gen_pool_destroy(struct gen_pool *pool) int order = pool->min_alloc_order; int bit, end_bit; - list_for_each_safe(_chunk, _next_chunk, &pool->chunks) { chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); list_del(&chunk->next_chunk); @@ -137,44 +255,50 @@ EXPORT_SYMBOL(gen_pool_destroy); * @size: number of bytes to allocate from the pool * * Allocate the requested number of bytes from the specified pool. - * Uses a first-fit algorithm. + * Uses a first-fit algorithm. Can not be used in NMI handler on + * architectures without NMI-safe cmpxchg implementation. */ unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size) { - struct list_head *_chunk; struct gen_pool_chunk *chunk; - unsigned long addr, flags; + unsigned long addr = 0; int order = pool->min_alloc_order; - int nbits, start_bit, end_bit; + int nbits, start_bit = 0, end_bit, remain; + +#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG + BUG_ON(in_nmi()); +#endif if (size == 0) return 0; nbits = (size + (1UL << order) - 1) >> order; - - read_lock(&pool->lock); - list_for_each(_chunk, &pool->chunks) { - chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); + rcu_read_lock(); + list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) { + if (size > atomic_read(&chunk->avail)) + continue; end_bit = (chunk->end_addr - chunk->start_addr) >> order; - - spin_lock_irqsave(&chunk->lock, flags); - start_bit = bitmap_find_next_zero_area(chunk->bits, end_bit, 0, - nbits, 0); - if (start_bit >= end_bit) { - spin_unlock_irqrestore(&chunk->lock, flags); +retry: + start_bit = bitmap_find_next_zero_area(chunk->bits, end_bit, + start_bit, nbits, 0); + if (start_bit >= end_bit) continue; + remain = bitmap_set_ll(chunk->bits, start_bit, nbits); + if (remain) { + remain = bitmap_clear_ll(chunk->bits, start_bit, + nbits - remain); + BUG_ON(remain); + goto retry; } addr = chunk->start_addr + ((unsigned long)start_bit << order); - - bitmap_set(chunk->bits, start_bit, nbits); - spin_unlock_irqrestore(&chunk->lock, flags); - read_unlock(&pool->lock); - return addr; + size = nbits << order; + atomic_sub(size, &chunk->avail); + break; } - read_unlock(&pool->lock); - return 0; + rcu_read_unlock(); + return addr; } EXPORT_SYMBOL(gen_pool_alloc); @@ -184,33 +308,95 @@ EXPORT_SYMBOL(gen_pool_alloc); * @addr: starting address of memory to free back to pool * @size: size in bytes of memory to free * - * Free previously allocated special memory back to the specified pool. + * Free previously allocated special memory back to the specified + * pool. Can not be used in NMI handler on architectures without + * NMI-safe cmpxchg implementation. */ void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size) { - struct list_head *_chunk; struct gen_pool_chunk *chunk; - unsigned long flags; int order = pool->min_alloc_order; - int bit, nbits; + int start_bit, nbits, remain; - nbits = (size + (1UL << order) - 1) >> order; - - read_lock(&pool->lock); - list_for_each(_chunk, &pool->chunks) { - chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); +#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG + BUG_ON(in_nmi()); +#endif + nbits = (size + (1UL << order) - 1) >> order; + rcu_read_lock(); + list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) { if (addr >= chunk->start_addr && addr < chunk->end_addr) { BUG_ON(addr + size > chunk->end_addr); - spin_lock_irqsave(&chunk->lock, flags); - bit = (addr - chunk->start_addr) >> order; - while (nbits--) - __clear_bit(bit++, chunk->bits); - spin_unlock_irqrestore(&chunk->lock, flags); - break; + start_bit = (addr - chunk->start_addr) >> order; + remain = bitmap_clear_ll(chunk->bits, start_bit, nbits); + BUG_ON(remain); + size = nbits << order; + atomic_add(size, &chunk->avail); + rcu_read_unlock(); + return; } } - BUG_ON(nbits > 0); - read_unlock(&pool->lock); + rcu_read_unlock(); + BUG(); } EXPORT_SYMBOL(gen_pool_free); + +/** + * gen_pool_for_each_chunk - call func for every chunk of generic memory pool + * @pool: the generic memory pool + * @func: func to call + * @data: additional data used by @func + * + * Call @func for every chunk of generic memory pool. The @func is + * called with rcu_read_lock held. + */ +void gen_pool_for_each_chunk(struct gen_pool *pool, + void (*func)(struct gen_pool *pool, struct gen_pool_chunk *chunk, void *data), + void *data) +{ + struct gen_pool_chunk *chunk; + + rcu_read_lock(); + list_for_each_entry_rcu(chunk, &(pool)->chunks, next_chunk) + func(pool, chunk, data); + rcu_read_unlock(); +} +EXPORT_SYMBOL(gen_pool_for_each_chunk); + +/** + * gen_pool_avail - get available free space of the pool + * @pool: pool to get available free space + * + * Return available free space of the specified pool. + */ +size_t gen_pool_avail(struct gen_pool *pool) +{ + struct gen_pool_chunk *chunk; + size_t avail = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) + avail += atomic_read(&chunk->avail); + rcu_read_unlock(); + return avail; +} +EXPORT_SYMBOL_GPL(gen_pool_avail); + +/** + * gen_pool_size - get size in bytes of memory managed by the pool + * @pool: pool to get size + * + * Return size in bytes of memory managed by the pool. + */ +size_t gen_pool_size(struct gen_pool *pool) +{ + struct gen_pool_chunk *chunk; + size_t size = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) + size += chunk->end_addr - chunk->start_addr; + rcu_read_unlock(); + return size; +} +EXPORT_SYMBOL_GPL(gen_pool_size); -- cgit v1.2.3 From ea8f5fb8a71fddaf5f3a17100d3247855701f732 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Wed, 13 Jul 2011 13:14:27 +0800 Subject: HWPoison: add memory_failure_queue() memory_failure() is the entry point for HWPoison memory error recovery. It must be called in process context. But commonly hardware memory errors are notified via MCE or NMI, so some delayed execution mechanism must be used. In MCE handler, a work queue + ring buffer mechanism is used. In addition to MCE, now APEI (ACPI Platform Error Interface) GHES (Generic Hardware Error Source) can be used to report memory errors too. To add support to APEI GHES memory recovery, a mechanism similar to that of MCE is implemented. memory_failure_queue() is the new entry point that can be called in IRQ context. The next step is to make MCE handler uses this interface too. Signed-off-by: Huang Ying Cc: Andi Kleen Cc: Wu Fengguang Cc: Andrew Morton Signed-off-by: Len Brown --- include/linux/mm.h | 1 + mm/memory-failure.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) (limited to 'include') diff --git a/include/linux/mm.h b/include/linux/mm.h index 9670f71d7be9..303108eed28c 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1633,6 +1633,7 @@ enum mf_flags { }; extern void memory_failure(unsigned long pfn, int trapno); extern int __memory_failure(unsigned long pfn, int trapno, int flags); +extern void memory_failure_queue(unsigned long pfn, int trapno, int flags); extern int unpoison_memory(unsigned long pfn); extern int sysctl_memory_failure_early_kill; extern int sysctl_memory_failure_recovery; diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 740c4f52059c..2b43ba051ac9 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -53,6 +53,7 @@ #include #include #include +#include #include "internal.h" int sysctl_memory_failure_early_kill __read_mostly = 0; @@ -1178,6 +1179,97 @@ void memory_failure(unsigned long pfn, int trapno) __memory_failure(pfn, trapno, 0); } +#define MEMORY_FAILURE_FIFO_ORDER 4 +#define MEMORY_FAILURE_FIFO_SIZE (1 << MEMORY_FAILURE_FIFO_ORDER) + +struct memory_failure_entry { + unsigned long pfn; + int trapno; + int flags; +}; + +struct memory_failure_cpu { + DECLARE_KFIFO(fifo, struct memory_failure_entry, + MEMORY_FAILURE_FIFO_SIZE); + spinlock_t lock; + struct work_struct work; +}; + +static DEFINE_PER_CPU(struct memory_failure_cpu, memory_failure_cpu); + +/** + * memory_failure_queue - Schedule handling memory failure of a page. + * @pfn: Page Number of the corrupted page + * @trapno: Trap number reported in the signal to user space. + * @flags: Flags for memory failure handling + * + * This function is called by the low level hardware error handler + * when it detects hardware memory corruption of a page. It schedules + * the recovering of error page, including dropping pages, killing + * processes etc. + * + * The function is primarily of use for corruptions that + * happen outside the current execution context (e.g. when + * detected by a background scrubber) + * + * Can run in IRQ context. + */ +void memory_failure_queue(unsigned long pfn, int trapno, int flags) +{ + struct memory_failure_cpu *mf_cpu; + unsigned long proc_flags; + struct memory_failure_entry entry = { + .pfn = pfn, + .trapno = trapno, + .flags = flags, + }; + + mf_cpu = &get_cpu_var(memory_failure_cpu); + spin_lock_irqsave(&mf_cpu->lock, proc_flags); + if (kfifo_put(&mf_cpu->fifo, &entry)) + schedule_work_on(smp_processor_id(), &mf_cpu->work); + else + pr_err("Memory failure: buffer overflow when queuing memory failure at 0x%#lx\n", + pfn); + spin_unlock_irqrestore(&mf_cpu->lock, proc_flags); + put_cpu_var(memory_failure_cpu); +} +EXPORT_SYMBOL_GPL(memory_failure_queue); + +static void memory_failure_work_func(struct work_struct *work) +{ + struct memory_failure_cpu *mf_cpu; + struct memory_failure_entry entry = { 0, }; + unsigned long proc_flags; + int gotten; + + mf_cpu = &__get_cpu_var(memory_failure_cpu); + for (;;) { + spin_lock_irqsave(&mf_cpu->lock, proc_flags); + gotten = kfifo_get(&mf_cpu->fifo, &entry); + spin_unlock_irqrestore(&mf_cpu->lock, proc_flags); + if (!gotten) + break; + __memory_failure(entry.pfn, entry.trapno, entry.flags); + } +} + +static int __init memory_failure_init(void) +{ + struct memory_failure_cpu *mf_cpu; + int cpu; + + for_each_possible_cpu(cpu) { + mf_cpu = &per_cpu(memory_failure_cpu, cpu); + spin_lock_init(&mf_cpu->lock); + INIT_KFIFO(mf_cpu->fifo); + INIT_WORK(&mf_cpu->work, memory_failure_work_func); + } + + return 0; +} +core_initcall(memory_failure_init); + /** * unpoison_memory - Unpoison a previously poisoned page * @pfn: Page number of the to be unpoisoned page -- cgit v1.2.3 From a7e09d450b2e0b068e850d103b6ee1af537d1910 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sat, 16 Jul 2011 18:14:21 -0400 Subject: ACPI: APEI build fix as GHES is optional... When # CONFIG_ACPI_APEI_GHES is not set: (.init.text+0x4c22): undefined reference to `ghes_disable' Reported-by: Randy Dunlap Acked-by: Randy Dunlap Signed-off-by: Len Brown --- drivers/acpi/bus.c | 2 -- include/acpi/apei.h | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 43ce3b0c4921..437ddbf0c49a 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -544,10 +544,8 @@ static void acpi_bus_osc_support(void) capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_PPC_OST_SUPPORT; #endif -#ifdef CONFIG_ACPI_APEI_GHES if (!ghes_disable) capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_APEI_SUPPORT; -#endif if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) return; if (ACPI_SUCCESS(acpi_run_osc(handle, &context))) { diff --git a/include/acpi/apei.h b/include/acpi/apei.h index d40bc5521fcb..51a527d24a8a 100644 --- a/include/acpi/apei.h +++ b/include/acpi/apei.h @@ -18,7 +18,11 @@ extern int hest_disable; extern int erst_disable; +#ifdef CONFIG_ACPI_APEI_GHES extern int ghes_disable; +#else +#define ghes_disable 1 +#endif #ifdef CONFIG_ACPI_APEI void __init acpi_hest_init(void); -- cgit v1.2.3 From d91ee5863b71e8c90eaf6035bff3078a85e2e7b5 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 1 Apr 2011 18:28:35 -0400 Subject: cpuidle: replace xen access to x86 pm_idle and default_idle When a Xen Dom0 kernel boots on a hypervisor, it gets access to the raw-hardware ACPI tables. While it parses the idle tables for the hypervisor's beneift, it uses HLT for its own idle. Rather than have xen scribble on pm_idle and access default_idle, have it simply disable_cpuidle() so acpi_idle will not load and architecture default HLT will be used. cc: xen-devel@lists.xensource.com Tested-by: Konrad Rzeszutek Wilk Acked-by: H. Peter Anvin Signed-off-by: Len Brown --- arch/x86/xen/setup.c | 3 ++- drivers/cpuidle/cpuidle.c | 4 ++++ include/linux/cpuidle.h | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index 60aeeb56948f..a9627e2e3295 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -426,7 +427,7 @@ void __init xen_arch_setup(void) #ifdef CONFIG_X86_32 boot_cpu_data.hlt_works_ok = 1; #endif - pm_idle = default_idle; + disable_cpuidle(); boot_option_idle_override = IDLE_HALT; fiddle_vdso(); diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index faae2c357bab..041df0b056b2 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -34,6 +34,10 @@ int cpuidle_disabled(void) { return off; } +void disable_cpuidle(void) +{ + off = 1; +} #if defined(CONFIG_ARCH_HAS_CPU_IDLE_WAIT) static void cpuidle_kick_cpus(void) diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 36719ead50e8..b89f67da919e 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -122,6 +122,7 @@ struct cpuidle_driver { }; #ifdef CONFIG_CPU_IDLE +extern void disable_cpuidle(void); extern int cpuidle_register_driver(struct cpuidle_driver *drv); struct cpuidle_driver *cpuidle_get_driver(void); @@ -135,6 +136,7 @@ extern int cpuidle_enable_device(struct cpuidle_device *dev); extern void cpuidle_disable_device(struct cpuidle_device *dev); #else +static inline void disable_cpuidle(void) { } static inline int cpuidle_register_driver(struct cpuidle_driver *drv) {return -ENODEV; } -- cgit v1.2.3 From a0bfa1373859e9d11dc92561a8667588803e42d8 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 1 Apr 2011 19:34:59 -0400 Subject: cpuidle: stop depending on pm_idle cpuidle users should call cpuidle_call_idle() directly rather than via (pm_idle)() function pointer. Architecture may choose to continue using (pm_idle)(), but cpuidle need not depend on it: my_arch_cpu_idle() ... if(cpuidle_call_idle()) pm_idle(); cc: Kevin Hilman cc: Paul Mundt cc: x86@kernel.org Acked-by: H. Peter Anvin Signed-off-by: Len Brown --- arch/arm/kernel/process.c | 4 +++- arch/sh/kernel/idle.c | 6 ++++-- arch/x86/kernel/process_32.c | 4 +++- arch/x86/kernel/process_64.c | 4 +++- drivers/cpuidle/cpuidle.c | 38 ++++++++++++++++++-------------------- include/linux/cpuidle.h | 2 ++ 6 files changed, 33 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 5e1e54197227..d7ee0d4c072d 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -196,7 +197,8 @@ void cpu_idle(void) cpu_relax(); } else { stop_critical_timings(); - pm_idle(); + if (cpuidle_call_idle()) + pm_idle(); start_critical_timings(); /* * This will eventually be removed - pm_idle diff --git a/arch/sh/kernel/idle.c b/arch/sh/kernel/idle.c index 425d604e3a28..9c7099ebfe14 100644 --- a/arch/sh/kernel/idle.c +++ b/arch/sh/kernel/idle.c @@ -16,12 +16,13 @@ #include #include #include +#include #include #include #include #include -void (*pm_idle)(void) = NULL; +static void (*pm_idle)(void); static int hlt_counter; @@ -100,7 +101,8 @@ void cpu_idle(void) local_irq_disable(); /* Don't trace irqs off for idle */ stop_critical_timings(); - pm_idle(); + if (cpuidle_call_idle()) + pm_idle(); /* * Sanity check to ensure that pm_idle() returns * with IRQs enabled diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index a3d0dc59067b..7a3b65107a27 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -109,7 +110,8 @@ void cpu_idle(void) local_irq_disable(); /* Don't trace irqs off for idle */ stop_critical_timings(); - pm_idle(); + if (cpuidle_idle_call()) + pm_idle(); start_critical_timings(); } tick_nohz_restart_sched_tick(); diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index ca6f7ab8df33..f693e44e1bf6 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -136,7 +137,8 @@ void cpu_idle(void) enter_idle(); /* Don't trace irqs off for idle */ stop_critical_timings(); - pm_idle(); + if (cpuidle_idle_call()) + pm_idle(); start_critical_timings(); /* In many cases the interrupt that ended idle diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 041df0b056b2..d4c542372886 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -25,10 +25,10 @@ DEFINE_PER_CPU(struct cpuidle_device *, cpuidle_devices); DEFINE_MUTEX(cpuidle_lock); LIST_HEAD(cpuidle_detected_devices); -static void (*pm_idle_old)(void); static int enabled_devices; static int off __read_mostly; +static int initialized __read_mostly; int cpuidle_disabled(void) { @@ -56,25 +56,23 @@ static int __cpuidle_register_device(struct cpuidle_device *dev); * cpuidle_idle_call - the main idle loop * * NOTE: no locks or semaphores should be used here + * return non-zero on failure */ -static void cpuidle_idle_call(void) +int cpuidle_idle_call(void) { struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); struct cpuidle_state *target_state; int next_state; + if (off) + return -ENODEV; + + if (!initialized) + return -ENODEV; + /* check if the device is ready */ - if (!dev || !dev->enabled) { - if (pm_idle_old) - pm_idle_old(); - else -#if defined(CONFIG_ARCH_HAS_DEFAULT_IDLE) - default_idle(); -#else - local_irq_enable(); -#endif - return; - } + if (!dev || !dev->enabled) + return -EBUSY; #if 0 /* shows regressions, re-enable for 2.6.29 */ @@ -99,7 +97,7 @@ static void cpuidle_idle_call(void) next_state = cpuidle_curr_governor->select(dev); if (need_resched()) { local_irq_enable(); - return; + return 0; } target_state = &dev->states[next_state]; @@ -124,6 +122,8 @@ static void cpuidle_idle_call(void) /* give the governor an opportunity to reflect on the outcome */ if (cpuidle_curr_governor->reflect) cpuidle_curr_governor->reflect(dev); + + return 0; } /** @@ -131,10 +131,10 @@ static void cpuidle_idle_call(void) */ void cpuidle_install_idle_handler(void) { - if (enabled_devices && (pm_idle != cpuidle_idle_call)) { + if (enabled_devices) { /* Make sure all changes finished before we switch to new idle */ smp_wmb(); - pm_idle = cpuidle_idle_call; + initialized = 1; } } @@ -143,8 +143,8 @@ void cpuidle_install_idle_handler(void) */ void cpuidle_uninstall_idle_handler(void) { - if (enabled_devices && pm_idle_old && (pm_idle != pm_idle_old)) { - pm_idle = pm_idle_old; + if (enabled_devices) { + initialized = 0; cpuidle_kick_cpus(); } } @@ -440,8 +440,6 @@ static int __init cpuidle_init(void) if (cpuidle_disabled()) return -ENODEV; - pm_idle_old = pm_idle; - ret = cpuidle_add_class_sysfs(&cpu_sysdev_class); if (ret) return ret; diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index b89f67da919e..b51629e15cfc 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -123,6 +123,7 @@ struct cpuidle_driver { #ifdef CONFIG_CPU_IDLE extern void disable_cpuidle(void); +extern int cpuidle_idle_call(void); extern int cpuidle_register_driver(struct cpuidle_driver *drv); struct cpuidle_driver *cpuidle_get_driver(void); @@ -137,6 +138,7 @@ extern void cpuidle_disable_device(struct cpuidle_device *dev); #else static inline void disable_cpuidle(void) { } +static inline int cpuidle_idle_call(void) { return -ENODEV; } static inline int cpuidle_register_driver(struct cpuidle_driver *drv) {return -ENODEV; } -- cgit v1.2.3 From dd48c085c1cdf9446f92826f1fd451167fb6c2fd Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 3 Aug 2011 16:21:01 -0700 Subject: fault-injection: add ability to export fault_attr in arbitrary directory init_fault_attr_dentries() is used to export fault_attr via debugfs. But it can only export it in debugfs root directory. Per Forlin is working on mmc_fail_request which adds support to inject data errors after a completed host transfer in MMC subsystem. The fault_attr for mmc_fail_request should be defined per mmc host and export it in debugfs directory per mmc host like /sys/kernel/debug/mmc0/mmc_fail_request. init_fault_attr_dentries() doesn't help for mmc_fail_request. So this introduces fault_create_debugfs_attr() which is able to create a directory in the arbitrary directory and replace init_fault_attr_dentries(). [akpm@linux-foundation.org: extraneous semicolon, per Randy] Signed-off-by: Akinobu Mita Tested-by: Per Forlin Cc: Jens Axboe Cc: Christoph Lameter Cc: Pekka Enberg Cc: Matt Mackall Cc: Randy Dunlap Cc: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/fault-injection/fault-injection.txt | 3 +-- block/blk-core.c | 6 ++++-- block/blk-timeout.c | 5 ++++- include/linux/fault-inject.h | 18 +++++------------- lib/fault-inject.c | 20 +++++++------------- mm/failslab.c | 14 +++++++------- mm/page_alloc.c | 13 +++++-------- 7 files changed, 33 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/Documentation/fault-injection/fault-injection.txt b/Documentation/fault-injection/fault-injection.txt index 7be15e44d481..82a5d250d75e 100644 --- a/Documentation/fault-injection/fault-injection.txt +++ b/Documentation/fault-injection/fault-injection.txt @@ -143,8 +143,7 @@ o provide a way to configure fault attributes failslab, fail_page_alloc, and fail_make_request use this way. Helper functions: - init_fault_attr_dentries(entries, attr, name); - void cleanup_fault_attr_dentries(entries); + fault_create_debugfs_attr(name, parent, attr); - module parameters diff --git a/block/blk-core.c b/block/blk-core.c index b850bedad229..b627558c461f 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1368,8 +1368,10 @@ static bool should_fail_request(struct hd_struct *part, unsigned int bytes) static int __init fail_make_request_debugfs(void) { - return init_fault_attr_dentries(&fail_make_request, - "fail_make_request"); + struct dentry *dir = fault_create_debugfs_attr("fail_make_request", + NULL, &fail_make_request); + + return IS_ERR(dir) ? PTR_ERR(dir) : 0; } late_initcall(fail_make_request_debugfs); diff --git a/block/blk-timeout.c b/block/blk-timeout.c index 4f0c06c7a338..780354888958 100644 --- a/block/blk-timeout.c +++ b/block/blk-timeout.c @@ -28,7 +28,10 @@ int blk_should_fake_timeout(struct request_queue *q) static int __init fail_io_timeout_debugfs(void) { - return init_fault_attr_dentries(&fail_io_timeout, "fail_io_timeout"); + struct dentry *dir = fault_create_debugfs_attr("fail_io_timeout", + NULL, &fail_io_timeout); + + return IS_ERR(dir) ? PTR_ERR(dir) : 0; } late_initcall(fail_io_timeout_debugfs); diff --git a/include/linux/fault-inject.h b/include/linux/fault-inject.h index 3ff060ac7810..c6f996f2abb6 100644 --- a/include/linux/fault-inject.h +++ b/include/linux/fault-inject.h @@ -25,10 +25,6 @@ struct fault_attr { unsigned long reject_end; unsigned long count; - -#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS - struct dentry *dir; -#endif }; #define FAULT_ATTR_INITIALIZER { \ @@ -45,19 +41,15 @@ bool should_fail(struct fault_attr *attr, ssize_t size); #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS -int init_fault_attr_dentries(struct fault_attr *attr, const char *name); -void cleanup_fault_attr_dentries(struct fault_attr *attr); +struct dentry *fault_create_debugfs_attr(const char *name, + struct dentry *parent, struct fault_attr *attr); #else /* CONFIG_FAULT_INJECTION_DEBUG_FS */ -static inline int init_fault_attr_dentries(struct fault_attr *attr, - const char *name) -{ - return -ENODEV; -} - -static inline void cleanup_fault_attr_dentries(struct fault_attr *attr) +static inline struct dentry *fault_create_debugfs_attr(const char *name, + struct dentry *parent, struct fault_attr *attr) { + return ERR_PTR(-ENODEV); } #endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */ diff --git a/lib/fault-inject.c b/lib/fault-inject.c index 2577b121c7c1..f193b7796449 100644 --- a/lib/fault-inject.c +++ b/lib/fault-inject.c @@ -197,21 +197,15 @@ static struct dentry *debugfs_create_atomic_t(const char *name, mode_t mode, return debugfs_create_file(name, mode, parent, value, &fops_atomic_t); } -void cleanup_fault_attr_dentries(struct fault_attr *attr) -{ - debugfs_remove_recursive(attr->dir); -} - -int init_fault_attr_dentries(struct fault_attr *attr, const char *name) +struct dentry *fault_create_debugfs_attr(const char *name, + struct dentry *parent, struct fault_attr *attr) { mode_t mode = S_IFREG | S_IRUSR | S_IWUSR; struct dentry *dir; - dir = debugfs_create_dir(name, NULL); + dir = debugfs_create_dir(name, parent); if (!dir) - return -ENOMEM; - - attr->dir = dir; + return ERR_PTR(-ENOMEM); if (!debugfs_create_ul("probability", mode, dir, &attr->probability)) goto fail; @@ -243,11 +237,11 @@ int init_fault_attr_dentries(struct fault_attr *attr, const char *name) #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */ - return 0; + return dir; fail: - debugfs_remove_recursive(attr->dir); + debugfs_remove_recursive(dir); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } #endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */ diff --git a/mm/failslab.c b/mm/failslab.c index 1ce58c201dca..0dd7b8fec71c 100644 --- a/mm/failslab.c +++ b/mm/failslab.c @@ -34,23 +34,23 @@ __setup("failslab=", setup_failslab); #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS static int __init failslab_debugfs_init(void) { + struct dentry *dir; mode_t mode = S_IFREG | S_IRUSR | S_IWUSR; - int err; - err = init_fault_attr_dentries(&failslab.attr, "failslab"); - if (err) - return err; + dir = fault_create_debugfs_attr("failslab", NULL, &failslab.attr); + if (IS_ERR(dir)) + return PTR_ERR(dir); - if (!debugfs_create_bool("ignore-gfp-wait", mode, failslab.attr.dir, + if (!debugfs_create_bool("ignore-gfp-wait", mode, dir, &failslab.ignore_gfp_wait)) goto fail; - if (!debugfs_create_bool("cache-filter", mode, failslab.attr.dir, + if (!debugfs_create_bool("cache-filter", mode, dir, &failslab.cache_filter)) goto fail; return 0; fail: - cleanup_fault_attr_dentries(&failslab.attr); + debugfs_remove_recursive(dir); return -ENOMEM; } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 1dbcf8888f14..6e8ecb6e021c 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1409,14 +1409,11 @@ static int __init fail_page_alloc_debugfs(void) { mode_t mode = S_IFREG | S_IRUSR | S_IWUSR; struct dentry *dir; - int err; - err = init_fault_attr_dentries(&fail_page_alloc.attr, - "fail_page_alloc"); - if (err) - return err; - - dir = fail_page_alloc.attr.dir; + dir = fault_create_debugfs_attr("fail_page_alloc", NULL, + &fail_page_alloc.attr); + if (IS_ERR(dir)) + return PTR_ERR(dir); if (!debugfs_create_bool("ignore-gfp-wait", mode, dir, &fail_page_alloc.ignore_gfp_wait)) @@ -1430,7 +1427,7 @@ static int __init fail_page_alloc_debugfs(void) return 0; fail: - cleanup_fault_attr_dentries(&fail_page_alloc.attr); + debugfs_remove_recursive(dir); return -ENOMEM; } -- cgit v1.2.3 From 88eca0207cf1574328c3ce8c3be537a9317261bb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 3 Aug 2011 16:21:06 -0700 Subject: ida: simplified functions for id allocation The current hyper-optimized functions are overkill if you simply want to allocate an id for a device. Create versions which use an internal lock. In followup patches, numerous drivers are converted to use this interface. Thanks to Tejun for feedback. Signed-off-by: Rusty Russell Acked-by: Tejun Heo Acked-by: Jonathan Cameron Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/idr.h | 4 ++++ lib/idr.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) (limited to 'include') diff --git a/include/linux/idr.h b/include/linux/idr.h index 13a801f3d028..255491cf522e 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -146,6 +146,10 @@ void ida_remove(struct ida *ida, int id); void ida_destroy(struct ida *ida); void ida_init(struct ida *ida); +int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end, + gfp_t gfp_mask); +void ida_simple_remove(struct ida *ida, unsigned int id); + void __init idr_init_cache(void); #endif /* __IDR_H__ */ diff --git a/lib/idr.c b/lib/idr.c index e15502e8b21e..db040ce3fa73 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -34,8 +34,10 @@ #include #include #include +#include static struct kmem_cache *idr_layer_cache; +static DEFINE_SPINLOCK(simple_ida_lock); static struct idr_layer *get_from_free_list(struct idr *idp) { @@ -925,6 +927,71 @@ void ida_destroy(struct ida *ida) } EXPORT_SYMBOL(ida_destroy); +/** + * ida_simple_get - get a new id. + * @ida: the (initialized) ida. + * @start: the minimum id (inclusive, < 0x8000000) + * @end: the maximum id (exclusive, < 0x8000000 or 0) + * @gfp_mask: memory allocation flags + * + * Allocates an id in the range start <= id < end, or returns -ENOSPC. + * On memory allocation failure, returns -ENOMEM. + * + * Use ida_simple_remove() to get rid of an id. + */ +int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end, + gfp_t gfp_mask) +{ + int ret, id; + unsigned int max; + + BUG_ON((int)start < 0); + BUG_ON((int)end < 0); + + if (end == 0) + max = 0x80000000; + else { + BUG_ON(end < start); + max = end - 1; + } + +again: + if (!ida_pre_get(ida, gfp_mask)) + return -ENOMEM; + + spin_lock(&simple_ida_lock); + ret = ida_get_new_above(ida, start, &id); + if (!ret) { + if (id > max) { + ida_remove(ida, id); + ret = -ENOSPC; + } else { + ret = id; + } + } + spin_unlock(&simple_ida_lock); + + if (unlikely(ret == -EAGAIN)) + goto again; + + return ret; +} +EXPORT_SYMBOL(ida_simple_get); + +/** + * ida_simple_remove - remove an allocated id. + * @ida: the (initialized) ida. + * @id: the id returned by ida_simple_get. + */ +void ida_simple_remove(struct ida *ida, unsigned int id) +{ + BUG_ON((int)id < 0); + spin_lock(&simple_ida_lock); + ida_remove(ida, id); + spin_unlock(&simple_ida_lock); +} +EXPORT_SYMBOL(ida_simple_remove); + /** * ida_init - initialize ida handle * @ida: ida handle -- cgit v1.2.3 From 3dab1bce8e840606e76f1bad5a009328e2aa35c7 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 3 Aug 2011 16:21:09 -0700 Subject: mm: page_alloc: increase __GFP_BITS_SHIFT to include __GFP_OTHER_NODE __GFP_OTHER_NODE is used for NUMA allocations on behalf of other nodes. It's supposed to be passed through from the page allocator to zone_statistics(), but it never gets there as gfp_allowed_mask is not wide enough and masks out the flag early in the allocation path. The result is an accounting glitch where successful NUMA allocations by-agent are not properly attributed as local. Increase __GFP_BITS_SHIFT so that it includes __GFP_OTHER_NODE. Signed-off-by: Johannes Weiner Acked-by: Andi Kleen Reviewed-by: Minchan Kim Acked-by: Mel Gorman Reviewed-by: Michal Hocko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/gfp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/gfp.h b/include/linux/gfp.h index cb4089254f01..3a76faf6a3ee 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -92,7 +92,7 @@ struct vm_area_struct; */ #define __GFP_NOTRACK_FALSE_POSITIVE (__GFP_NOTRACK) -#define __GFP_BITS_SHIFT 23 /* Room for 23 __GFP_FOO bits */ +#define __GFP_BITS_SHIFT 24 /* Room for N __GFP_FOO bits */ #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) /* This equals 0, but use constants in case they ever change */ -- cgit v1.2.3 From 5d6f921b42749d1a70441685b7a4f2801e12ebfb Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 3 Aug 2011 16:21:17 -0700 Subject: drivers/video/backlight/aat2870_bl.c: fix setting max_current - Current implementation tests wrong value for setting aat2870_bl->max_current. - In the current implementation, we cannot differentiate between 2 cases: a) if pdata->max_current is not set , or b) pdata->max_current is set to AAT2870_CURRENT_0_45 (which is also 0). Fix it by setting AAT2870_CURRENT_0_45 to be 1 and adjust the equation in aat2870_brightness() accordingly. Signed-off-by: Axel Lin Cc: Richard Purdie Cc: Samuel Ortiz Tested-by: Jin Park Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/aat2870_bl.c | 4 ++-- include/linux/mfd/aat2870.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/video/backlight/aat2870_bl.c b/drivers/video/backlight/aat2870_bl.c index f13a3f7e2e8f..331f1ef1dad5 100644 --- a/drivers/video/backlight/aat2870_bl.c +++ b/drivers/video/backlight/aat2870_bl.c @@ -44,7 +44,7 @@ static inline int aat2870_brightness(struct aat2870_bl_driver_data *aat2870_bl, struct backlight_device *bd = aat2870_bl->bd; int val; - val = brightness * aat2870_bl->max_current; + val = brightness * (aat2870_bl->max_current - 1); val /= bd->props.max_brightness; return val; @@ -175,7 +175,7 @@ static int aat2870_bl_probe(struct platform_device *pdev) else aat2870_bl->channels = AAT2870_BL_CH_ALL; - if (pdata->max_brightness > 0) + if (pdata->max_current > 0) aat2870_bl->max_current = pdata->max_current; else aat2870_bl->max_current = AAT2870_CURRENT_27_9; diff --git a/include/linux/mfd/aat2870.h b/include/linux/mfd/aat2870.h index 89212df05622..f7316c29bdec 100644 --- a/include/linux/mfd/aat2870.h +++ b/include/linux/mfd/aat2870.h @@ -89,7 +89,7 @@ enum aat2870_id { /* Backlight current magnitude (mA) */ enum aat2870_current { - AAT2870_CURRENT_0_45, + AAT2870_CURRENT_0_45 = 1, AAT2870_CURRENT_0_90, AAT2870_CURRENT_1_80, AAT2870_CURRENT_2_70, -- cgit v1.2.3 From 6328650bb4d854a7dc1498d1c0048b838b0d340c Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 3 Aug 2011 16:21:18 -0700 Subject: radix_tree: exceptional entries and indices A patchset to extend tmpfs to MAX_LFS_FILESIZE by abandoning its peculiar swap vector, instead keeping a file's swap entries in the same radix tree as its struct page pointers: thus saving memory, and simplifying its code and locking. This patch: The radix_tree is used by several subsystems for different purposes. A major use is to store the struct page pointers of a file's pagecache for memory management. But what if mm wanted to store something other than page pointers there too? The low bit of a radix_tree entry is already used to denote an indirect pointer, for internal use, and the unlikely radix_tree_deref_retry() case. Define the next bit as denoting an exceptional entry, and supply inline functions radix_tree_exception() to return non-0 in either unlikely case, and radix_tree_exceptional_entry() to return non-0 in the second case. If a subsystem already uses radix_tree with that bit set, no problem: it does not affect internal workings at all, but is defined for the convenience of those storing well-aligned pointers in the radix_tree. The radix_tree_gang_lookups have an implicit assumption that the caller can deduce the offset of each entry returned e.g. by the page->index of a struct page. But that may not be feasible for some kinds of item to be stored there. radix_tree_gang_lookup_slot() allow for an optional indices argument, output array in which to return those offsets. The same could be added to other radix_tree_gang_lookups, but for now keep it to the only one for which we need it. Signed-off-by: Hugh Dickins Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/radix-tree.h | 36 +++++++++++++++++++++++++++++++++--- lib/radix-tree.c | 29 +++++++++++++++++++---------- mm/filemap.c | 4 ++-- 3 files changed, 54 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index 23241c2fecce..b7edf8251455 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h @@ -39,7 +39,15 @@ * when it is shrunk, before we rcu free the node. See shrink code for * details. */ -#define RADIX_TREE_INDIRECT_PTR 1 +#define RADIX_TREE_INDIRECT_PTR 1 +/* + * A common use of the radix tree is to store pointers to struct pages; + * but shmem/tmpfs needs also to store swap entries in the same tree: + * those are marked as exceptional entries to distinguish them. + * EXCEPTIONAL_ENTRY tests the bit, EXCEPTIONAL_SHIFT shifts content past it. + */ +#define RADIX_TREE_EXCEPTIONAL_ENTRY 2 +#define RADIX_TREE_EXCEPTIONAL_SHIFT 2 #define radix_tree_indirect_to_ptr(ptr) \ radix_tree_indirect_to_ptr((void __force *)(ptr)) @@ -173,6 +181,28 @@ static inline int radix_tree_deref_retry(void *arg) return unlikely((unsigned long)arg & RADIX_TREE_INDIRECT_PTR); } +/** + * radix_tree_exceptional_entry - radix_tree_deref_slot gave exceptional entry? + * @arg: value returned by radix_tree_deref_slot + * Returns: 0 if well-aligned pointer, non-0 if exceptional entry. + */ +static inline int radix_tree_exceptional_entry(void *arg) +{ + /* Not unlikely because radix_tree_exception often tested first */ + return (unsigned long)arg & RADIX_TREE_EXCEPTIONAL_ENTRY; +} + +/** + * radix_tree_exception - radix_tree_deref_slot returned either exception? + * @arg: value returned by radix_tree_deref_slot + * Returns: 0 if well-aligned pointer, non-0 if either kind of exception. + */ +static inline int radix_tree_exception(void *arg) +{ + return unlikely((unsigned long)arg & + (RADIX_TREE_INDIRECT_PTR | RADIX_TREE_EXCEPTIONAL_ENTRY)); +} + /** * radix_tree_replace_slot - replace item in a slot * @pslot: pointer to slot, returned by radix_tree_lookup_slot @@ -194,8 +224,8 @@ void *radix_tree_delete(struct radix_tree_root *, unsigned long); unsigned int radix_tree_gang_lookup(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items); -unsigned int -radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results, +unsigned int radix_tree_gang_lookup_slot(struct radix_tree_root *root, + void ***results, unsigned long *indices, unsigned long first_index, unsigned int max_items); unsigned long radix_tree_next_hole(struct radix_tree_root *root, unsigned long index, unsigned long max_scan); diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 7ea2e033d715..348eaefbed74 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -823,8 +823,8 @@ unsigned long radix_tree_prev_hole(struct radix_tree_root *root, EXPORT_SYMBOL(radix_tree_prev_hole); static unsigned int -__lookup(struct radix_tree_node *slot, void ***results, unsigned long index, - unsigned int max_items, unsigned long *next_index) +__lookup(struct radix_tree_node *slot, void ***results, unsigned long *indices, + unsigned long index, unsigned int max_items, unsigned long *next_index) { unsigned int nr_found = 0; unsigned int shift, height; @@ -857,12 +857,16 @@ __lookup(struct radix_tree_node *slot, void ***results, unsigned long index, /* Bottom level: grab some items */ for (i = index & RADIX_TREE_MAP_MASK; i < RADIX_TREE_MAP_SIZE; i++) { - index++; if (slot->slots[i]) { - results[nr_found++] = &(slot->slots[i]); - if (nr_found == max_items) + results[nr_found] = &(slot->slots[i]); + if (indices) + indices[nr_found] = index; + if (++nr_found == max_items) { + index++; goto out; + } } + index++; } out: *next_index = index; @@ -918,8 +922,8 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results, if (cur_index > max_index) break; - slots_found = __lookup(node, (void ***)results + ret, cur_index, - max_items - ret, &next_index); + slots_found = __lookup(node, (void ***)results + ret, NULL, + cur_index, max_items - ret, &next_index); nr_found = 0; for (i = 0; i < slots_found; i++) { struct radix_tree_node *slot; @@ -944,6 +948,7 @@ EXPORT_SYMBOL(radix_tree_gang_lookup); * radix_tree_gang_lookup_slot - perform multiple slot lookup on radix tree * @root: radix tree root * @results: where the results of the lookup are placed + * @indices: where their indices should be placed (but usually NULL) * @first_index: start the lookup from this key * @max_items: place up to this many items at *results * @@ -958,7 +963,8 @@ EXPORT_SYMBOL(radix_tree_gang_lookup); * protection, radix_tree_deref_slot may fail requiring a retry. */ unsigned int -radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results, +radix_tree_gang_lookup_slot(struct radix_tree_root *root, + void ***results, unsigned long *indices, unsigned long first_index, unsigned int max_items) { unsigned long max_index; @@ -974,6 +980,8 @@ radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results, if (first_index > 0) return 0; results[0] = (void **)&root->rnode; + if (indices) + indices[0] = 0; return 1; } node = indirect_to_ptr(node); @@ -987,8 +995,9 @@ radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results, if (cur_index > max_index) break; - slots_found = __lookup(node, results + ret, cur_index, - max_items - ret, &next_index); + slots_found = __lookup(node, results + ret, + indices ? indices + ret : NULL, + cur_index, max_items - ret, &next_index); ret += slots_found; if (next_index == 0) break; diff --git a/mm/filemap.c b/mm/filemap.c index 867d40222ec7..b83aebfd0a00 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -840,7 +840,7 @@ unsigned find_get_pages(struct address_space *mapping, pgoff_t start, rcu_read_lock(); restart: nr_found = radix_tree_gang_lookup_slot(&mapping->page_tree, - (void ***)pages, start, nr_pages); + (void ***)pages, NULL, start, nr_pages); ret = 0; for (i = 0; i < nr_found; i++) { struct page *page; @@ -903,7 +903,7 @@ unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t index, rcu_read_lock(); restart: nr_found = radix_tree_gang_lookup_slot(&mapping->page_tree, - (void ***)pages, index, nr_pages); + (void ***)pages, NULL, index, nr_pages); ret = 0; for (i = 0; i < nr_found; i++) { struct page *page; -- cgit v1.2.3 From a2c16d6cb0e478812829ca84aeabd02e36af35eb Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 3 Aug 2011 16:21:19 -0700 Subject: mm: let swap use exceptional entries If swap entries are to be stored along with struct page pointers in a radix tree, they need to be distinguished as exceptional entries. Most of the handling of swap entries in radix tree will be contained in shmem.c, but a few functions in filemap.c's common code need to check for their appearance: find_get_page(), find_lock_page(), find_get_pages() and find_get_pages_contig(). So as not to slow their fast paths, tuck those checks inside the existing checks for unlikely radix_tree_deref_slot(); except for find_lock_page(), where it is an added test. And make it a BUG in find_get_pages_tag(), which is not applied to tmpfs files. A part of the reason for eliminating shmem_readpage() earlier, was to minimize the places where common code would need to allow for swap entries. The swp_entry_t known to swapfile.c must be massaged into a slightly different form when stored in the radix tree, just as it gets massaged into a pte_t when stored in page tables. In an i386 kernel this limits its information (type and page offset) to 30 bits: given 32 "types" of swapfile and 4kB pagesize, that's a maximum swapfile size of 128GB. Which is less than the 512GB we previously allowed with X86_PAE (where the swap entry can occupy the entire upper 32 bits of a pte_t), but not a new limitation on 32-bit without PAE; and there's not a new limitation on 64-bit (where swap filesize is already limited to 16TB by a 32-bit page offset). Thirty areas of 128GB is probably still enough swap for a 64GB 32-bit machine. Provide swp_to_radix_entry() and radix_to_swp_entry() conversions, and enforce filesize limit in read_swap_header(), just as for ptes. Signed-off-by: Hugh Dickins Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swapops.h | 23 +++++++++++++++++++++++ mm/filemap.c | 49 +++++++++++++++++++++++++++++++------------------ mm/swapfile.c | 20 ++++++++++++-------- 3 files changed, 66 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/linux/swapops.h b/include/linux/swapops.h index cd42e30b7c6e..2189d3ffc85d 100644 --- a/include/linux/swapops.h +++ b/include/linux/swapops.h @@ -1,3 +1,8 @@ +#ifndef _LINUX_SWAPOPS_H +#define _LINUX_SWAPOPS_H + +#include + /* * swapcache pages are stored in the swapper_space radix tree. We want to * get good packing density in that tree, so the index should be dense in @@ -76,6 +81,22 @@ static inline pte_t swp_entry_to_pte(swp_entry_t entry) return __swp_entry_to_pte(arch_entry); } +static inline swp_entry_t radix_to_swp_entry(void *arg) +{ + swp_entry_t entry; + + entry.val = (unsigned long)arg >> RADIX_TREE_EXCEPTIONAL_SHIFT; + return entry; +} + +static inline void *swp_to_radix_entry(swp_entry_t entry) +{ + unsigned long value; + + value = entry.val << RADIX_TREE_EXCEPTIONAL_SHIFT; + return (void *)(value | RADIX_TREE_EXCEPTIONAL_ENTRY); +} + #ifdef CONFIG_MIGRATION static inline swp_entry_t make_migration_entry(struct page *page, int write) { @@ -169,3 +190,5 @@ static inline int non_swap_entry(swp_entry_t entry) return 0; } #endif + +#endif /* _LINUX_SWAPOPS_H */ diff --git a/mm/filemap.c b/mm/filemap.c index b83aebfd0a00..76bfb6460f57 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -714,9 +714,12 @@ repeat: page = radix_tree_deref_slot(pagep); if (unlikely(!page)) goto out; - if (radix_tree_deref_retry(page)) + if (radix_tree_exception(page)) { + if (radix_tree_exceptional_entry(page)) + goto out; + /* radix_tree_deref_retry(page) */ goto repeat; - + } if (!page_cache_get_speculative(page)) goto repeat; @@ -753,7 +756,7 @@ struct page *find_lock_page(struct address_space *mapping, pgoff_t offset) repeat: page = find_get_page(mapping, offset); - if (page) { + if (page && !radix_tree_exception(page)) { lock_page(page); /* Has the page been truncated? */ if (unlikely(page->mapping != mapping)) { @@ -849,11 +852,14 @@ repeat: if (unlikely(!page)) continue; - /* - * This can only trigger when the entry at index 0 moves out - * of or back to the root: none yet gotten, safe to restart. - */ - if (radix_tree_deref_retry(page)) { + if (radix_tree_exception(page)) { + if (radix_tree_exceptional_entry(page)) + continue; + /* + * radix_tree_deref_retry(page): + * can only trigger when entry at index 0 moves out of + * or back to root: none yet gotten, safe to restart. + */ WARN_ON(start | i); goto restart; } @@ -912,12 +918,16 @@ repeat: if (unlikely(!page)) continue; - /* - * This can only trigger when the entry at index 0 moves out - * of or back to the root: none yet gotten, safe to restart. - */ - if (radix_tree_deref_retry(page)) + if (radix_tree_exception(page)) { + if (radix_tree_exceptional_entry(page)) + break; + /* + * radix_tree_deref_retry(page): + * can only trigger when entry at index 0 moves out of + * or back to root: none yet gotten, safe to restart. + */ goto restart; + } if (!page_cache_get_speculative(page)) goto repeat; @@ -977,12 +987,15 @@ repeat: if (unlikely(!page)) continue; - /* - * This can only trigger when the entry at index 0 moves out - * of or back to the root: none yet gotten, safe to restart. - */ - if (radix_tree_deref_retry(page)) + if (radix_tree_exception(page)) { + BUG_ON(radix_tree_exceptional_entry(page)); + /* + * radix_tree_deref_retry(page): + * can only trigger when entry at index 0 moves out of + * or back to root: none yet gotten, safe to restart. + */ goto restart; + } if (!page_cache_get_speculative(page)) goto repeat; diff --git a/mm/swapfile.c b/mm/swapfile.c index 1b8c33907242..17bc224bce68 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1924,20 +1924,24 @@ static unsigned long read_swap_header(struct swap_info_struct *p, /* * Find out how many pages are allowed for a single swap - * device. There are two limiting factors: 1) the number of - * bits for the swap offset in the swp_entry_t type and - * 2) the number of bits in the a swap pte as defined by - * the different architectures. In order to find the - * largest possible bit mask a swap entry with swap type 0 + * device. There are three limiting factors: 1) the number + * of bits for the swap offset in the swp_entry_t type, and + * 2) the number of bits in the swap pte as defined by the + * the different architectures, and 3) the number of free bits + * in an exceptional radix_tree entry. In order to find the + * largest possible bit mask, a swap entry with swap type 0 * and swap offset ~0UL is created, encoded to a swap pte, - * decoded to a swp_entry_t again and finally the swap + * decoded to a swp_entry_t again, and finally the swap * offset is extracted. This will mask all the bits from * the initial ~0UL mask that can't be encoded in either * the swp_entry_t or the architecture definition of a - * swap pte. + * swap pte. Then the same is done for a radix_tree entry. */ maxpages = swp_offset(pte_to_swp_entry( - swp_entry_to_pte(swp_entry(0, ~0UL)))) + 1; + swp_entry_to_pte(swp_entry(0, ~0UL)))); + maxpages = swp_offset(radix_to_swp_entry( + swp_to_radix_entry(swp_entry(0, maxpages)))) + 1; + if (maxpages > swap_header->info.last_page) { maxpages = swap_header->info.last_page + 1; /* p->max is an unsigned int: don't overflow it */ -- cgit v1.2.3 From 285b2c4fdd69ea73b4762785d8c6be83b6c074a6 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 3 Aug 2011 16:21:20 -0700 Subject: tmpfs: demolish old swap vector support The maximum size of a shmem/tmpfs file has been limited by the maximum size of its triple-indirect swap vector. With 4kB page size, maximum filesize was just over 2TB on a 32-bit kernel, but sadly one eighth of that on a 64-bit kernel. (With 8kB page size, maximum filesize was just over 4TB on a 64-bit kernel, but 16TB on a 32-bit kernel, MAX_LFS_FILESIZE being then more restrictive than swap vector layout.) It's a shame that tmpfs should be more restrictive than ramfs, and this limitation has now been noticed. Add another level to the swap vector? No, it became obscure and hard to maintain, once I complicated it to make use of highmem pages nine years ago: better choose another way. Surely, if 2.4 had had the radix tree pagecache introduced in 2.5, then tmpfs would never have invented its own peculiar radix tree: we would have fitted swap entries into the common radix tree instead, in much the same way as we fit swap entries into page tables. And why should each file have a separate radix tree for its pages and for its swap entries? The swap entries are required precisely where and when the pages are not. We want to put them together in a single radix tree: which can then avoid much of the locking which was needed to prevent them from being exchanged underneath us. This also avoids the waste of memory devoted to swap vectors, first in the shmem_inode itself, then at least two more pages once a file grew beyond 16 data pages (pages accounted by df and du, but not by memcg). Allocated upfront, to avoid allocation when under swapping pressure, but pure waste when CONFIG_SWAP is not set - I have never spattered around the ifdefs to prevent that, preferring this move to sharing the common radix tree instead. There are three downsides to sharing the radix tree. One, that it binds tmpfs more tightly to the rest of mm, either requiring knowledge of swap entries in radix tree there, or duplication of its code here in shmem.c. I believe that the simplications and memory savings (and probable higher performance, not yet measured) justify that. Two, that on HIGHMEM systems with SWAP enabled, it's the lowmem radix nodes that cannot be freed under memory pressure - whereas before it was the less precious highmem swap vector pages that could not be freed. I'm hoping that 64-bit has now been accessible for long enough, that the highmem argument has grown much less persuasive. Three, that swapoff is slower than it used to be on tmpfs files, since it's using a simple generic mechanism not tailored to it: I find this noticeable, and shall want to improve, but maybe nobody else will notice. So... now remove most of the old swap vector code from shmem.c. But, for the moment, keep the simple i_direct vector of 16 pages, with simple accessors shmem_put_swap() and shmem_get_swap(), as a toy implementation to help mark where swap needs to be handled in subsequent patches. Signed-off-by: Hugh Dickins Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/shmem_fs.h | 2 - mm/shmem.c | 782 +++++------------------------------------------ 2 files changed, 84 insertions(+), 700 deletions(-) (limited to 'include') diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index aa08fa8fd79b..80b695213fdb 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -17,9 +17,7 @@ struct shmem_inode_info { unsigned long flags; unsigned long alloced; /* data pages alloced to file */ unsigned long swapped; /* subtotal assigned to swap */ - unsigned long next_index; /* highest alloced index + 1 */ struct shared_policy policy; /* NUMA memory alloc policy */ - struct page *i_indirect; /* top indirect blocks page */ union { swp_entry_t i_direct[SHMEM_NR_DIRECT]; /* first blocks */ char inline_symlink[SHMEM_SYMLINK_INLINE_LEN]; diff --git a/mm/shmem.c b/mm/shmem.c index 5cc21f8b4cd3..5574b00ca771 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -66,37 +66,9 @@ static struct vfsmount *shm_mnt; #include #include -/* - * The maximum size of a shmem/tmpfs file is limited by the maximum size of - * its triple-indirect swap vector - see illustration at shmem_swp_entry(). - * - * With 4kB page size, maximum file size is just over 2TB on a 32-bit kernel, - * but one eighth of that on a 64-bit kernel. With 8kB page size, maximum - * file size is just over 4TB on a 64-bit kernel, but 16TB on a 32-bit kernel, - * MAX_LFS_FILESIZE being then more restrictive than swap vector layout. - * - * We use / and * instead of shifts in the definitions below, so that the swap - * vector can be tested with small even values (e.g. 20) for ENTRIES_PER_PAGE. - */ -#define ENTRIES_PER_PAGE (PAGE_CACHE_SIZE/sizeof(unsigned long)) -#define ENTRIES_PER_PAGEPAGE ((unsigned long long)ENTRIES_PER_PAGE*ENTRIES_PER_PAGE) - -#define SHMSWP_MAX_INDEX (SHMEM_NR_DIRECT + (ENTRIES_PER_PAGEPAGE/2) * (ENTRIES_PER_PAGE+1)) -#define SHMSWP_MAX_BYTES (SHMSWP_MAX_INDEX << PAGE_CACHE_SHIFT) - -#define SHMEM_MAX_BYTES min_t(unsigned long long, SHMSWP_MAX_BYTES, MAX_LFS_FILESIZE) -#define SHMEM_MAX_INDEX ((unsigned long)((SHMEM_MAX_BYTES+1) >> PAGE_CACHE_SHIFT)) - #define BLOCKS_PER_PAGE (PAGE_CACHE_SIZE/512) #define VM_ACCT(size) (PAGE_CACHE_ALIGN(size) >> PAGE_SHIFT) -/* info->flags needs VM_flags to handle pagein/truncate races efficiently */ -#define SHMEM_PAGEIN VM_READ -#define SHMEM_TRUNCATE VM_WRITE - -/* Definition to limit shmem_truncate's steps between cond_rescheds */ -#define LATENCY_LIMIT 64 - /* Pretend that each entry is of this size in directory's i_size */ #define BOGO_DIRENT_SIZE 20 @@ -107,7 +79,7 @@ struct shmem_xattr { char value[0]; }; -/* Flag allocation requirements to shmem_getpage and shmem_swp_alloc */ +/* Flag allocation requirements to shmem_getpage */ enum sgp_type { SGP_READ, /* don't exceed i_size, don't allocate page */ SGP_CACHE, /* don't exceed i_size, may allocate page */ @@ -137,56 +109,6 @@ static inline int shmem_getpage(struct inode *inode, pgoff_t index, mapping_gfp_mask(inode->i_mapping), fault_type); } -static inline struct page *shmem_dir_alloc(gfp_t gfp_mask) -{ - /* - * The above definition of ENTRIES_PER_PAGE, and the use of - * BLOCKS_PER_PAGE on indirect pages, assume PAGE_CACHE_SIZE: - * might be reconsidered if it ever diverges from PAGE_SIZE. - * - * Mobility flags are masked out as swap vectors cannot move - */ - return alloc_pages((gfp_mask & ~GFP_MOVABLE_MASK) | __GFP_ZERO, - PAGE_CACHE_SHIFT-PAGE_SHIFT); -} - -static inline void shmem_dir_free(struct page *page) -{ - __free_pages(page, PAGE_CACHE_SHIFT-PAGE_SHIFT); -} - -static struct page **shmem_dir_map(struct page *page) -{ - return (struct page **)kmap_atomic(page, KM_USER0); -} - -static inline void shmem_dir_unmap(struct page **dir) -{ - kunmap_atomic(dir, KM_USER0); -} - -static swp_entry_t *shmem_swp_map(struct page *page) -{ - return (swp_entry_t *)kmap_atomic(page, KM_USER1); -} - -static inline void shmem_swp_balance_unmap(void) -{ - /* - * When passing a pointer to an i_direct entry, to code which - * also handles indirect entries and so will shmem_swp_unmap, - * we must arrange for the preempt count to remain in balance. - * What kmap_atomic of a lowmem page does depends on config - * and architecture, so pretend to kmap_atomic some lowmem page. - */ - (void) kmap_atomic(ZERO_PAGE(0), KM_USER1); -} - -static inline void shmem_swp_unmap(swp_entry_t *entry) -{ - kunmap_atomic(entry, KM_USER1); -} - static inline struct shmem_sb_info *SHMEM_SB(struct super_block *sb) { return sb->s_fs_info; @@ -303,468 +225,56 @@ static void shmem_recalc_inode(struct inode *inode) } } -/** - * shmem_swp_entry - find the swap vector position in the info structure - * @info: info structure for the inode - * @index: index of the page to find - * @page: optional page to add to the structure. Has to be preset to - * all zeros - * - * If there is no space allocated yet it will return NULL when - * page is NULL, else it will use the page for the needed block, - * setting it to NULL on return to indicate that it has been used. - * - * The swap vector is organized the following way: - * - * There are SHMEM_NR_DIRECT entries directly stored in the - * shmem_inode_info structure. So small files do not need an addional - * allocation. - * - * For pages with index > SHMEM_NR_DIRECT there is the pointer - * i_indirect which points to a page which holds in the first half - * doubly indirect blocks, in the second half triple indirect blocks: - * - * For an artificial ENTRIES_PER_PAGE = 4 this would lead to the - * following layout (for SHMEM_NR_DIRECT == 16): - * - * i_indirect -> dir --> 16-19 - * | +-> 20-23 - * | - * +-->dir2 --> 24-27 - * | +-> 28-31 - * | +-> 32-35 - * | +-> 36-39 - * | - * +-->dir3 --> 40-43 - * +-> 44-47 - * +-> 48-51 - * +-> 52-55 - */ -static swp_entry_t *shmem_swp_entry(struct shmem_inode_info *info, unsigned long index, struct page **page) -{ - unsigned long offset; - struct page **dir; - struct page *subdir; - - if (index < SHMEM_NR_DIRECT) { - shmem_swp_balance_unmap(); - return info->i_direct+index; - } - if (!info->i_indirect) { - if (page) { - info->i_indirect = *page; - *page = NULL; - } - return NULL; /* need another page */ - } - - index -= SHMEM_NR_DIRECT; - offset = index % ENTRIES_PER_PAGE; - index /= ENTRIES_PER_PAGE; - dir = shmem_dir_map(info->i_indirect); - - if (index >= ENTRIES_PER_PAGE/2) { - index -= ENTRIES_PER_PAGE/2; - dir += ENTRIES_PER_PAGE/2 + index/ENTRIES_PER_PAGE; - index %= ENTRIES_PER_PAGE; - subdir = *dir; - if (!subdir) { - if (page) { - *dir = *page; - *page = NULL; - } - shmem_dir_unmap(dir); - return NULL; /* need another page */ - } - shmem_dir_unmap(dir); - dir = shmem_dir_map(subdir); - } - - dir += index; - subdir = *dir; - if (!subdir) { - if (!page || !(subdir = *page)) { - shmem_dir_unmap(dir); - return NULL; /* need a page */ - } - *dir = subdir; - *page = NULL; - } - shmem_dir_unmap(dir); - return shmem_swp_map(subdir) + offset; -} - -static void shmem_swp_set(struct shmem_inode_info *info, swp_entry_t *entry, unsigned long value) +static void shmem_put_swap(struct shmem_inode_info *info, pgoff_t index, + swp_entry_t swap) { - long incdec = value? 1: -1; - - entry->val = value; - info->swapped += incdec; - if ((unsigned long)(entry - info->i_direct) >= SHMEM_NR_DIRECT) { - struct page *page = kmap_atomic_to_page(entry); - set_page_private(page, page_private(page) + incdec); - } -} - -/** - * shmem_swp_alloc - get the position of the swap entry for the page. - * @info: info structure for the inode - * @index: index of the page to find - * @sgp: check and recheck i_size? skip allocation? - * @gfp: gfp mask to use for any page allocation - * - * If the entry does not exist, allocate it. - */ -static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info, - unsigned long index, enum sgp_type sgp, gfp_t gfp) -{ - struct inode *inode = &info->vfs_inode; - struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); - struct page *page = NULL; - swp_entry_t *entry; - - if (sgp != SGP_WRITE && - ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) - return ERR_PTR(-EINVAL); - - while (!(entry = shmem_swp_entry(info, index, &page))) { - if (sgp == SGP_READ) - return shmem_swp_map(ZERO_PAGE(0)); - /* - * Test used_blocks against 1 less max_blocks, since we have 1 data - * page (and perhaps indirect index pages) yet to allocate: - * a waste to allocate index if we cannot allocate data. - */ - if (sbinfo->max_blocks) { - if (percpu_counter_compare(&sbinfo->used_blocks, - sbinfo->max_blocks - 1) >= 0) - return ERR_PTR(-ENOSPC); - percpu_counter_inc(&sbinfo->used_blocks); - inode->i_blocks += BLOCKS_PER_PAGE; - } - - spin_unlock(&info->lock); - page = shmem_dir_alloc(gfp); - spin_lock(&info->lock); - - if (!page) { - shmem_free_blocks(inode, 1); - return ERR_PTR(-ENOMEM); - } - if (sgp != SGP_WRITE && - ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) { - entry = ERR_PTR(-EINVAL); - break; - } - if (info->next_index <= index) - info->next_index = index + 1; - } - if (page) { - /* another task gave its page, or truncated the file */ - shmem_free_blocks(inode, 1); - shmem_dir_free(page); - } - if (info->next_index <= index && !IS_ERR(entry)) - info->next_index = index + 1; - return entry; -} - -/** - * shmem_free_swp - free some swap entries in a directory - * @dir: pointer to the directory - * @edir: pointer after last entry of the directory - * @punch_lock: pointer to spinlock when needed for the holepunch case - */ -static int shmem_free_swp(swp_entry_t *dir, swp_entry_t *edir, - spinlock_t *punch_lock) -{ - spinlock_t *punch_unlock = NULL; - swp_entry_t *ptr; - int freed = 0; - - for (ptr = dir; ptr < edir; ptr++) { - if (ptr->val) { - if (unlikely(punch_lock)) { - punch_unlock = punch_lock; - punch_lock = NULL; - spin_lock(punch_unlock); - if (!ptr->val) - continue; - } - free_swap_and_cache(*ptr); - *ptr = (swp_entry_t){0}; - freed++; - } - } - if (punch_unlock) - spin_unlock(punch_unlock); - return freed; -} - -static int shmem_map_and_free_swp(struct page *subdir, int offset, - int limit, struct page ***dir, spinlock_t *punch_lock) -{ - swp_entry_t *ptr; - int freed = 0; - - ptr = shmem_swp_map(subdir); - for (; offset < limit; offset += LATENCY_LIMIT) { - int size = limit - offset; - if (size > LATENCY_LIMIT) - size = LATENCY_LIMIT; - freed += shmem_free_swp(ptr+offset, ptr+offset+size, - punch_lock); - if (need_resched()) { - shmem_swp_unmap(ptr); - if (*dir) { - shmem_dir_unmap(*dir); - *dir = NULL; - } - cond_resched(); - ptr = shmem_swp_map(subdir); - } - } - shmem_swp_unmap(ptr); - return freed; + if (index < SHMEM_NR_DIRECT) + info->i_direct[index] = swap; } -static void shmem_free_pages(struct list_head *next) +static swp_entry_t shmem_get_swap(struct shmem_inode_info *info, pgoff_t index) { - struct page *page; - int freed = 0; - - do { - page = container_of(next, struct page, lru); - next = next->next; - shmem_dir_free(page); - freed++; - if (freed >= LATENCY_LIMIT) { - cond_resched(); - freed = 0; - } - } while (next); + return (index < SHMEM_NR_DIRECT) ? + info->i_direct[index] : (swp_entry_t){0}; } -void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end) +void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend) { + struct address_space *mapping = inode->i_mapping; struct shmem_inode_info *info = SHMEM_I(inode); - unsigned long idx; - unsigned long size; - unsigned long limit; - unsigned long stage; - unsigned long diroff; - struct page **dir; - struct page *topdir; - struct page *middir; - struct page *subdir; - swp_entry_t *ptr; - LIST_HEAD(pages_to_free); - long nr_pages_to_free = 0; - long nr_swaps_freed = 0; - int offset; - int freed; - int punch_hole; - spinlock_t *needs_lock; - spinlock_t *punch_lock; - unsigned long upper_limit; + pgoff_t start = (lstart + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + pgoff_t end = (lend >> PAGE_CACHE_SHIFT); + pgoff_t index; + swp_entry_t swap; - truncate_inode_pages_range(inode->i_mapping, start, end); + truncate_inode_pages_range(mapping, lstart, lend); - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - idx = (start + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - if (idx >= info->next_index) - return; + if (end > SHMEM_NR_DIRECT) + end = SHMEM_NR_DIRECT; spin_lock(&info->lock); - info->flags |= SHMEM_TRUNCATE; - if (likely(end == (loff_t) -1)) { - limit = info->next_index; - upper_limit = SHMEM_MAX_INDEX; - info->next_index = idx; - needs_lock = NULL; - punch_hole = 0; - } else { - if (end + 1 >= inode->i_size) { /* we may free a little more */ - limit = (inode->i_size + PAGE_CACHE_SIZE - 1) >> - PAGE_CACHE_SHIFT; - upper_limit = SHMEM_MAX_INDEX; - } else { - limit = (end + 1) >> PAGE_CACHE_SHIFT; - upper_limit = limit; - } - needs_lock = &info->lock; - punch_hole = 1; - } - - topdir = info->i_indirect; - if (topdir && idx <= SHMEM_NR_DIRECT && !punch_hole) { - info->i_indirect = NULL; - nr_pages_to_free++; - list_add(&topdir->lru, &pages_to_free); - } - spin_unlock(&info->lock); - - if (info->swapped && idx < SHMEM_NR_DIRECT) { - ptr = info->i_direct; - size = limit; - if (size > SHMEM_NR_DIRECT) - size = SHMEM_NR_DIRECT; - nr_swaps_freed = shmem_free_swp(ptr+idx, ptr+size, needs_lock); - } - - /* - * If there are no indirect blocks or we are punching a hole - * below indirect blocks, nothing to be done. - */ - if (!topdir || limit <= SHMEM_NR_DIRECT) - goto done2; - - /* - * The truncation case has already dropped info->lock, and we're safe - * because i_size and next_index have already been lowered, preventing - * access beyond. But in the punch_hole case, we still need to take - * the lock when updating the swap directory, because there might be - * racing accesses by shmem_getpage(SGP_CACHE), shmem_unuse_inode or - * shmem_writepage. However, whenever we find we can remove a whole - * directory page (not at the misaligned start or end of the range), - * we first NULLify its pointer in the level above, and then have no - * need to take the lock when updating its contents: needs_lock and - * punch_lock (either pointing to info->lock or NULL) manage this. - */ - - upper_limit -= SHMEM_NR_DIRECT; - limit -= SHMEM_NR_DIRECT; - idx = (idx > SHMEM_NR_DIRECT)? (idx - SHMEM_NR_DIRECT): 0; - offset = idx % ENTRIES_PER_PAGE; - idx -= offset; - - dir = shmem_dir_map(topdir); - stage = ENTRIES_PER_PAGEPAGE/2; - if (idx < ENTRIES_PER_PAGEPAGE/2) { - middir = topdir; - diroff = idx/ENTRIES_PER_PAGE; - } else { - dir += ENTRIES_PER_PAGE/2; - dir += (idx - ENTRIES_PER_PAGEPAGE/2)/ENTRIES_PER_PAGEPAGE; - while (stage <= idx) - stage += ENTRIES_PER_PAGEPAGE; - middir = *dir; - if (*dir) { - diroff = ((idx - ENTRIES_PER_PAGEPAGE/2) % - ENTRIES_PER_PAGEPAGE) / ENTRIES_PER_PAGE; - if (!diroff && !offset && upper_limit >= stage) { - if (needs_lock) { - spin_lock(needs_lock); - *dir = NULL; - spin_unlock(needs_lock); - needs_lock = NULL; - } else - *dir = NULL; - nr_pages_to_free++; - list_add(&middir->lru, &pages_to_free); - } - shmem_dir_unmap(dir); - dir = shmem_dir_map(middir); - } else { - diroff = 0; - offset = 0; - idx = stage; + for (index = start; index < end; index++) { + swap = shmem_get_swap(info, index); + if (swap.val) { + free_swap_and_cache(swap); + shmem_put_swap(info, index, (swp_entry_t){0}); + info->swapped--; } } - for (; idx < limit; idx += ENTRIES_PER_PAGE, diroff++) { - if (unlikely(idx == stage)) { - shmem_dir_unmap(dir); - dir = shmem_dir_map(topdir) + - ENTRIES_PER_PAGE/2 + idx/ENTRIES_PER_PAGEPAGE; - while (!*dir) { - dir++; - idx += ENTRIES_PER_PAGEPAGE; - if (idx >= limit) - goto done1; - } - stage = idx + ENTRIES_PER_PAGEPAGE; - middir = *dir; - if (punch_hole) - needs_lock = &info->lock; - if (upper_limit >= stage) { - if (needs_lock) { - spin_lock(needs_lock); - *dir = NULL; - spin_unlock(needs_lock); - needs_lock = NULL; - } else - *dir = NULL; - nr_pages_to_free++; - list_add(&middir->lru, &pages_to_free); - } - shmem_dir_unmap(dir); - cond_resched(); - dir = shmem_dir_map(middir); - diroff = 0; - } - punch_lock = needs_lock; - subdir = dir[diroff]; - if (subdir && !offset && upper_limit-idx >= ENTRIES_PER_PAGE) { - if (needs_lock) { - spin_lock(needs_lock); - dir[diroff] = NULL; - spin_unlock(needs_lock); - punch_lock = NULL; - } else - dir[diroff] = NULL; - nr_pages_to_free++; - list_add(&subdir->lru, &pages_to_free); - } - if (subdir && page_private(subdir) /* has swap entries */) { - size = limit - idx; - if (size > ENTRIES_PER_PAGE) - size = ENTRIES_PER_PAGE; - freed = shmem_map_and_free_swp(subdir, - offset, size, &dir, punch_lock); - if (!dir) - dir = shmem_dir_map(middir); - nr_swaps_freed += freed; - if (offset || punch_lock) { - spin_lock(&info->lock); - set_page_private(subdir, - page_private(subdir) - freed); - spin_unlock(&info->lock); - } else - BUG_ON(page_private(subdir) != freed); - } - offset = 0; - } -done1: - shmem_dir_unmap(dir); -done2: - if (inode->i_mapping->nrpages && (info->flags & SHMEM_PAGEIN)) { + if (mapping->nrpages) { + spin_unlock(&info->lock); /* - * Call truncate_inode_pages again: racing shmem_unuse_inode - * may have swizzled a page in from swap since - * truncate_pagecache or generic_delete_inode did it, before we - * lowered next_index. Also, though shmem_getpage checks - * i_size before adding to cache, no recheck after: so fix the - * narrow window there too. + * A page may have meanwhile sneaked in from swap. */ - truncate_inode_pages_range(inode->i_mapping, start, end); + truncate_inode_pages_range(mapping, lstart, lend); + spin_lock(&info->lock); } - spin_lock(&info->lock); - info->flags &= ~SHMEM_TRUNCATE; - info->swapped -= nr_swaps_freed; - if (nr_pages_to_free) - shmem_free_blocks(inode, nr_pages_to_free); shmem_recalc_inode(inode); spin_unlock(&info->lock); - /* - * Empty swap vector directory pages to be freed? - */ - if (!list_empty(&pages_to_free)) { - pages_to_free.prev->next = NULL; - shmem_free_pages(pages_to_free.next); - } + inode->i_ctime = inode->i_mtime = CURRENT_TIME; } EXPORT_SYMBOL_GPL(shmem_truncate_range); @@ -797,19 +307,6 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr) if (page) unlock_page(page); } - /* - * Reset SHMEM_PAGEIN flag so that shmem_truncate can - * detect if any pages might have been added to cache - * after truncate_inode_pages. But we needn't bother - * if it's being fully truncated to zero-length: the - * nrpages check is efficient enough in that case. - */ - if (newsize) { - struct shmem_inode_info *info = SHMEM_I(inode); - spin_lock(&info->lock); - info->flags &= ~SHMEM_PAGEIN; - spin_unlock(&info->lock); - } } if (newsize != oldsize) { i_size_write(inode, newsize); @@ -859,106 +356,28 @@ static void shmem_evict_inode(struct inode *inode) end_writeback(inode); } -static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *dir, swp_entry_t *edir) -{ - swp_entry_t *ptr; - - for (ptr = dir; ptr < edir; ptr++) { - if (ptr->val == entry.val) - return ptr - dir; - } - return -1; -} - static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, struct page *page) { - struct address_space *mapping; + struct address_space *mapping = info->vfs_inode.i_mapping; unsigned long idx; - unsigned long size; - unsigned long limit; - unsigned long stage; - struct page **dir; - struct page *subdir; - swp_entry_t *ptr; - int offset; int error; - idx = 0; - ptr = info->i_direct; - spin_lock(&info->lock); - if (!info->swapped) { - list_del_init(&info->swaplist); - goto lost2; - } - limit = info->next_index; - size = limit; - if (size > SHMEM_NR_DIRECT) - size = SHMEM_NR_DIRECT; - offset = shmem_find_swp(entry, ptr, ptr+size); - if (offset >= 0) { - shmem_swp_balance_unmap(); - goto found; - } - if (!info->i_indirect) - goto lost2; - - dir = shmem_dir_map(info->i_indirect); - stage = SHMEM_NR_DIRECT + ENTRIES_PER_PAGEPAGE/2; - - for (idx = SHMEM_NR_DIRECT; idx < limit; idx += ENTRIES_PER_PAGE, dir++) { - if (unlikely(idx == stage)) { - shmem_dir_unmap(dir-1); - if (cond_resched_lock(&info->lock)) { - /* check it has not been truncated */ - if (limit > info->next_index) { - limit = info->next_index; - if (idx >= limit) - goto lost2; - } - } - dir = shmem_dir_map(info->i_indirect) + - ENTRIES_PER_PAGE/2 + idx/ENTRIES_PER_PAGEPAGE; - while (!*dir) { - dir++; - idx += ENTRIES_PER_PAGEPAGE; - if (idx >= limit) - goto lost1; - } - stage = idx + ENTRIES_PER_PAGEPAGE; - subdir = *dir; - shmem_dir_unmap(dir); - dir = shmem_dir_map(subdir); - } - subdir = *dir; - if (subdir && page_private(subdir)) { - ptr = shmem_swp_map(subdir); - size = limit - idx; - if (size > ENTRIES_PER_PAGE) - size = ENTRIES_PER_PAGE; - offset = shmem_find_swp(entry, ptr, ptr+size); - shmem_swp_unmap(ptr); - if (offset >= 0) { - shmem_dir_unmap(dir); - ptr = shmem_swp_map(subdir); - goto found; - } - } - } -lost1: - shmem_dir_unmap(dir-1); -lost2: - spin_unlock(&info->lock); + for (idx = 0; idx < SHMEM_NR_DIRECT; idx++) + if (shmem_get_swap(info, idx).val == entry.val) + goto found; return 0; found: - idx += offset; - ptr += offset; + spin_lock(&info->lock); + if (shmem_get_swap(info, idx).val != entry.val) { + spin_unlock(&info->lock); + return 0; + } /* * Move _head_ to start search for next from here. * But be careful: shmem_evict_inode checks list_empty without taking * mutex, and there's an instant in list_move_tail when info->swaplist - * would appear empty, if it were the only one on shmem_swaplist. We - * could avoid doing it if inode NULL; or use this minor optimization. + * would appear empty, if it were the only one on shmem_swaplist. */ if (shmem_swaplist.next != &info->swaplist) list_move_tail(&shmem_swaplist, &info->swaplist); @@ -968,19 +387,17 @@ found: * but also to hold up shmem_evict_inode(): so inode cannot be freed * beneath us (pagelock doesn't help until the page is in pagecache). */ - mapping = info->vfs_inode.i_mapping; error = add_to_page_cache_locked(page, mapping, idx, GFP_NOWAIT); /* which does mem_cgroup_uncharge_cache_page on error */ if (error != -ENOMEM) { delete_from_swap_cache(page); set_page_dirty(page); - info->flags |= SHMEM_PAGEIN; - shmem_swp_set(info, ptr, 0); + shmem_put_swap(info, idx, (swp_entry_t){0}); + info->swapped--; swap_free(entry); error = 1; /* not an error, but entry was found */ } - shmem_swp_unmap(ptr); spin_unlock(&info->lock); return error; } @@ -1017,7 +434,14 @@ int shmem_unuse(swp_entry_t entry, struct page *page) mutex_lock(&shmem_swaplist_mutex); list_for_each_safe(p, next, &shmem_swaplist) { info = list_entry(p, struct shmem_inode_info, swaplist); - found = shmem_unuse_inode(info, entry, page); + if (!info->swapped) { + spin_lock(&info->lock); + if (!info->swapped) + list_del_init(&info->swaplist); + spin_unlock(&info->lock); + } + if (info->swapped) + found = shmem_unuse_inode(info, entry, page); cond_resched(); if (found) break; @@ -1041,7 +465,7 @@ out: static int shmem_writepage(struct page *page, struct writeback_control *wbc) { struct shmem_inode_info *info; - swp_entry_t *entry, swap; + swp_entry_t swap, oswap; struct address_space *mapping; unsigned long index; struct inode *inode; @@ -1067,6 +491,15 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) WARN_ON_ONCE(1); /* Still happens? Tell us about it! */ goto redirty; } + + /* + * Just for this patch, we have a toy implementation, + * which can swap out only the first SHMEM_NR_DIRECT pages: + * for simple demonstration of where we need to think about swap. + */ + if (index >= SHMEM_NR_DIRECT) + goto redirty; + swap = get_swap_page(); if (!swap.val) goto redirty; @@ -1087,22 +520,19 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) spin_lock(&info->lock); mutex_unlock(&shmem_swaplist_mutex); - if (index >= info->next_index) { - BUG_ON(!(info->flags & SHMEM_TRUNCATE)); - goto unlock; - } - entry = shmem_swp_entry(info, index, NULL); - if (entry->val) { + oswap = shmem_get_swap(info, index); + if (oswap.val) { WARN_ON_ONCE(1); /* Still happens? Tell us about it! */ - free_swap_and_cache(*entry); - shmem_swp_set(info, entry, 0); + free_swap_and_cache(oswap); + shmem_put_swap(info, index, (swp_entry_t){0}); + info->swapped--; } shmem_recalc_inode(inode); if (add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) { delete_from_page_cache(page); - shmem_swp_set(info, entry, swap.val); - shmem_swp_unmap(entry); + shmem_put_swap(info, index, swap); + info->swapped++; swap_shmem_alloc(swap); spin_unlock(&info->lock); BUG_ON(page_mapped(page)); @@ -1110,13 +540,7 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) return 0; } - shmem_swp_unmap(entry); -unlock: spin_unlock(&info->lock); - /* - * add_to_swap_cache() doesn't return -EEXIST, so we can safely - * clear SWAP_HAS_CACHE flag. - */ swapcache_free(swap, NULL); redirty: set_page_dirty(page); @@ -1230,12 +654,10 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t idx, struct shmem_sb_info *sbinfo; struct page *page; struct page *prealloc_page = NULL; - swp_entry_t *entry; swp_entry_t swap; int error; - int ret; - if (idx >= SHMEM_MAX_INDEX) + if (idx > (MAX_LFS_FILESIZE >> PAGE_CACHE_SHIFT)) return -EFBIG; repeat: page = find_lock_page(mapping, idx); @@ -1272,37 +694,22 @@ repeat: spin_lock(&info->lock); shmem_recalc_inode(inode); - entry = shmem_swp_alloc(info, idx, sgp, gfp); - if (IS_ERR(entry)) { - spin_unlock(&info->lock); - error = PTR_ERR(entry); - goto out; - } - swap = *entry; - + swap = shmem_get_swap(info, idx); if (swap.val) { /* Look it up and read it in.. */ page = lookup_swap_cache(swap); if (!page) { - shmem_swp_unmap(entry); spin_unlock(&info->lock); /* here we actually do the io */ if (fault_type) *fault_type |= VM_FAULT_MAJOR; page = shmem_swapin(swap, gfp, info, idx); if (!page) { - spin_lock(&info->lock); - entry = shmem_swp_alloc(info, idx, sgp, gfp); - if (IS_ERR(entry)) - error = PTR_ERR(entry); - else { - if (entry->val == swap.val) - error = -ENOMEM; - shmem_swp_unmap(entry); - } - spin_unlock(&info->lock); - if (error) + swp_entry_t nswap = shmem_get_swap(info, idx); + if (nswap.val == swap.val) { + error = -ENOMEM; goto out; + } goto repeat; } wait_on_page_locked(page); @@ -1312,14 +719,12 @@ repeat: /* We have to do this with page locked to prevent races */ if (!trylock_page(page)) { - shmem_swp_unmap(entry); spin_unlock(&info->lock); wait_on_page_locked(page); page_cache_release(page); goto repeat; } if (PageWriteback(page)) { - shmem_swp_unmap(entry); spin_unlock(&info->lock); wait_on_page_writeback(page); unlock_page(page); @@ -1327,7 +732,6 @@ repeat: goto repeat; } if (!PageUptodate(page)) { - shmem_swp_unmap(entry); spin_unlock(&info->lock); unlock_page(page); page_cache_release(page); @@ -1338,7 +742,6 @@ repeat: error = add_to_page_cache_locked(page, mapping, idx, GFP_NOWAIT); if (error) { - shmem_swp_unmap(entry); spin_unlock(&info->lock); if (error == -ENOMEM) { /* @@ -1358,16 +761,14 @@ repeat: goto repeat; } - info->flags |= SHMEM_PAGEIN; - shmem_swp_set(info, entry, 0); - shmem_swp_unmap(entry); delete_from_swap_cache(page); + shmem_put_swap(info, idx, (swp_entry_t){0}); + info->swapped--; spin_unlock(&info->lock); set_page_dirty(page); swap_free(swap); } else if (sgp == SGP_READ) { - shmem_swp_unmap(entry); page = find_get_page(mapping, idx); if (page && !trylock_page(page)) { spin_unlock(&info->lock); @@ -1378,7 +779,6 @@ repeat: spin_unlock(&info->lock); } else if (prealloc_page) { - shmem_swp_unmap(entry); sbinfo = SHMEM_SB(inode->i_sb); if (sbinfo->max_blocks) { if (percpu_counter_compare(&sbinfo->used_blocks, @@ -1393,34 +793,24 @@ repeat: page = prealloc_page; prealloc_page = NULL; - entry = shmem_swp_alloc(info, idx, sgp, gfp); - if (IS_ERR(entry)) - error = PTR_ERR(entry); - else { - swap = *entry; - shmem_swp_unmap(entry); - } - ret = error || swap.val; - if (ret) + swap = shmem_get_swap(info, idx); + if (swap.val) mem_cgroup_uncharge_cache_page(page); else - ret = add_to_page_cache_lru(page, mapping, + error = add_to_page_cache_lru(page, mapping, idx, GFP_NOWAIT); /* * At add_to_page_cache_lru() failure, * uncharge will be done automatically. */ - if (ret) { + if (swap.val || error) { shmem_unacct_blocks(info->flags, 1); shmem_free_blocks(inode, 1); spin_unlock(&info->lock); page_cache_release(page); - if (error) - goto out; goto repeat; } - info->flags |= SHMEM_PAGEIN; info->alloced++; spin_unlock(&info->lock); clear_highpage(page); @@ -2627,7 +2017,7 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent) goto failed; sbinfo->free_inodes = sbinfo->max_inodes; - sb->s_maxbytes = SHMEM_MAX_BYTES; + sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = TMPFS_MAGIC; @@ -2863,7 +2253,7 @@ out4: void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff, struct page **pagep, swp_entry_t *ent) { - swp_entry_t entry = { .val = 0 }, *ptr; + swp_entry_t entry = { .val = 0 }; struct page *page = NULL; struct shmem_inode_info *info = SHMEM_I(inode); @@ -2871,16 +2261,13 @@ void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff, goto out; spin_lock(&info->lock); - ptr = shmem_swp_entry(info, pgoff, NULL); #ifdef CONFIG_SWAP - if (ptr && ptr->val) { - entry.val = ptr->val; + entry = shmem_get_swap(info, pgoff); + if (entry.val) page = find_get_page(&swapper_space, entry.val); - } else + else #endif page = find_get_page(inode->i_mapping, pgoff); - if (ptr) - shmem_swp_unmap(ptr); spin_unlock(&info->lock); out: *pagep = page; @@ -2963,7 +2350,6 @@ out: #define shmem_get_inode(sb, dir, mode, dev, flags) ramfs_get_inode(sb, dir, mode, dev) #define shmem_acct_size(flags, size) 0 #define shmem_unacct_size(flags, size) do {} while (0) -#define SHMEM_MAX_BYTES MAX_LFS_FILESIZE #endif /* CONFIG_SHMEM */ @@ -2987,7 +2373,7 @@ struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags if (IS_ERR(shm_mnt)) return (void *)shm_mnt; - if (size < 0 || size > SHMEM_MAX_BYTES) + if (size < 0 || size > MAX_LFS_FILESIZE) return ERR_PTR(-EINVAL); if (shmem_acct_size(flags, size)) -- cgit v1.2.3 From 41ffe5d5ceef7f7ff2ff18e320d88ca6d629efaf Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 3 Aug 2011 16:21:21 -0700 Subject: tmpfs: miscellaneous trivial cleanups While it's at its least, make a number of boring nitpicky cleanups to shmem.c, mostly for consistency of variable naming. Things like "swap" instead of "entry", "pgoff_t index" instead of "unsigned long idx". And since everything else here is prefixed "shmem_", better change init_tmpfs() to shmem_init(). Signed-off-by: Hugh Dickins Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/shmem_fs.h | 2 +- init/main.c | 2 +- mm/shmem.c | 216 +++++++++++++++++++++++------------------------ 3 files changed, 109 insertions(+), 111 deletions(-) (limited to 'include') diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 80b695213fdb..3f05795dcf7b 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -47,7 +47,7 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode) /* * Functions in mm/shmem.c called directly from elsewhere: */ -extern int init_tmpfs(void); +extern int shmem_init(void); extern int shmem_fill_super(struct super_block *sb, void *data, int silent); extern struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags); diff --git a/init/main.c b/init/main.c index d7211faed2ad..1952d37e4ecb 100644 --- a/init/main.c +++ b/init/main.c @@ -715,7 +715,7 @@ static void __init do_basic_setup(void) { cpuset_init_smp(); usermodehelper_init(); - init_tmpfs(); + shmem_init(); driver_init(); init_irq_proc(); do_ctors(); diff --git a/mm/shmem.c b/mm/shmem.c index 5574b00ca771..24e95ac16053 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -28,7 +28,6 @@ #include #include #include -#include #include static struct vfsmount *shm_mnt; @@ -51,6 +50,7 @@ static struct vfsmount *shm_mnt; #include #include #include +#include #include #include #include @@ -63,7 +63,6 @@ static struct vfsmount *shm_mnt; #include #include -#include #include #define BLOCKS_PER_PAGE (PAGE_CACHE_SIZE/512) @@ -201,7 +200,7 @@ static void shmem_free_inode(struct super_block *sb) } /** - * shmem_recalc_inode - recalculate the size of an inode + * shmem_recalc_inode - recalculate the block usage of an inode * @inode: inode to recalc * * We have to calculate the free blocks since the mm can drop @@ -356,19 +355,20 @@ static void shmem_evict_inode(struct inode *inode) end_writeback(inode); } -static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, struct page *page) +static int shmem_unuse_inode(struct shmem_inode_info *info, + swp_entry_t swap, struct page *page) { struct address_space *mapping = info->vfs_inode.i_mapping; - unsigned long idx; + pgoff_t index; int error; - for (idx = 0; idx < SHMEM_NR_DIRECT; idx++) - if (shmem_get_swap(info, idx).val == entry.val) + for (index = 0; index < SHMEM_NR_DIRECT; index++) + if (shmem_get_swap(info, index).val == swap.val) goto found; return 0; found: spin_lock(&info->lock); - if (shmem_get_swap(info, idx).val != entry.val) { + if (shmem_get_swap(info, index).val != swap.val) { spin_unlock(&info->lock); return 0; } @@ -387,15 +387,15 @@ found: * but also to hold up shmem_evict_inode(): so inode cannot be freed * beneath us (pagelock doesn't help until the page is in pagecache). */ - error = add_to_page_cache_locked(page, mapping, idx, GFP_NOWAIT); + error = add_to_page_cache_locked(page, mapping, index, GFP_NOWAIT); /* which does mem_cgroup_uncharge_cache_page on error */ if (error != -ENOMEM) { delete_from_swap_cache(page); set_page_dirty(page); - shmem_put_swap(info, idx, (swp_entry_t){0}); + shmem_put_swap(info, index, (swp_entry_t){0}); info->swapped--; - swap_free(entry); + swap_free(swap); error = 1; /* not an error, but entry was found */ } spin_unlock(&info->lock); @@ -405,9 +405,9 @@ found: /* * shmem_unuse() search for an eventually swapped out shmem page. */ -int shmem_unuse(swp_entry_t entry, struct page *page) +int shmem_unuse(swp_entry_t swap, struct page *page) { - struct list_head *p, *next; + struct list_head *this, *next; struct shmem_inode_info *info; int found = 0; int error; @@ -432,8 +432,8 @@ int shmem_unuse(swp_entry_t entry, struct page *page) radix_tree_preload_end(); mutex_lock(&shmem_swaplist_mutex); - list_for_each_safe(p, next, &shmem_swaplist) { - info = list_entry(p, struct shmem_inode_info, swaplist); + list_for_each_safe(this, next, &shmem_swaplist) { + info = list_entry(this, struct shmem_inode_info, swaplist); if (!info->swapped) { spin_lock(&info->lock); if (!info->swapped) @@ -441,7 +441,7 @@ int shmem_unuse(swp_entry_t entry, struct page *page) spin_unlock(&info->lock); } if (info->swapped) - found = shmem_unuse_inode(info, entry, page); + found = shmem_unuse_inode(info, swap, page); cond_resched(); if (found) break; @@ -467,7 +467,7 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) struct shmem_inode_info *info; swp_entry_t swap, oswap; struct address_space *mapping; - unsigned long index; + pgoff_t index; struct inode *inode; BUG_ON(!PageLocked(page)); @@ -577,35 +577,33 @@ static struct mempolicy *shmem_get_sbmpol(struct shmem_sb_info *sbinfo) } #endif /* CONFIG_TMPFS */ -static struct page *shmem_swapin(swp_entry_t entry, gfp_t gfp, - struct shmem_inode_info *info, unsigned long idx) +static struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp, + struct shmem_inode_info *info, pgoff_t index) { struct mempolicy mpol, *spol; struct vm_area_struct pvma; - struct page *page; spol = mpol_cond_copy(&mpol, - mpol_shared_policy_lookup(&info->policy, idx)); + mpol_shared_policy_lookup(&info->policy, index)); /* Create a pseudo vma that just contains the policy */ pvma.vm_start = 0; - pvma.vm_pgoff = idx; + pvma.vm_pgoff = index; pvma.vm_ops = NULL; pvma.vm_policy = spol; - page = swapin_readahead(entry, gfp, &pvma, 0); - return page; + return swapin_readahead(swap, gfp, &pvma, 0); } static struct page *shmem_alloc_page(gfp_t gfp, - struct shmem_inode_info *info, unsigned long idx) + struct shmem_inode_info *info, pgoff_t index) { struct vm_area_struct pvma; /* Create a pseudo vma that just contains the policy */ pvma.vm_start = 0; - pvma.vm_pgoff = idx; + pvma.vm_pgoff = index; pvma.vm_ops = NULL; - pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, idx); + pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, index); /* * alloc_page_vma() will drop the shared policy reference @@ -614,19 +612,19 @@ static struct page *shmem_alloc_page(gfp_t gfp, } #else /* !CONFIG_NUMA */ #ifdef CONFIG_TMPFS -static inline void shmem_show_mpol(struct seq_file *seq, struct mempolicy *p) +static inline void shmem_show_mpol(struct seq_file *seq, struct mempolicy *mpol) { } #endif /* CONFIG_TMPFS */ -static inline struct page *shmem_swapin(swp_entry_t entry, gfp_t gfp, - struct shmem_inode_info *info, unsigned long idx) +static inline struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp, + struct shmem_inode_info *info, pgoff_t index) { - return swapin_readahead(entry, gfp, NULL, 0); + return swapin_readahead(swap, gfp, NULL, 0); } static inline struct page *shmem_alloc_page(gfp_t gfp, - struct shmem_inode_info *info, unsigned long idx) + struct shmem_inode_info *info, pgoff_t index) { return alloc_page(gfp); } @@ -646,7 +644,7 @@ static inline struct mempolicy *shmem_get_sbmpol(struct shmem_sb_info *sbinfo) * vm. If we swap it in we mark it dirty since we also free the swap * entry since a page cannot live in both the swap and page cache */ -static int shmem_getpage_gfp(struct inode *inode, pgoff_t idx, +static int shmem_getpage_gfp(struct inode *inode, pgoff_t index, struct page **pagep, enum sgp_type sgp, gfp_t gfp, int *fault_type) { struct address_space *mapping = inode->i_mapping; @@ -657,10 +655,10 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t idx, swp_entry_t swap; int error; - if (idx > (MAX_LFS_FILESIZE >> PAGE_CACHE_SHIFT)) + if (index > (MAX_LFS_FILESIZE >> PAGE_CACHE_SHIFT)) return -EFBIG; repeat: - page = find_lock_page(mapping, idx); + page = find_lock_page(mapping, index); if (page) { /* * Once we can get the page lock, it must be uptodate: @@ -681,7 +679,7 @@ repeat: radix_tree_preload_end(); if (sgp != SGP_READ && !prealloc_page) { - prealloc_page = shmem_alloc_page(gfp, info, idx); + prealloc_page = shmem_alloc_page(gfp, info, index); if (prealloc_page) { SetPageSwapBacked(prealloc_page); if (mem_cgroup_cache_charge(prealloc_page, @@ -694,7 +692,7 @@ repeat: spin_lock(&info->lock); shmem_recalc_inode(inode); - swap = shmem_get_swap(info, idx); + swap = shmem_get_swap(info, index); if (swap.val) { /* Look it up and read it in.. */ page = lookup_swap_cache(swap); @@ -703,9 +701,9 @@ repeat: /* here we actually do the io */ if (fault_type) *fault_type |= VM_FAULT_MAJOR; - page = shmem_swapin(swap, gfp, info, idx); + page = shmem_swapin(swap, gfp, info, index); if (!page) { - swp_entry_t nswap = shmem_get_swap(info, idx); + swp_entry_t nswap = shmem_get_swap(info, index); if (nswap.val == swap.val) { error = -ENOMEM; goto out; @@ -740,7 +738,7 @@ repeat: } error = add_to_page_cache_locked(page, mapping, - idx, GFP_NOWAIT); + index, GFP_NOWAIT); if (error) { spin_unlock(&info->lock); if (error == -ENOMEM) { @@ -762,14 +760,14 @@ repeat: } delete_from_swap_cache(page); - shmem_put_swap(info, idx, (swp_entry_t){0}); + shmem_put_swap(info, index, (swp_entry_t){0}); info->swapped--; spin_unlock(&info->lock); set_page_dirty(page); swap_free(swap); } else if (sgp == SGP_READ) { - page = find_get_page(mapping, idx); + page = find_get_page(mapping, index); if (page && !trylock_page(page)) { spin_unlock(&info->lock); wait_on_page_locked(page); @@ -793,12 +791,12 @@ repeat: page = prealloc_page; prealloc_page = NULL; - swap = shmem_get_swap(info, idx); + swap = shmem_get_swap(info, index); if (swap.val) mem_cgroup_uncharge_cache_page(page); else error = add_to_page_cache_lru(page, mapping, - idx, GFP_NOWAIT); + index, GFP_NOWAIT); /* * At add_to_page_cache_lru() failure, * uncharge will be done automatically. @@ -841,7 +839,7 @@ nospace: * but must also avoid reporting a spurious ENOSPC while working on a * full tmpfs. */ - page = find_get_page(mapping, idx); + page = find_get_page(mapping, index); spin_unlock(&info->lock); if (page) { page_cache_release(page); @@ -872,20 +870,20 @@ static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) } #ifdef CONFIG_NUMA -static int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *new) +static int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *mpol) { - struct inode *i = vma->vm_file->f_path.dentry->d_inode; - return mpol_set_shared_policy(&SHMEM_I(i)->policy, vma, new); + struct inode *inode = vma->vm_file->f_path.dentry->d_inode; + return mpol_set_shared_policy(&SHMEM_I(inode)->policy, vma, mpol); } static struct mempolicy *shmem_get_policy(struct vm_area_struct *vma, unsigned long addr) { - struct inode *i = vma->vm_file->f_path.dentry->d_inode; - unsigned long idx; + struct inode *inode = vma->vm_file->f_path.dentry->d_inode; + pgoff_t index; - idx = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; - return mpol_shared_policy_lookup(&SHMEM_I(i)->policy, idx); + index = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; + return mpol_shared_policy_lookup(&SHMEM_I(inode)->policy, index); } #endif @@ -1016,7 +1014,8 @@ static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_ { struct inode *inode = filp->f_path.dentry->d_inode; struct address_space *mapping = inode->i_mapping; - unsigned long index, offset; + pgoff_t index; + unsigned long offset; enum sgp_type sgp = SGP_READ; /* @@ -1032,7 +1031,8 @@ static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_ for (;;) { struct page *page = NULL; - unsigned long end_index, nr, ret; + pgoff_t end_index; + unsigned long nr, ret; loff_t i_size = i_size_read(inode); end_index = i_size >> PAGE_CACHE_SHIFT; @@ -1270,8 +1270,9 @@ static int shmem_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_namelen = NAME_MAX; if (sbinfo->max_blocks) { buf->f_blocks = sbinfo->max_blocks; - buf->f_bavail = buf->f_bfree = - sbinfo->max_blocks - percpu_counter_sum(&sbinfo->used_blocks); + buf->f_bavail = + buf->f_bfree = sbinfo->max_blocks - + percpu_counter_sum(&sbinfo->used_blocks); } if (sbinfo->max_inodes) { buf->f_files = sbinfo->max_inodes; @@ -1480,8 +1481,8 @@ static void *shmem_follow_link_inline(struct dentry *dentry, struct nameidata *n static void *shmem_follow_link(struct dentry *dentry, struct nameidata *nd) { struct page *page = NULL; - int res = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ, NULL); - nd_set_link(nd, res ? ERR_PTR(res) : kmap(page)); + int error = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ, NULL); + nd_set_link(nd, error ? ERR_PTR(error) : kmap(page)); if (page) unlock_page(page); return page; @@ -1592,7 +1593,6 @@ out: return err; } - static const struct xattr_handler *shmem_xattr_handlers[] = { #ifdef CONFIG_TMPFS_POSIX_ACL &generic_acl_access_handler, @@ -2052,14 +2052,14 @@ static struct kmem_cache *shmem_inode_cachep; static struct inode *shmem_alloc_inode(struct super_block *sb) { - struct shmem_inode_info *p; - p = (struct shmem_inode_info *)kmem_cache_alloc(shmem_inode_cachep, GFP_KERNEL); - if (!p) + struct shmem_inode_info *info; + info = kmem_cache_alloc(shmem_inode_cachep, GFP_KERNEL); + if (!info) return NULL; - return &p->vfs_inode; + return &info->vfs_inode; } -static void shmem_i_callback(struct rcu_head *head) +static void shmem_destroy_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); INIT_LIST_HEAD(&inode->i_dentry); @@ -2072,25 +2072,24 @@ static void shmem_destroy_inode(struct inode *inode) /* only struct inode is valid if it's an inline symlink */ mpol_free_shared_policy(&SHMEM_I(inode)->policy); } - call_rcu(&inode->i_rcu, shmem_i_callback); + call_rcu(&inode->i_rcu, shmem_destroy_callback); } -static void init_once(void *foo) +static void shmem_init_inode(void *foo) { - struct shmem_inode_info *p = (struct shmem_inode_info *) foo; - - inode_init_once(&p->vfs_inode); + struct shmem_inode_info *info = foo; + inode_init_once(&info->vfs_inode); } -static int init_inodecache(void) +static int shmem_init_inodecache(void) { shmem_inode_cachep = kmem_cache_create("shmem_inode_cache", sizeof(struct shmem_inode_info), - 0, SLAB_PANIC, init_once); + 0, SLAB_PANIC, shmem_init_inode); return 0; } -static void destroy_inodecache(void) +static void shmem_destroy_inodecache(void) { kmem_cache_destroy(shmem_inode_cachep); } @@ -2187,21 +2186,20 @@ static const struct vm_operations_struct shmem_vm_ops = { #endif }; - static struct dentry *shmem_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_nodev(fs_type, flags, data, shmem_fill_super); } -static struct file_system_type tmpfs_fs_type = { +static struct file_system_type shmem_fs_type = { .owner = THIS_MODULE, .name = "tmpfs", .mount = shmem_mount, .kill_sb = kill_litter_super, }; -int __init init_tmpfs(void) +int __init shmem_init(void) { int error; @@ -2209,18 +2207,18 @@ int __init init_tmpfs(void) if (error) goto out4; - error = init_inodecache(); + error = shmem_init_inodecache(); if (error) goto out3; - error = register_filesystem(&tmpfs_fs_type); + error = register_filesystem(&shmem_fs_type); if (error) { printk(KERN_ERR "Could not register tmpfs\n"); goto out2; } - shm_mnt = vfs_kern_mount(&tmpfs_fs_type, MS_NOUSER, - tmpfs_fs_type.name, NULL); + shm_mnt = vfs_kern_mount(&shmem_fs_type, MS_NOUSER, + shmem_fs_type.name, NULL); if (IS_ERR(shm_mnt)) { error = PTR_ERR(shm_mnt); printk(KERN_ERR "Could not kern_mount tmpfs\n"); @@ -2229,9 +2227,9 @@ int __init init_tmpfs(void) return 0; out1: - unregister_filesystem(&tmpfs_fs_type); + unregister_filesystem(&shmem_fs_type); out2: - destroy_inodecache(); + shmem_destroy_inodecache(); out3: bdi_destroy(&shmem_backing_dev_info); out4: @@ -2241,37 +2239,37 @@ out4: #ifdef CONFIG_CGROUP_MEM_RES_CTLR /** - * mem_cgroup_get_shmem_target - find a page or entry assigned to the shmem file + * mem_cgroup_get_shmem_target - find page or swap assigned to the shmem file * @inode: the inode to be searched - * @pgoff: the offset to be searched + * @index: the page offset to be searched * @pagep: the pointer for the found page to be stored - * @ent: the pointer for the found swap entry to be stored + * @swapp: the pointer for the found swap entry to be stored * * If a page is found, refcount of it is incremented. Callers should handle * these refcount. */ -void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff, - struct page **pagep, swp_entry_t *ent) +void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t index, + struct page **pagep, swp_entry_t *swapp) { - swp_entry_t entry = { .val = 0 }; - struct page *page = NULL; struct shmem_inode_info *info = SHMEM_I(inode); + struct page *page = NULL; + swp_entry_t swap = {0}; - if ((pgoff << PAGE_CACHE_SHIFT) >= i_size_read(inode)) + if ((index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) goto out; spin_lock(&info->lock); #ifdef CONFIG_SWAP - entry = shmem_get_swap(info, pgoff); - if (entry.val) - page = find_get_page(&swapper_space, entry.val); + swap = shmem_get_swap(info, index); + if (swap.val) + page = find_get_page(&swapper_space, swap.val); else #endif - page = find_get_page(inode->i_mapping, pgoff); + page = find_get_page(inode->i_mapping, index); spin_unlock(&info->lock); out: *pagep = page; - *ent = entry; + *swapp = swap; } #endif @@ -2288,23 +2286,23 @@ out: #include -static struct file_system_type tmpfs_fs_type = { +static struct file_system_type shmem_fs_type = { .name = "tmpfs", .mount = ramfs_mount, .kill_sb = kill_litter_super, }; -int __init init_tmpfs(void) +int __init shmem_init(void) { - BUG_ON(register_filesystem(&tmpfs_fs_type) != 0); + BUG_ON(register_filesystem(&shmem_fs_type) != 0); - shm_mnt = kern_mount(&tmpfs_fs_type); + shm_mnt = kern_mount(&shmem_fs_type); BUG_ON(IS_ERR(shm_mnt)); return 0; } -int shmem_unuse(swp_entry_t entry, struct page *page) +int shmem_unuse(swp_entry_t swap, struct page *page) { return 0; } @@ -2314,34 +2312,34 @@ int shmem_lock(struct file *file, int lock, struct user_struct *user) return 0; } -void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end) +void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend) { - truncate_inode_pages_range(inode->i_mapping, start, end); + truncate_inode_pages_range(inode->i_mapping, lstart, lend); } EXPORT_SYMBOL_GPL(shmem_truncate_range); #ifdef CONFIG_CGROUP_MEM_RES_CTLR /** - * mem_cgroup_get_shmem_target - find a page or entry assigned to the shmem file + * mem_cgroup_get_shmem_target - find page or swap assigned to the shmem file * @inode: the inode to be searched - * @pgoff: the offset to be searched + * @index: the page offset to be searched * @pagep: the pointer for the found page to be stored - * @ent: the pointer for the found swap entry to be stored + * @swapp: the pointer for the found swap entry to be stored * * If a page is found, refcount of it is incremented. Callers should handle * these refcount. */ -void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff, - struct page **pagep, swp_entry_t *ent) +void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t index, + struct page **pagep, swp_entry_t *swapp) { struct page *page = NULL; - if ((pgoff << PAGE_CACHE_SHIFT) >= i_size_read(inode)) + if ((index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) goto out; - page = find_get_page(inode->i_mapping, pgoff); + page = find_get_page(inode->i_mapping, index); out: *pagep = page; - *ent = (swp_entry_t){ .val = 0 }; + *swapp = (swp_entry_t){0}; } #endif -- cgit v1.2.3 From aa3b189551ad8e5cc1d9c663735c131650238278 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 3 Aug 2011 16:21:24 -0700 Subject: tmpfs: convert mem_cgroup shmem to radix-swap Remove mem_cgroup_shmem_charge_fallback(): it was only required when we had to move swappage to filecache with GFP_NOWAIT. Remove the GFP_NOWAIT special case from mem_cgroup_cache_charge(), by moving its call out from shmem_add_to_page_cache() to two of thats three callers. But leave it doing mem_cgroup_uncharge_cache_page() on error: although asymmetrical, it's easier for all 3 callers to handle. These two changes would also be appropriate if anyone were to start using shmem_read_mapping_page_gfp() with GFP_NOWAIT. Remove mem_cgroup_get_shmem_target(): mc_handle_file_pte() can test radix_tree_exceptional_entry() to get what it needs for itself. Signed-off-by: Hugh Dickins Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 8 ----- include/linux/shmem_fs.h | 2 -- mm/memcontrol.c | 66 +++++------------------------------- mm/shmem.c | 83 ++++++---------------------------------------- 4 files changed, 20 insertions(+), 139 deletions(-) (limited to 'include') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index b96600786913..3b535db00a94 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -86,8 +86,6 @@ extern void mem_cgroup_uncharge_end(void); extern void mem_cgroup_uncharge_page(struct page *page); extern void mem_cgroup_uncharge_cache_page(struct page *page); -extern int mem_cgroup_shmem_charge_fallback(struct page *page, - struct mm_struct *mm, gfp_t gfp_mask); extern void mem_cgroup_out_of_memory(struct mem_cgroup *mem, gfp_t gfp_mask); int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem); @@ -225,12 +223,6 @@ static inline void mem_cgroup_uncharge_cache_page(struct page *page) { } -static inline int mem_cgroup_shmem_charge_fallback(struct page *page, - struct mm_struct *mm, gfp_t gfp_mask) -{ - return 0; -} - static inline void mem_cgroup_add_lru_list(struct page *page, int lru) { } diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 3f05795dcf7b..0c8e952df594 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -57,8 +57,6 @@ extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping, pgoff_t index, gfp_t gfp_mask); extern void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end); extern int shmem_unuse(swp_entry_t entry, struct page *page); -extern void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff, - struct page **pagep, swp_entry_t *ent); static inline struct page *shmem_read_mapping_page( struct address_space *mapping, pgoff_t index) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 5f84d2351ddb..f4ec4e7ca4cd 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -2873,30 +2872,6 @@ int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, return 0; if (PageCompound(page)) return 0; - /* - * Corner case handling. This is called from add_to_page_cache() - * in usual. But some FS (shmem) precharges this page before calling it - * and call add_to_page_cache() with GFP_NOWAIT. - * - * For GFP_NOWAIT case, the page may be pre-charged before calling - * add_to_page_cache(). (See shmem.c) check it here and avoid to call - * charge twice. (It works but has to pay a bit larger cost.) - * And when the page is SwapCache, it should take swap information - * into account. This is under lock_page() now. - */ - if (!(gfp_mask & __GFP_WAIT)) { - struct page_cgroup *pc; - - pc = lookup_page_cgroup(page); - if (!pc) - return 0; - lock_page_cgroup(pc); - if (PageCgroupUsed(pc)) { - unlock_page_cgroup(pc); - return 0; - } - unlock_page_cgroup(pc); - } if (unlikely(!mm)) mm = &init_mm; @@ -3486,31 +3461,6 @@ void mem_cgroup_end_migration(struct mem_cgroup *mem, cgroup_release_and_wakeup_rmdir(&mem->css); } -/* - * A call to try to shrink memory usage on charge failure at shmem's swapin. - * Calling hierarchical_reclaim is not enough because we should update - * last_oom_jiffies to prevent pagefault_out_of_memory from invoking global OOM. - * Moreover considering hierarchy, we should reclaim from the mem_over_limit, - * not from the memcg which this page would be charged to. - * try_charge_swapin does all of these works properly. - */ -int mem_cgroup_shmem_charge_fallback(struct page *page, - struct mm_struct *mm, - gfp_t gfp_mask) -{ - struct mem_cgroup *mem; - int ret; - - if (mem_cgroup_disabled()) - return 0; - - ret = mem_cgroup_try_charge_swapin(mm, page, gfp_mask, &mem); - if (!ret) - mem_cgroup_cancel_charge_swapin(mem); /* it does !mem check */ - - return ret; -} - #ifdef CONFIG_DEBUG_VM static struct page_cgroup *lookup_page_cgroup_used(struct page *page) { @@ -5330,15 +5280,17 @@ static struct page *mc_handle_file_pte(struct vm_area_struct *vma, pgoff = pte_to_pgoff(ptent); /* page is moved even if it's not RSS of this task(page-faulted). */ - if (!mapping_cap_swap_backed(mapping)) { /* normal file */ - page = find_get_page(mapping, pgoff); - } else { /* shmem/tmpfs file. we should take account of swap too. */ - swp_entry_t ent; - mem_cgroup_get_shmem_target(inode, pgoff, &page, &ent); + page = find_get_page(mapping, pgoff); + +#ifdef CONFIG_SWAP + /* shmem/tmpfs may report page out on swap: account for that too. */ + if (radix_tree_exceptional_entry(page)) { + swp_entry_t swap = radix_to_swp_entry(page); if (do_swap_account) - entry->val = ent.val; + *entry = swap; + page = find_get_page(&swapper_space, swap.val); } - +#endif return page; } diff --git a/mm/shmem.c b/mm/shmem.c index 92f01d7cc150..13ef2d7e912d 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -262,15 +262,11 @@ static int shmem_add_to_page_cache(struct page *page, struct address_space *mapping, pgoff_t index, gfp_t gfp, void *expected) { - int error; + int error = 0; VM_BUG_ON(!PageLocked(page)); VM_BUG_ON(!PageSwapBacked(page)); - error = mem_cgroup_cache_charge(page, current->mm, - gfp & GFP_RECLAIM_MASK); - if (error) - goto out; if (!expected) error = radix_tree_preload(gfp & GFP_RECLAIM_MASK); if (!error) { @@ -300,7 +296,6 @@ static int shmem_add_to_page_cache(struct page *page, } if (error) mem_cgroup_uncharge_cache_page(page); -out: return error; } @@ -660,7 +655,6 @@ int shmem_unuse(swp_entry_t swap, struct page *page) * Charge page using GFP_KERNEL while we can wait, before taking * the shmem_swaplist_mutex which might hold up shmem_writepage(). * Charged back to the user (not to caller) when swap account is used. - * shmem_add_to_page_cache() will be called with GFP_NOWAIT. */ error = mem_cgroup_cache_charge(page, current->mm, GFP_KERNEL); if (error) @@ -954,8 +948,11 @@ repeat: goto failed; } - error = shmem_add_to_page_cache(page, mapping, index, - gfp, swp_to_radix_entry(swap)); + error = mem_cgroup_cache_charge(page, current->mm, + gfp & GFP_RECLAIM_MASK); + if (!error) + error = shmem_add_to_page_cache(page, mapping, index, + gfp, swp_to_radix_entry(swap)); if (error) goto failed; @@ -990,8 +987,11 @@ repeat: SetPageSwapBacked(page); __set_page_locked(page); - error = shmem_add_to_page_cache(page, mapping, index, - gfp, NULL); + error = mem_cgroup_cache_charge(page, current->mm, + gfp & GFP_RECLAIM_MASK); + if (!error) + error = shmem_add_to_page_cache(page, mapping, index, + gfp, NULL); if (error) goto decused; lru_cache_add_anon(page); @@ -2442,42 +2442,6 @@ out4: return error; } -#ifdef CONFIG_CGROUP_MEM_RES_CTLR -/** - * mem_cgroup_get_shmem_target - find page or swap assigned to the shmem file - * @inode: the inode to be searched - * @index: the page offset to be searched - * @pagep: the pointer for the found page to be stored - * @swapp: the pointer for the found swap entry to be stored - * - * If a page is found, refcount of it is incremented. Callers should handle - * these refcount. - */ -void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t index, - struct page **pagep, swp_entry_t *swapp) -{ - struct shmem_inode_info *info = SHMEM_I(inode); - struct page *page = NULL; - swp_entry_t swap = {0}; - - if ((index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) - goto out; - - spin_lock(&info->lock); -#ifdef CONFIG_SWAP - swap = shmem_get_swap(info, index); - if (swap.val) - page = find_get_page(&swapper_space, swap.val); - else -#endif - page = find_get_page(inode->i_mapping, index); - spin_unlock(&info->lock); -out: - *pagep = page; - *swapp = swap; -} -#endif - #else /* !CONFIG_SHMEM */ /* @@ -2523,31 +2487,6 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend) } EXPORT_SYMBOL_GPL(shmem_truncate_range); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR -/** - * mem_cgroup_get_shmem_target - find page or swap assigned to the shmem file - * @inode: the inode to be searched - * @index: the page offset to be searched - * @pagep: the pointer for the found page to be stored - * @swapp: the pointer for the found swap entry to be stored - * - * If a page is found, refcount of it is incremented. Callers should handle - * these refcount. - */ -void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t index, - struct page **pagep, swp_entry_t *swapp) -{ - struct page *page = NULL; - - if ((index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) - goto out; - page = find_get_page(inode->i_mapping, index); -out: - *pagep = page; - *swapp = (swp_entry_t){0}; -} -#endif - #define shmem_vm_ops generic_file_vm_ops #define shmem_file_operations ramfs_file_operations #define shmem_get_inode(sb, dir, mode, dev, flags) ramfs_get_inode(sb, dir, mode, dev) -- cgit v1.2.3 From 69f07ec938712b58755add82dd3d0b35f01317cc Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 3 Aug 2011 16:21:26 -0700 Subject: tmpfs: use kmemdup for short symlinks But we've not yet removed the old swp_entry_t i_direct[16] from shmem_inode_info. That's because it was still being shared with the inline symlink. Remove it now (saving 64 or 128 bytes from shmem inode size), and use kmemdup() for short symlinks, say, those up to 128 bytes. I wonder why mpol_free_shared_policy() is done in shmem_destroy_inode() rather than shmem_evict_inode(), where we usually do such freeing? I guess it doesn't matter, and I'm not into NUMA mpol testing right now. Signed-off-by: Hugh Dickins Acked-by: Rik van Riel Reviewed-by: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/shmem_fs.h | 11 +++-------- mm/shmem.c | 31 ++++++++++++++++++------------- 2 files changed, 21 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 0c8e952df594..9291ac3cc627 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -8,20 +8,15 @@ /* inode in-kernel data */ -#define SHMEM_NR_DIRECT 16 - -#define SHMEM_SYMLINK_INLINE_LEN (SHMEM_NR_DIRECT * sizeof(swp_entry_t)) - struct shmem_inode_info { spinlock_t lock; unsigned long flags; unsigned long alloced; /* data pages alloced to file */ - unsigned long swapped; /* subtotal assigned to swap */ - struct shared_policy policy; /* NUMA memory alloc policy */ union { - swp_entry_t i_direct[SHMEM_NR_DIRECT]; /* first blocks */ - char inline_symlink[SHMEM_SYMLINK_INLINE_LEN]; + unsigned long swapped; /* subtotal assigned to swap */ + char *symlink; /* unswappable short symlink */ }; + struct shared_policy policy; /* NUMA memory alloc policy */ struct list_head swaplist; /* chain of maybes on swap */ struct list_head xattr_list; /* list of shmem_xattr */ struct inode vfs_inode; diff --git a/mm/shmem.c b/mm/shmem.c index 0f094a258526..3a5be0feb6af 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -73,6 +73,9 @@ static struct vfsmount *shm_mnt; /* Pretend that each entry is of this size in directory's i_size */ #define BOGO_DIRENT_SIZE 20 +/* Symlink up to this size is kmalloc'ed instead of using a swappable page */ +#define SHORT_SYMLINK_LEN 128 + struct shmem_xattr { struct list_head list; /* anchored by shmem_inode_info->xattr_list */ char *name; /* xattr name */ @@ -585,7 +588,8 @@ static void shmem_evict_inode(struct inode *inode) list_del_init(&info->swaplist); mutex_unlock(&shmem_swaplist_mutex); } - } + } else + kfree(info->symlink); list_for_each_entry_safe(xattr, nxattr, &info->xattr_list, list) { kfree(xattr->name); @@ -1173,7 +1177,7 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode #ifdef CONFIG_TMPFS static const struct inode_operations shmem_symlink_inode_operations; -static const struct inode_operations shmem_symlink_inline_operations; +static const struct inode_operations shmem_short_symlink_operations; static int shmem_write_begin(struct file *file, struct address_space *mapping, @@ -1638,10 +1642,13 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s info = SHMEM_I(inode); inode->i_size = len-1; - if (len <= SHMEM_SYMLINK_INLINE_LEN) { - /* do it inline */ - memcpy(info->inline_symlink, symname, len); - inode->i_op = &shmem_symlink_inline_operations; + if (len <= SHORT_SYMLINK_LEN) { + info->symlink = kmemdup(symname, len, GFP_KERNEL); + if (!info->symlink) { + iput(inode); + return -ENOMEM; + } + inode->i_op = &shmem_short_symlink_operations; } else { error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL); if (error) { @@ -1664,9 +1671,9 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s return 0; } -static void *shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd) +static void *shmem_follow_short_symlink(struct dentry *dentry, struct nameidata *nd) { - nd_set_link(nd, SHMEM_I(dentry->d_inode)->inline_symlink); + nd_set_link(nd, SHMEM_I(dentry->d_inode)->symlink); return NULL; } @@ -1914,9 +1921,9 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) } #endif /* CONFIG_TMPFS_XATTR */ -static const struct inode_operations shmem_symlink_inline_operations = { +static const struct inode_operations shmem_short_symlink_operations = { .readlink = generic_readlink, - .follow_link = shmem_follow_link_inline, + .follow_link = shmem_follow_short_symlink, #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr, .getxattr = shmem_getxattr, @@ -2259,10 +2266,8 @@ static void shmem_destroy_callback(struct rcu_head *head) static void shmem_destroy_inode(struct inode *inode) { - if ((inode->i_mode & S_IFMT) == S_IFREG) { - /* only struct inode is valid if it's an inline symlink */ + if ((inode->i_mode & S_IFMT) == S_IFREG) mpol_free_shared_policy(&SHMEM_I(inode)->policy); - } call_rcu(&inode->i_rcu, shmem_destroy_callback); } -- cgit v1.2.3 From e504f3fdd63d486d45b18009e5a65f2e329acb0a Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 3 Aug 2011 16:21:27 -0700 Subject: tmpfs radix_tree: locate_item to speed up swapoff We have already acknowledged that swapoff of a tmpfs file is slower than it was before conversion to the generic radix_tree: a little slower there will be acceptable, if the hotter paths are faster. But it was a shock to find swapoff of a 500MB file 20 times slower on my laptop, taking 10 minutes; and at that rate it significantly slows down my testing. Now, most of that turned out to be overhead from PROVE_LOCKING and PROVE_RCU: without those it was only 4 times slower than before; and more realistic tests on other machines don't fare as badly. I've tried a number of things to improve it, including tagging the swap entries, then doing lookup by tag: I'd expected that to halve the time, but in practice it's erratic, and often counter-productive. The only change I've so far found to make a consistent improvement, is to short-circuit the way we go back and forth, gang lookup packing entries into the array supplied, then shmem scanning that array for the target entry. Scanning in place doubles the speed, so it's now only twice as slow as before (or three times slower when the PROVEs are on). So, add radix_tree_locate_item() as an expedient, once-off, single-caller hack to do the lookup directly in place. #ifdef it on CONFIG_SHMEM and CONFIG_SWAP, as much to document its limited applicability as save space in other configurations. And, sadly, #include sched.h for cond_resched(). Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/radix-tree.h | 1 + lib/radix-tree.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++ mm/shmem.c | 38 +------------------ 3 files changed, 94 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index b7edf8251455..9d4539c52e53 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h @@ -252,6 +252,7 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root, unsigned long nr_to_tag, unsigned int fromtag, unsigned int totag); int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag); +unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item); static inline void radix_tree_preload_end(void) { diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 348eaefbed74..a2f9da59c197 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -1203,6 +1203,98 @@ radix_tree_gang_lookup_tag_slot(struct radix_tree_root *root, void ***results, } EXPORT_SYMBOL(radix_tree_gang_lookup_tag_slot); +#if defined(CONFIG_SHMEM) && defined(CONFIG_SWAP) +#include /* for cond_resched() */ + +/* + * This linear search is at present only useful to shmem_unuse_inode(). + */ +static unsigned long __locate(struct radix_tree_node *slot, void *item, + unsigned long index, unsigned long *found_index) +{ + unsigned int shift, height; + unsigned long i; + + height = slot->height; + shift = (height-1) * RADIX_TREE_MAP_SHIFT; + + for ( ; height > 1; height--) { + i = (index >> shift) & RADIX_TREE_MAP_MASK; + for (;;) { + if (slot->slots[i] != NULL) + break; + index &= ~((1UL << shift) - 1); + index += 1UL << shift; + if (index == 0) + goto out; /* 32-bit wraparound */ + i++; + if (i == RADIX_TREE_MAP_SIZE) + goto out; + } + + shift -= RADIX_TREE_MAP_SHIFT; + slot = rcu_dereference_raw(slot->slots[i]); + if (slot == NULL) + goto out; + } + + /* Bottom level: check items */ + for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) { + if (slot->slots[i] == item) { + *found_index = index + i; + index = 0; + goto out; + } + } + index += RADIX_TREE_MAP_SIZE; +out: + return index; +} + +/** + * radix_tree_locate_item - search through radix tree for item + * @root: radix tree root + * @item: item to be found + * + * Returns index where item was found, or -1 if not found. + * Caller must hold no lock (since this time-consuming function needs + * to be preemptible), and must check afterwards if item is still there. + */ +unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item) +{ + struct radix_tree_node *node; + unsigned long max_index; + unsigned long cur_index = 0; + unsigned long found_index = -1; + + do { + rcu_read_lock(); + node = rcu_dereference_raw(root->rnode); + if (!radix_tree_is_indirect_ptr(node)) { + rcu_read_unlock(); + if (node == item) + found_index = 0; + break; + } + + node = indirect_to_ptr(node); + max_index = radix_tree_maxindex(node->height); + if (cur_index > max_index) + break; + + cur_index = __locate(node, item, cur_index, &found_index); + rcu_read_unlock(); + cond_resched(); + } while (cur_index != 0 && cur_index <= max_index); + + return found_index; +} +#else +unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item) +{ + return -1; +} +#endif /* CONFIG_SHMEM && CONFIG_SWAP */ /** * radix_tree_shrink - shrink height of a radix tree to minimal diff --git a/mm/shmem.c b/mm/shmem.c index 3a5be0feb6af..1c702f6f1241 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -356,42 +356,6 @@ export: return ret; } -/* - * Lockless lookup of swap entry in radix tree, avoiding refcount on pages. - */ -static pgoff_t shmem_find_swap(struct address_space *mapping, void *radswap) -{ - void **slots[PAGEVEC_SIZE]; - pgoff_t indices[PAGEVEC_SIZE]; - unsigned int nr_found; - -restart: - nr_found = 1; - indices[0] = -1; - while (nr_found) { - pgoff_t index = indices[nr_found - 1] + 1; - unsigned int i; - - rcu_read_lock(); - nr_found = radix_tree_gang_lookup_slot(&mapping->page_tree, - slots, indices, index, PAGEVEC_SIZE); - for (i = 0; i < nr_found; i++) { - void *item = radix_tree_deref_slot(slots[i]); - if (radix_tree_deref_retry(item)) { - rcu_read_unlock(); - goto restart; - } - if (item == radswap) { - rcu_read_unlock(); - return indices[i]; - } - } - rcu_read_unlock(); - cond_resched(); - } - return -1; -} - /* * Remove swap entry from radix tree, free the swap and its page cache. */ @@ -612,7 +576,7 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, int error; radswap = swp_to_radix_entry(swap); - index = shmem_find_swap(mapping, radswap); + index = radix_tree_locate_item(&mapping->page_tree, radswap); if (index == -1) return 0; -- cgit v1.2.3 From ebec9a7bf11f843b0602b06c402f04bf4213b35a Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 3 Aug 2011 09:22:54 -0700 Subject: drm: track CEA version number if present Drivers need to know the CEA version number in addition to other display info (like whether the display is an HDMI sink) before enabling certain features. So track the CEA version number in the display info structure. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/drm_edid.c | 9 +++++++++ include/drm/drm_crtc.h | 2 ++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 09292193dafe..e12f8b0cec94 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1439,6 +1439,8 @@ EXPORT_SYMBOL(drm_detect_monitor_audio); static void drm_add_display_info(struct edid *edid, struct drm_display_info *info) { + u8 *edid_ext; + info->width_mm = edid->width_cm * 10; info->height_mm = edid->height_cm * 10; @@ -1483,6 +1485,13 @@ static void drm_add_display_info(struct edid *edid, info->color_formats = DRM_COLOR_FORMAT_YCRCB444; if (info->color_formats & DRM_EDID_FEATURE_RGB_YCRCB422) info->color_formats = DRM_COLOR_FORMAT_YCRCB422; + + /* Get data from CEA blocks if present */ + edid_ext = drm_find_cea_extension(edid); + if (!edid_ext) + return; + + info->cea_rev = edid_ext[1]; } /** diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 33d12f87f0e0..d515bc8f4ba7 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -205,6 +205,8 @@ struct drm_display_info { enum subpixel_order subpixel_order; u32 color_formats; + u8 cea_rev; + char *raw_edid; /* if any */ }; -- cgit v1.2.3 From fe55c1844a1c106e9d9d3dd27cbfcf8caeb9e77e Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 4 Aug 2011 10:27:32 +0100 Subject: Revert "dt: add of_alias_scan and of_alias_get_id" This reverts commit 750f463a749e28464151ad26938d11b07b1c43cb. of_alias_* still needs work to be generalized for 'promtree' dt platforms, and to no implicitly create entries for available ids. Signed-off-by: Grant Likely --- drivers/of/base.c | 130 ------------------------------------------------- drivers/of/fdt.c | 4 +- include/linux/of.h | 8 --- include/linux/of_fdt.h | 1 + 4 files changed, 2 insertions(+), 141 deletions(-) (limited to 'include') diff --git a/drivers/of/base.c b/drivers/of/base.c index fb28b5af733b..3ff22e32b602 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -17,39 +17,14 @@ * 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 -/** - * struct alias_prop - Alias property in 'aliases' node - * @link: List node to link the structure in aliases_lookup list - * @alias: Alias property name - * @np: Pointer to device_node that the alias stands for - * @id: Index value from end of alias name - * @stem: Alias string without the index - * - * The structure represents one alias property of 'aliases' node as - * an entry in aliases_lookup list. - */ -struct alias_prop { - struct list_head link; - const char *alias; - struct device_node *np; - int id; - char stem[0]; -}; - -static LIST_HEAD(aliases_lookup); - struct device_node *allnodes; struct device_node *of_chosen; -struct device_node *of_aliases; - -static DEFINE_MUTEX(of_aliases_mutex); /* use when traversing tree through the allnext, child, sibling, * or parent members of struct device_node. @@ -1013,108 +988,3 @@ out_unlock: } #endif /* defined(CONFIG_OF_DYNAMIC) */ -static void of_alias_add(struct alias_prop *ap, struct device_node *np, - int id, const char *stem, int stem_len) -{ - ap->id = id; - ap->np = np; - strncpy(ap->stem, stem, stem_len); - ap->stem[stem_len] = 0; - list_add_tail(&ap->link, &aliases_lookup); - pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n", - ap->alias, ap->stem, ap->id, np ? np->full_name : NULL); -} - -/** - * of_alias_scan() - Scan all properties of 'aliases' node - * - * The function scans all the properties of 'aliases' node and populate - * the global lookup table with the properties. It returns the - * number of alias_prop found, or error code in error case. - */ -__init void of_alias_scan(void) -{ - struct property *pp; - - if (!of_aliases) - return; - - for_each_property(pp, of_aliases->properties) { - const char *start = pp->name; - const char *end = start + strlen(start); - struct device_node *np; - struct alias_prop *ap; - int id, len; - - /* Skip those we do not want to proceed */ - if (!strcmp(pp->name, "name") || - !strcmp(pp->name, "phandle") || - !strcmp(pp->name, "linux,phandle")) - continue; - - np = of_find_node_by_path(pp->value); - if (!np) - continue; - - /* walk alias backwards to extract the id and 'stem' string */ - while (isdigit(*(end-1)) && end > start) - end--; - len = end - start; - id = strlen(end) ? simple_strtoul(end, NULL, 10) : -1; - - /* Allocate an alias_prop with enough space for the stem */ - ap = early_init_dt_alloc_memory_arch(sizeof(*ap) + len + 1, 4); - if (!ap) - continue; - ap->alias = start; - of_alias_add(ap, np, id, start, len); - } -} - -/** - * of_alias_get_id() - Get alias id for the given device_node - * @np: Pointer to the given device_node - * @stem: Alias stem of the given device_node - * - * The function travels the lookup table to get alias id for the given - * device_node and alias stem. It returns the alias id if find it. - * If not, dynamically creates one in the lookup table and returns it, - * or returns error code if fail to create. - */ -int of_alias_get_id(struct device_node *np, const char *stem) -{ - struct alias_prop *app; - int id = 0; - bool found = false; - - mutex_lock(&of_aliases_mutex); - list_for_each_entry(app, &aliases_lookup, link) { - if (strcmp(app->stem, stem) != 0) - continue; - - if (np == app->np) { - found = true; - id = app->id; - break; - } - - if (id <= app->id) - id = app->id + 1; - } - - /* If an id is not found, then allocate a new one */ - if (!found) { - app = kzalloc(sizeof(*app) + strlen(stem) + 1, 4); - if (!app) { - id = -ENODEV; - goto out; - } - of_alias_add(app, np, id, stem, strlen(stem)); - } - - out: - mutex_unlock(&of_aliases_mutex); - - return id; -} -EXPORT_SYMBOL_GPL(of_alias_get_id); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 13d6d3a96b31..65200af29c52 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -707,12 +707,10 @@ void __init unflatten_device_tree(void) __unflatten_device_tree(initial_boot_params, &allnodes, early_init_dt_alloc_memory_arch); - /* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */ + /* Get pointer to OF "/chosen" node for use everywhere */ of_chosen = of_find_node_by_path("/chosen"); if (of_chosen == NULL) of_chosen = of_find_node_by_path("/chosen@0"); - of_aliases = of_find_node_by_path("/aliases"); - of_alias_scan(); } #endif /* CONFIG_OF_EARLY_FLATTREE */ diff --git a/include/linux/of.h b/include/linux/of.h index bc3dc6399547..0085bb01c041 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -68,7 +68,6 @@ struct device_node { /* Pointer for first entry in chain of all nodes. */ extern struct device_node *allnodes; extern struct device_node *of_chosen; -extern struct device_node *of_aliases; extern rwlock_t devtree_lock; static inline bool of_have_populated_dt(void) @@ -210,9 +209,6 @@ extern int of_device_is_available(const struct device_node *device); extern const void *of_get_property(const struct device_node *node, const char *name, int *lenp); -#define for_each_property(pp, properties) \ - for (pp = properties; pp != NULL; pp = pp->next) - extern int of_n_addr_cells(struct device_node *np); extern int of_n_size_cells(struct device_node *np); extern const struct of_device_id *of_match_node( @@ -225,10 +221,6 @@ extern int of_parse_phandles_with_args(struct device_node *np, const char *list_name, const char *cells_name, int index, struct device_node **out_node, const void **out_args); -extern void *early_init_dt_alloc_memory_arch(u64 size, u64 align); -extern void of_alias_scan(void); -extern int of_alias_get_id(struct device_node *np, const char *stem); - extern int of_machine_is_compatible(const char *compat); extern int prom_add_property(struct device_node* np, struct property* prop); diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index b74b74ffe0e7..c84d900fbbb3 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -97,6 +97,7 @@ extern void early_init_dt_check_for_initrd(unsigned long node); extern int early_init_dt_scan_memory(unsigned long node, const char *uname, int depth, void *data); extern void early_init_dt_add_memory_arch(u64 base, u64 size); +extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align); extern u64 dt_mem_next_cell(int s, __be32 **cellp); /* -- cgit v1.2.3 From 051963d4832ed61e5ae74f5330b0a94489e101b9 Mon Sep 17 00:00:00 2001 From: Thomas Reim Date: Fri, 29 Jul 2011 14:28:57 +0000 Subject: drm: Separate EDID Header Check from EDID Block Check Provides function drm_edid_header_is_valid() for EDID header check and replaces EDID header check part of function drm_edid_block_valid() by a call of drm_edid_header_is_valid(). This is a prerequisite to extend DDC probing, e. g. in function radeon_ddc_probe() for Radeon devices, by a central EDID header check. Tested for kernel 2.6.35, 2.6.38 and 3.0 Cc: Signed-off-by: Thomas Reim Reviewed-by: Alex Deucher Acked-by: Stephen Michaels Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 24 ++++++++++++++++++------ include/drm/drm_crtc.h | 1 + 2 files changed, 19 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 7496d245f28c..7425e5c9bd75 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -127,6 +127,23 @@ static const u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + /* + * Sanity check the header of the base EDID block. Return 8 if the header + * is perfect, down to 0 if it's totally wrong. + */ +int drm_edid_header_is_valid(const u8 *raw_edid) +{ + int i, score = 0; + + for (i = 0; i < sizeof(edid_header); i++) + if (raw_edid[i] == edid_header[i]) + score++; + + return score; +} +EXPORT_SYMBOL(drm_edid_header_is_valid); + + /* * Sanity check the EDID block (base or extension). Return 0 if the block * doesn't check out, or 1 if it's valid. @@ -139,12 +156,7 @@ drm_edid_block_valid(u8 *raw_edid) struct edid *edid = (struct edid *)raw_edid; if (raw_edid[0] == 0x00) { - int score = 0; - - for (i = 0; i < sizeof(edid_header); i++) - if (raw_edid[i] == edid_header[i]) - score++; - + int score = drm_edid_header_is_valid(raw_edid); if (score == 8) ; else if (score >= 6) { DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index d515bc8f4ba7..44335e57eaaa 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -804,6 +804,7 @@ extern struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev, extern int drm_add_modes_noedid(struct drm_connector *connector, int hdisplay, int vdisplay); +extern int drm_edid_header_is_valid(const u8 *raw_edid); extern bool drm_edid_is_valid(struct edid *edid); struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh); -- cgit v1.2.3 From 655b16128482fd12808f77a6799eea5419c93709 Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Sun, 29 May 2011 10:57:47 +0300 Subject: nfs_xdr: Move nfs4_string definition out of #ifdef CONFIG_NFS_V4 exofs file system wants to use pnfs_osd_xdr.h file instead of redefining pnfs-objects types in it's private "pnfs.h" headr. Before we do the switch we must make sure pnfs_osd_xdr.h is compilable also under NFS versions smaller than 4.1. Since now it is needed regardless of version, by the exofs code. nfs4_string is not the only nfs4 type out in the global scope. Ack-by: Trond Myklebust Signed-off-by: Boaz Harrosh --- include/linux/nfs_xdr.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 00848d86ffb2..d9f5e8d6ca84 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -759,6 +759,11 @@ struct nfs3_getaclres { struct posix_acl * acl_default; }; +struct nfs4_string { + unsigned int len; + char *data; +}; + #ifdef CONFIG_NFS_V4 typedef u64 clientid4; @@ -949,11 +954,6 @@ struct nfs4_server_caps_res { struct nfs4_sequence_res seq_res; }; -struct nfs4_string { - unsigned int len; - char *data; -}; - #define NFS4_PATHNAME_MAXCOMPONENTS 512 struct nfs4_pathname { unsigned int ncomponents; -- cgit v1.2.3 From 33009557bd9397c446a59e4cc91059a8e84c046b Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Tue, 24 May 2011 15:16:43 -0400 Subject: Add KEY_MICMUTE and enable it on Lenovo X220 I suspect that this works on T410. Signed-off-by: Andy Lutomirski Signed-off-by: Matthew Garrett --- drivers/platform/x86/thinkpad_acpi.c | 11 ++++++++++- include/linux/input.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 26c5b117df22..7bd829f247eb 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3185,9 +3185,18 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */ + /* (assignments unknown, please report if found) */ + KEY_UNKNOWN, KEY_UNKNOWN, + + /* + * The mic mute button only sends 0x1a. It does not + * automatically mute the mic or change the mute light. + */ + KEY_MICMUTE, /* 0x1a: Mic mute (since ?400 or so) */ + /* (assignments unknown, please report if found) */ KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + KEY_UNKNOWN, }, }; diff --git a/include/linux/input.h b/include/linux/input.h index 068784e17972..a637e7814334 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -438,6 +438,8 @@ struct input_keymap_entry { #define KEY_WIMAX 246 #define KEY_RFKILL 247 /* Key that controls all radios */ +#define KEY_MICMUTE 248 /* Mute / unmute the microphone */ + /* Code 255 is reserved for special needs of AT keyboard driver */ #define BTN_MISC 0x100 -- cgit v1.2.3 From 1eb19a12bd2214cdcad5273d472b062a4ba97fa1 Mon Sep 17 00:00:00 2001 From: Mandeep Singh Baines Date: Fri, 5 Aug 2011 18:46:27 -0700 Subject: lib/sha1: use the git implementation of SHA-1 For ChromiumOS, we use SHA-1 to verify the integrity of the root filesystem. The speed of the kernel sha-1 implementation has a major impact on our boot performance. To improve boot performance, we investigated using the heavily optimized sha-1 implementation used in git. With the git sha-1 implementation, we see a 11.7% improvement in boot time. 10 reboots, remove slowest/fastest. Before: Mean: 6.58 seconds Stdev: 0.14 After (with git sha-1, this patch): Mean: 5.89 seconds Stdev: 0.07 The other cool thing about the git SHA-1 implementation is that it only needs 64 bytes of stack for the workspace while the original kernel implementation needed 320 bytes. Signed-off-by: Mandeep Singh Baines Cc: Ramsay Jones Cc: Nicolas Pitre Cc: Herbert Xu Cc: David S. Miller Cc: linux-crypto@vger.kernel.org Signed-off-by: Linus Torvalds --- include/linux/cryptohash.h | 2 +- lib/sha1.c | 212 +++++++++++++++++++++++++++++++++------------ 2 files changed, 159 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/include/linux/cryptohash.h b/include/linux/cryptohash.h index ec78a4bbe1d5..f9452185b019 100644 --- a/include/linux/cryptohash.h +++ b/include/linux/cryptohash.h @@ -3,7 +3,7 @@ #define SHA_DIGEST_WORDS 5 #define SHA_MESSAGE_BYTES (512 /*bits*/ / 8) -#define SHA_WORKSPACE_WORDS 80 +#define SHA_WORKSPACE_WORDS 16 void sha_init(__u32 *buf); void sha_transform(__u32 *digest, const char *data, __u32 *W); diff --git a/lib/sha1.c b/lib/sha1.c index 4c45fd50e913..f33271dd00cb 100644 --- a/lib/sha1.c +++ b/lib/sha1.c @@ -1,31 +1,72 @@ /* - * SHA transform algorithm, originally taken from code written by - * Peter Gutmann, and placed in the public domain. + * SHA1 routine optimized to do word accesses rather than byte accesses, + * and to avoid unnecessary copies into the context array. + * + * This was based on the git SHA1 implementation. */ #include #include -#include +#include +#include -/* The SHA f()-functions. */ +/* + * If you have 32 registers or more, the compiler can (and should) + * try to change the array[] accesses into registers. However, on + * machines with less than ~25 registers, that won't really work, + * and at least gcc will make an unholy mess of it. + * + * So to avoid that mess which just slows things down, we force + * the stores to memory to actually happen (we might be better off + * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as + * suggested by Artur Skawina - that will also make gcc unable to + * try to do the silly "optimize away loads" part because it won't + * see what the value will be). + * + * Ben Herrenschmidt reports that on PPC, the C version comes close + * to the optimized asm with this (ie on PPC you don't want that + * 'volatile', since there are lots of registers). + * + * On ARM we get the best code generation by forcing a full memory barrier + * between each SHA_ROUND, otherwise gcc happily get wild with spilling and + * the stack frame size simply explode and performance goes down the drain. + */ -#define f1(x,y,z) (z ^ (x & (y ^ z))) /* x ? y : z */ -#define f2(x,y,z) (x ^ y ^ z) /* XOR */ -#define f3(x,y,z) ((x & y) + (z & (x ^ y))) /* majority */ +#ifdef CONFIG_X86 + #define setW(x, val) (*(volatile __u32 *)&W(x) = (val)) +#elif defined(CONFIG_ARM) + #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0) +#else + #define setW(x, val) (W(x) = (val)) +#endif -/* The SHA Mysterious Constants */ +/* This "rolls" over the 512-bit array */ +#define W(x) (array[(x)&15]) -#define K1 0x5A827999L /* Rounds 0-19: sqrt(2) * 2^30 */ -#define K2 0x6ED9EBA1L /* Rounds 20-39: sqrt(3) * 2^30 */ -#define K3 0x8F1BBCDCL /* Rounds 40-59: sqrt(5) * 2^30 */ -#define K4 0xCA62C1D6L /* Rounds 60-79: sqrt(10) * 2^30 */ +/* + * Where do we get the source from? The first 16 iterations get it from + * the input data, the next mix it from the 512-bit array. + */ +#define SHA_SRC(t) get_unaligned_be32((__u32 *)data + t) +#define SHA_MIX(t) rol32(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1) + +#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ + __u32 TEMP = input(t); setW(t, TEMP); \ + E += TEMP + rol32(A,5) + (fn) + (constant); \ + B = ror32(B, 2); } while (0) + +#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E ) +#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E ) +#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E ) /** * sha_transform - single block SHA1 transform * * @digest: 160 bit digest to update * @data: 512 bits of data to hash - * @W: 80 words of workspace (see note) + * @array: 16 words of workspace (see note) * * This function generates a SHA1 digest for a single 512-bit block. * Be warned, it does not handle padding and message digest, do not @@ -36,47 +77,111 @@ * to clear the workspace. This is left to the caller to avoid * unnecessary clears between chained hashing operations. */ -void sha_transform(__u32 *digest, const char *in, __u32 *W) +void sha_transform(__u32 *digest, const char *data, __u32 *array) { - __u32 a, b, c, d, e, t, i; - - for (i = 0; i < 16; i++) - W[i] = be32_to_cpu(((const __be32 *)in)[i]); - - for (i = 0; i < 64; i++) - W[i+16] = rol32(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 1); - - a = digest[0]; - b = digest[1]; - c = digest[2]; - d = digest[3]; - e = digest[4]; - - for (i = 0; i < 20; i++) { - t = f1(b, c, d) + K1 + rol32(a, 5) + e + W[i]; - e = d; d = c; c = rol32(b, 30); b = a; a = t; - } - - for (; i < 40; i ++) { - t = f2(b, c, d) + K2 + rol32(a, 5) + e + W[i]; - e = d; d = c; c = rol32(b, 30); b = a; a = t; - } - - for (; i < 60; i ++) { - t = f3(b, c, d) + K3 + rol32(a, 5) + e + W[i]; - e = d; d = c; c = rol32(b, 30); b = a; a = t; - } - - for (; i < 80; i ++) { - t = f2(b, c, d) + K4 + rol32(a, 5) + e + W[i]; - e = d; d = c; c = rol32(b, 30); b = a; a = t; - } - - digest[0] += a; - digest[1] += b; - digest[2] += c; - digest[3] += d; - digest[4] += e; + __u32 A, B, C, D, E; + + A = digest[0]; + B = digest[1]; + C = digest[2]; + D = digest[3]; + E = digest[4]; + + /* Round 1 - iterations 0-16 take their input from 'data' */ + T_0_15( 0, A, B, C, D, E); + T_0_15( 1, E, A, B, C, D); + T_0_15( 2, D, E, A, B, C); + T_0_15( 3, C, D, E, A, B); + T_0_15( 4, B, C, D, E, A); + T_0_15( 5, A, B, C, D, E); + T_0_15( 6, E, A, B, C, D); + T_0_15( 7, D, E, A, B, C); + T_0_15( 8, C, D, E, A, B); + T_0_15( 9, B, C, D, E, A); + T_0_15(10, A, B, C, D, E); + T_0_15(11, E, A, B, C, D); + T_0_15(12, D, E, A, B, C); + T_0_15(13, C, D, E, A, B); + T_0_15(14, B, C, D, E, A); + T_0_15(15, A, B, C, D, E); + + /* Round 1 - tail. Input from 512-bit mixing array */ + T_16_19(16, E, A, B, C, D); + T_16_19(17, D, E, A, B, C); + T_16_19(18, C, D, E, A, B); + T_16_19(19, B, C, D, E, A); + + /* Round 2 */ + T_20_39(20, A, B, C, D, E); + T_20_39(21, E, A, B, C, D); + T_20_39(22, D, E, A, B, C); + T_20_39(23, C, D, E, A, B); + T_20_39(24, B, C, D, E, A); + T_20_39(25, A, B, C, D, E); + T_20_39(26, E, A, B, C, D); + T_20_39(27, D, E, A, B, C); + T_20_39(28, C, D, E, A, B); + T_20_39(29, B, C, D, E, A); + T_20_39(30, A, B, C, D, E); + T_20_39(31, E, A, B, C, D); + T_20_39(32, D, E, A, B, C); + T_20_39(33, C, D, E, A, B); + T_20_39(34, B, C, D, E, A); + T_20_39(35, A, B, C, D, E); + T_20_39(36, E, A, B, C, D); + T_20_39(37, D, E, A, B, C); + T_20_39(38, C, D, E, A, B); + T_20_39(39, B, C, D, E, A); + + /* Round 3 */ + T_40_59(40, A, B, C, D, E); + T_40_59(41, E, A, B, C, D); + T_40_59(42, D, E, A, B, C); + T_40_59(43, C, D, E, A, B); + T_40_59(44, B, C, D, E, A); + T_40_59(45, A, B, C, D, E); + T_40_59(46, E, A, B, C, D); + T_40_59(47, D, E, A, B, C); + T_40_59(48, C, D, E, A, B); + T_40_59(49, B, C, D, E, A); + T_40_59(50, A, B, C, D, E); + T_40_59(51, E, A, B, C, D); + T_40_59(52, D, E, A, B, C); + T_40_59(53, C, D, E, A, B); + T_40_59(54, B, C, D, E, A); + T_40_59(55, A, B, C, D, E); + T_40_59(56, E, A, B, C, D); + T_40_59(57, D, E, A, B, C); + T_40_59(58, C, D, E, A, B); + T_40_59(59, B, C, D, E, A); + + /* Round 4 */ + T_60_79(60, A, B, C, D, E); + T_60_79(61, E, A, B, C, D); + T_60_79(62, D, E, A, B, C); + T_60_79(63, C, D, E, A, B); + T_60_79(64, B, C, D, E, A); + T_60_79(65, A, B, C, D, E); + T_60_79(66, E, A, B, C, D); + T_60_79(67, D, E, A, B, C); + T_60_79(68, C, D, E, A, B); + T_60_79(69, B, C, D, E, A); + T_60_79(70, A, B, C, D, E); + T_60_79(71, E, A, B, C, D); + T_60_79(72, D, E, A, B, C); + T_60_79(73, C, D, E, A, B); + T_60_79(74, B, C, D, E, A); + T_60_79(75, A, B, C, D, E); + T_60_79(76, E, A, B, C, D); + T_60_79(77, D, E, A, B, C); + T_60_79(78, C, D, E, A, B); + T_60_79(79, B, C, D, E, A); + + digest[0] += A; + digest[1] += B; + digest[2] += C; + digest[3] += D; + digest[4] += E; } EXPORT_SYMBOL(sha_transform); @@ -92,4 +197,3 @@ void sha_init(__u32 *buf) buf[3] = 0x10325476; buf[4] = 0xc3d2e1f0; } - -- cgit v1.2.3 From bc0b96b54a21246e377122d54569eef71cec535f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 3 Aug 2011 19:45:10 -0700 Subject: crypto: Move md5_transform to lib/md5.c We are going to use this for TCP/IP sequence number and fragment ID generation. Signed-off-by: David S. Miller --- crypto/md5.c | 92 +------------------------------------------- include/linux/cryptohash.h | 5 +++ lib/Makefile | 2 +- lib/md5.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 92 deletions(-) create mode 100644 lib/md5.c (limited to 'include') diff --git a/crypto/md5.c b/crypto/md5.c index 30efc7dad891..7febeaab923b 100644 --- a/crypto/md5.c +++ b/crypto/md5.c @@ -21,99 +21,9 @@ #include #include #include +#include #include -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -#define MD5STEP(f, w, x, y, z, in, s) \ - (w += f(x, y, z) + in, w = (w<>(32-s)) + x) - -static void md5_transform(u32 *hash, u32 const *in) -{ - u32 a, b, c, d; - - a = hash[0]; - b = hash[1]; - c = hash[2]; - d = hash[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - hash[0] += a; - hash[1] += b; - hash[2] += c; - hash[3] += d; -} - /* XXX: this stuff can be optimized */ static inline void le32_to_cpu_array(u32 *buf, unsigned int words) { diff --git a/include/linux/cryptohash.h b/include/linux/cryptohash.h index ec78a4bbe1d5..d2984fbebbc0 100644 --- a/include/linux/cryptohash.h +++ b/include/linux/cryptohash.h @@ -8,6 +8,11 @@ void sha_init(__u32 *buf); void sha_transform(__u32 *digest, const char *data, __u32 *W); +#define MD5_DIGEST_WORDS 4 +#define MD5_MESSAGE_BYTES 64 + +void md5_transform(__u32 *hash, __u32 const *in); + __u32 half_md4_transform(__u32 buf[4], __u32 const in[8]); #endif diff --git a/lib/Makefile b/lib/Makefile index 6457af4a7caf..d5d175c8a6ca 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -10,7 +10,7 @@ endif lib-y := ctype.o string.o vsprintf.o cmdline.o \ rbtree.o radix-tree.o dump_stack.o timerqueue.o\ idr.o int_sqrt.o extable.o prio_tree.o \ - sha1.o irq_regs.o reciprocal_div.o argv_split.o \ + sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \ proportions.o prio_heap.o ratelimit.o show_mem.o \ is_single_threaded.o plist.o decompress.o find_next_bit.o diff --git a/lib/md5.c b/lib/md5.c new file mode 100644 index 000000000000..c777180e1f2f --- /dev/null +++ b/lib/md5.c @@ -0,0 +1,95 @@ +#include +#include +#include + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define MD5STEP(f, w, x, y, z, in, s) \ + (w += f(x, y, z) + in, w = (w<>(32-s)) + x) + +void md5_transform(__u32 *hash, __u32 const *in) +{ + u32 a, b, c, d; + + a = hash[0]; + b = hash[1]; + c = hash[2]; + d = hash[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + hash[0] += a; + hash[1] += b; + hash[2] += c; + hash[3] += d; +} +EXPORT_SYMBOL(md5_transform); -- cgit v1.2.3 From 6e5714eaf77d79ae1c8b47e3e040ff5411b717ec Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 3 Aug 2011 20:50:44 -0700 Subject: net: Compute protocol sequence numbers and fragment IDs using MD5. Computers have become a lot faster since we compromised on the partial MD4 hash which we use currently for performance reasons. MD5 is a much safer choice, and is inline with both RFC1948 and other ISS generators (OpenBSD, Solaris, etc.) Furthermore, only having 24-bits of the sequence number be truly unpredictable is a very serious limitation. So the periodic regeneration and 8-bit counter have been removed. We compute and use a full 32-bit sequence number. For ipv6, DCCP was found to use a 32-bit truncated initial sequence number (it needs 43-bits) and that is fixed here as well. Reported-by: Dan Kaminsky Tested-by: Willy Tarreau Signed-off-by: David S. Miller --- drivers/char/random.c | 349 +------------------------------ include/linux/random.h | 12 -- include/net/secure_seq.h | 20 ++ net/core/Makefile | 2 +- net/core/secure_seq.c | 184 ++++++++++++++++ net/dccp/ipv4.c | 1 + net/dccp/ipv6.c | 9 +- net/ipv4/inet_hashtables.c | 1 + net/ipv4/inetpeer.c | 1 + net/ipv4/netfilter/nf_nat_proto_common.c | 1 + net/ipv4/route.c | 1 + net/ipv4/tcp_ipv4.c | 1 + net/ipv6/inet6_hashtables.c | 1 + net/ipv6/tcp_ipv6.c | 1 + 14 files changed, 223 insertions(+), 361 deletions(-) create mode 100644 include/net/secure_seq.h create mode 100644 net/core/secure_seq.c (limited to 'include') diff --git a/drivers/char/random.c b/drivers/char/random.c index 729281961f22..c35a785005b0 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1300,345 +1300,14 @@ ctl_table random_table[] = { }; #endif /* CONFIG_SYSCTL */ -/******************************************************************** - * - * Random functions for networking - * - ********************************************************************/ - -/* - * TCP initial sequence number picking. This uses the random number - * generator to pick an initial secret value. This value is hashed - * along with the TCP endpoint information to provide a unique - * starting point for each pair of TCP endpoints. This defeats - * attacks which rely on guessing the initial TCP sequence number. - * This algorithm was suggested by Steve Bellovin. - * - * Using a very strong hash was taking an appreciable amount of the total - * TCP connection establishment time, so this is a weaker hash, - * compensated for by changing the secret periodically. - */ - -/* F, G and H are basic MD4 functions: selection, majority, parity */ -#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) - -/* - * The generic round function. The application is so specific that - * we don't bother protecting all the arguments with parens, as is generally - * good macro practice, in favor of extra legibility. - * Rotation is separate from addition to prevent recomputation - */ -#define ROUND(f, a, b, c, d, x, s) \ - (a += f(b, c, d) + x, a = (a << s) | (a >> (32 - s))) -#define K1 0 -#define K2 013240474631UL -#define K3 015666365641UL - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - -static __u32 twothirdsMD4Transform(__u32 const buf[4], __u32 const in[12]) -{ - __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; - - /* Round 1 */ - ROUND(F, a, b, c, d, in[ 0] + K1, 3); - ROUND(F, d, a, b, c, in[ 1] + K1, 7); - ROUND(F, c, d, a, b, in[ 2] + K1, 11); - ROUND(F, b, c, d, a, in[ 3] + K1, 19); - ROUND(F, a, b, c, d, in[ 4] + K1, 3); - ROUND(F, d, a, b, c, in[ 5] + K1, 7); - ROUND(F, c, d, a, b, in[ 6] + K1, 11); - ROUND(F, b, c, d, a, in[ 7] + K1, 19); - ROUND(F, a, b, c, d, in[ 8] + K1, 3); - ROUND(F, d, a, b, c, in[ 9] + K1, 7); - ROUND(F, c, d, a, b, in[10] + K1, 11); - ROUND(F, b, c, d, a, in[11] + K1, 19); - - /* Round 2 */ - ROUND(G, a, b, c, d, in[ 1] + K2, 3); - ROUND(G, d, a, b, c, in[ 3] + K2, 5); - ROUND(G, c, d, a, b, in[ 5] + K2, 9); - ROUND(G, b, c, d, a, in[ 7] + K2, 13); - ROUND(G, a, b, c, d, in[ 9] + K2, 3); - ROUND(G, d, a, b, c, in[11] + K2, 5); - ROUND(G, c, d, a, b, in[ 0] + K2, 9); - ROUND(G, b, c, d, a, in[ 2] + K2, 13); - ROUND(G, a, b, c, d, in[ 4] + K2, 3); - ROUND(G, d, a, b, c, in[ 6] + K2, 5); - ROUND(G, c, d, a, b, in[ 8] + K2, 9); - ROUND(G, b, c, d, a, in[10] + K2, 13); - - /* Round 3 */ - ROUND(H, a, b, c, d, in[ 3] + K3, 3); - ROUND(H, d, a, b, c, in[ 7] + K3, 9); - ROUND(H, c, d, a, b, in[11] + K3, 11); - ROUND(H, b, c, d, a, in[ 2] + K3, 15); - ROUND(H, a, b, c, d, in[ 6] + K3, 3); - ROUND(H, d, a, b, c, in[10] + K3, 9); - ROUND(H, c, d, a, b, in[ 1] + K3, 11); - ROUND(H, b, c, d, a, in[ 5] + K3, 15); - ROUND(H, a, b, c, d, in[ 9] + K3, 3); - ROUND(H, d, a, b, c, in[ 0] + K3, 9); - ROUND(H, c, d, a, b, in[ 4] + K3, 11); - ROUND(H, b, c, d, a, in[ 8] + K3, 15); - - return buf[1] + b; /* "most hashed" word */ - /* Alternative: return sum of all words? */ -} -#endif - -#undef ROUND -#undef F -#undef G -#undef H -#undef K1 -#undef K2 -#undef K3 - -/* This should not be decreased so low that ISNs wrap too fast. */ -#define REKEY_INTERVAL (300 * HZ) -/* - * Bit layout of the tcp sequence numbers (before adding current time): - * bit 24-31: increased after every key exchange - * bit 0-23: hash(source,dest) - * - * The implementation is similar to the algorithm described - * in the Appendix of RFC 1185, except that - * - it uses a 1 MHz clock instead of a 250 kHz clock - * - it performs a rekey every 5 minutes, which is equivalent - * to a (source,dest) tulple dependent forward jump of the - * clock by 0..2^(HASH_BITS+1) - * - * Thus the average ISN wraparound time is 68 minutes instead of - * 4.55 hours. - * - * SMP cleanup and lock avoidance with poor man's RCU. - * Manfred Spraul - * - */ -#define COUNT_BITS 8 -#define COUNT_MASK ((1 << COUNT_BITS) - 1) -#define HASH_BITS 24 -#define HASH_MASK ((1 << HASH_BITS) - 1) +static u32 random_int_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned; -static struct keydata { - __u32 count; /* already shifted to the final position */ - __u32 secret[12]; -} ____cacheline_aligned ip_keydata[2]; - -static unsigned int ip_cnt; - -static void rekey_seq_generator(struct work_struct *work); - -static DECLARE_DELAYED_WORK(rekey_work, rekey_seq_generator); - -/* - * Lock avoidance: - * The ISN generation runs lockless - it's just a hash over random data. - * State changes happen every 5 minutes when the random key is replaced. - * Synchronization is performed by having two copies of the hash function - * state and rekey_seq_generator always updates the inactive copy. - * The copy is then activated by updating ip_cnt. - * The implementation breaks down if someone blocks the thread - * that processes SYN requests for more than 5 minutes. Should never - * happen, and even if that happens only a not perfectly compliant - * ISN is generated, nothing fatal. - */ -static void rekey_seq_generator(struct work_struct *work) +static int __init random_int_secret_init(void) { - struct keydata *keyptr = &ip_keydata[1 ^ (ip_cnt & 1)]; - - get_random_bytes(keyptr->secret, sizeof(keyptr->secret)); - keyptr->count = (ip_cnt & COUNT_MASK) << HASH_BITS; - smp_wmb(); - ip_cnt++; - schedule_delayed_work(&rekey_work, - round_jiffies_relative(REKEY_INTERVAL)); -} - -static inline struct keydata *get_keyptr(void) -{ - struct keydata *keyptr = &ip_keydata[ip_cnt & 1]; - - smp_rmb(); - - return keyptr; -} - -static __init int seqgen_init(void) -{ - rekey_seq_generator(NULL); + get_random_bytes(random_int_secret, sizeof(random_int_secret)); return 0; } -late_initcall(seqgen_init); - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -__u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr, - __be16 sport, __be16 dport) -{ - __u32 seq; - __u32 hash[12]; - struct keydata *keyptr = get_keyptr(); - - /* The procedure is the same as for IPv4, but addresses are longer. - * Thus we must use twothirdsMD4Transform. - */ - - memcpy(hash, saddr, 16); - hash[4] = ((__force u16)sport << 16) + (__force u16)dport; - memcpy(&hash[5], keyptr->secret, sizeof(__u32) * 7); - - seq = twothirdsMD4Transform((const __u32 *)daddr, hash) & HASH_MASK; - seq += keyptr->count; - - seq += ktime_to_ns(ktime_get_real()); - - return seq; -} -EXPORT_SYMBOL(secure_tcpv6_sequence_number); -#endif - -/* The code below is shamelessly stolen from secure_tcp_sequence_number(). - * All blames to Andrey V. Savochkin . - */ -__u32 secure_ip_id(__be32 daddr) -{ - struct keydata *keyptr; - __u32 hash[4]; - - keyptr = get_keyptr(); - - /* - * Pick a unique starting offset for each IP destination. - * The dest ip address is placed in the starting vector, - * which is then hashed with random data. - */ - hash[0] = (__force __u32)daddr; - hash[1] = keyptr->secret[9]; - hash[2] = keyptr->secret[10]; - hash[3] = keyptr->secret[11]; - - return half_md4_transform(hash, keyptr->secret); -} - -__u32 secure_ipv6_id(const __be32 daddr[4]) -{ - const struct keydata *keyptr; - __u32 hash[4]; - - keyptr = get_keyptr(); - - hash[0] = (__force __u32)daddr[0]; - hash[1] = (__force __u32)daddr[1]; - hash[2] = (__force __u32)daddr[2]; - hash[3] = (__force __u32)daddr[3]; - - return half_md4_transform(hash, keyptr->secret); -} - -#ifdef CONFIG_INET - -__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, - __be16 sport, __be16 dport) -{ - __u32 seq; - __u32 hash[4]; - struct keydata *keyptr = get_keyptr(); - - /* - * Pick a unique starting offset for each TCP connection endpoints - * (saddr, daddr, sport, dport). - * Note that the words are placed into the starting vector, which is - * then mixed with a partial MD4 over random data. - */ - hash[0] = (__force u32)saddr; - hash[1] = (__force u32)daddr; - hash[2] = ((__force u16)sport << 16) + (__force u16)dport; - hash[3] = keyptr->secret[11]; - - seq = half_md4_transform(hash, keyptr->secret) & HASH_MASK; - seq += keyptr->count; - /* - * As close as possible to RFC 793, which - * suggests using a 250 kHz clock. - * Further reading shows this assumes 2 Mb/s networks. - * For 10 Mb/s Ethernet, a 1 MHz clock is appropriate. - * For 10 Gb/s Ethernet, a 1 GHz clock should be ok, but - * we also need to limit the resolution so that the u32 seq - * overlaps less than one time per MSL (2 minutes). - * Choosing a clock of 64 ns period is OK. (period of 274 s) - */ - seq += ktime_to_ns(ktime_get_real()) >> 6; - - return seq; -} - -/* Generate secure starting point for ephemeral IPV4 transport port search */ -u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport) -{ - struct keydata *keyptr = get_keyptr(); - u32 hash[4]; - - /* - * Pick a unique starting offset for each ephemeral port search - * (saddr, daddr, dport) and 48bits of random data. - */ - hash[0] = (__force u32)saddr; - hash[1] = (__force u32)daddr; - hash[2] = (__force u32)dport ^ keyptr->secret[10]; - hash[3] = keyptr->secret[11]; - - return half_md4_transform(hash, keyptr->secret); -} -EXPORT_SYMBOL_GPL(secure_ipv4_port_ephemeral); - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, - __be16 dport) -{ - struct keydata *keyptr = get_keyptr(); - u32 hash[12]; - - memcpy(hash, saddr, 16); - hash[4] = (__force u32)dport; - memcpy(&hash[5], keyptr->secret, sizeof(__u32) * 7); - - return twothirdsMD4Transform((const __u32 *)daddr, hash); -} -#endif - -#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE) -/* Similar to secure_tcp_sequence_number but generate a 48 bit value - * bit's 32-47 increase every key exchange - * 0-31 hash(source, dest) - */ -u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, - __be16 sport, __be16 dport) -{ - u64 seq; - __u32 hash[4]; - struct keydata *keyptr = get_keyptr(); - - hash[0] = (__force u32)saddr; - hash[1] = (__force u32)daddr; - hash[2] = ((__force u16)sport << 16) + (__force u16)dport; - hash[3] = keyptr->secret[11]; - - seq = half_md4_transform(hash, keyptr->secret); - seq |= ((u64)keyptr->count) << (32 - HASH_BITS); - - seq += ktime_to_ns(ktime_get_real()); - seq &= (1ull << 48) - 1; - - return seq; -} -EXPORT_SYMBOL(secure_dccp_sequence_number); -#endif - -#endif /* CONFIG_INET */ - +late_initcall(random_int_secret_init); /* * Get a random word for internal kernel use only. Similar to urandom but @@ -1646,17 +1315,15 @@ EXPORT_SYMBOL(secure_dccp_sequence_number); * value is not cryptographically secure but for several uses the cost of * depleting entropy is too high */ -DEFINE_PER_CPU(__u32 [4], get_random_int_hash); +DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash); unsigned int get_random_int(void) { - struct keydata *keyptr; __u32 *hash = get_cpu_var(get_random_int_hash); - int ret; + unsigned int ret; - keyptr = get_keyptr(); hash[0] += current->pid + jiffies + get_cycles(); - - ret = half_md4_transform(hash, keyptr->secret); + md5_transform(hash, random_int_secret); + ret = hash[0]; put_cpu_var(get_random_int_hash); return ret; diff --git a/include/linux/random.h b/include/linux/random.h index ce29a040c8dc..d13059f3ea32 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -57,18 +57,6 @@ extern void add_interrupt_randomness(int irq); extern void get_random_bytes(void *buf, int nbytes); void generate_random_uuid(unsigned char uuid_out[16]); -extern __u32 secure_ip_id(__be32 daddr); -extern __u32 secure_ipv6_id(const __be32 daddr[4]); -extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport); -extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, - __be16 dport); -extern __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, - __be16 sport, __be16 dport); -extern __u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr, - __be16 sport, __be16 dport); -extern u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, - __be16 sport, __be16 dport); - #ifndef MODULE extern const struct file_operations random_fops, urandom_fops; #endif diff --git a/include/net/secure_seq.h b/include/net/secure_seq.h new file mode 100644 index 000000000000..d97f6892c019 --- /dev/null +++ b/include/net/secure_seq.h @@ -0,0 +1,20 @@ +#ifndef _NET_SECURE_SEQ +#define _NET_SECURE_SEQ + +#include + +extern __u32 secure_ip_id(__be32 daddr); +extern __u32 secure_ipv6_id(const __be32 daddr[4]); +extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport); +extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, + __be16 dport); +extern __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, + __be16 sport, __be16 dport); +extern __u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr, + __be16 sport, __be16 dport); +extern u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, + __be16 sport, __be16 dport); +extern u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr, + __be16 sport, __be16 dport); + +#endif /* _NET_SECURE_SEQ */ diff --git a/net/core/Makefile b/net/core/Makefile index 8a04dd22cf77..0d357b1c4e57 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -3,7 +3,7 @@ # obj-y := sock.o request_sock.o skbuff.o iovec.o datagram.o stream.o scm.o \ - gen_stats.o gen_estimator.o net_namespace.o + gen_stats.o gen_estimator.o net_namespace.o secure_seq.o obj-$(CONFIG_SYSCTL) += sysctl_net_core.o diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c new file mode 100644 index 000000000000..45329d7c9dd9 --- /dev/null +++ b/net/core/secure_seq.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static u32 net_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned; + +static int __init net_secret_init(void) +{ + get_random_bytes(net_secret, sizeof(net_secret)); + return 0; +} +late_initcall(net_secret_init); + +static u32 seq_scale(u32 seq) +{ + /* + * As close as possible to RFC 793, which + * suggests using a 250 kHz clock. + * Further reading shows this assumes 2 Mb/s networks. + * For 10 Mb/s Ethernet, a 1 MHz clock is appropriate. + * For 10 Gb/s Ethernet, a 1 GHz clock should be ok, but + * we also need to limit the resolution so that the u32 seq + * overlaps less than one time per MSL (2 minutes). + * Choosing a clock of 64 ns period is OK. (period of 274 s) + */ + return seq + (ktime_to_ns(ktime_get_real()) >> 6); +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +__u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr, + __be16 sport, __be16 dport) +{ + u32 secret[MD5_MESSAGE_BYTES / 4]; + u32 hash[MD5_DIGEST_WORDS]; + u32 i; + + memcpy(hash, saddr, 16); + for (i = 0; i < 4; i++) + secret[i] = net_secret[i] + daddr[i]; + secret[4] = net_secret[4] + + (((__force u16)sport << 16) + (__force u16)dport); + for (i = 5; i < MD5_MESSAGE_BYTES / 4; i++) + secret[i] = net_secret[i]; + + md5_transform(hash, secret); + + return seq_scale(hash[0]); +} +EXPORT_SYMBOL(secure_tcpv6_sequence_number); + +u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, + __be16 dport) +{ + u32 secret[MD5_MESSAGE_BYTES / 4]; + u32 hash[MD5_DIGEST_WORDS]; + u32 i; + + memcpy(hash, saddr, 16); + for (i = 0; i < 4; i++) + secret[i] = net_secret[i] + (__force u32) daddr[i]; + secret[4] = net_secret[4] + (__force u32)dport; + for (i = 5; i < MD5_MESSAGE_BYTES / 4; i++) + secret[i] = net_secret[i]; + + md5_transform(hash, secret); + + return hash[0]; +} +#endif + +#ifdef CONFIG_INET +__u32 secure_ip_id(__be32 daddr) +{ + u32 hash[MD5_DIGEST_WORDS]; + + hash[0] = (__force __u32) daddr; + hash[1] = net_secret[13]; + hash[2] = net_secret[14]; + hash[3] = net_secret[15]; + + md5_transform(hash, net_secret); + + return hash[0]; +} + +__u32 secure_ipv6_id(const __be32 daddr[4]) +{ + __u32 hash[4]; + + memcpy(hash, daddr, 16); + md5_transform(hash, net_secret); + + return hash[0]; +} + +__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, + __be16 sport, __be16 dport) +{ + u32 hash[MD5_DIGEST_WORDS]; + + hash[0] = (__force u32)saddr; + hash[1] = (__force u32)daddr; + hash[2] = ((__force u16)sport << 16) + (__force u16)dport; + hash[3] = net_secret[15]; + + md5_transform(hash, net_secret); + + return seq_scale(hash[0]); +} + +u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport) +{ + u32 hash[MD5_DIGEST_WORDS]; + + hash[0] = (__force u32)saddr; + hash[1] = (__force u32)daddr; + hash[2] = (__force u32)dport ^ net_secret[14]; + hash[3] = net_secret[15]; + + md5_transform(hash, net_secret); + + return hash[0]; +} +EXPORT_SYMBOL_GPL(secure_ipv4_port_ephemeral); +#endif + +#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE) +u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, + __be16 sport, __be16 dport) +{ + u32 hash[MD5_DIGEST_WORDS]; + u64 seq; + + hash[0] = (__force u32)saddr; + hash[1] = (__force u32)daddr; + hash[2] = ((__force u16)sport << 16) + (__force u16)dport; + hash[3] = net_secret[15]; + + md5_transform(hash, net_secret); + + seq = hash[0] | (((u64)hash[1]) << 32); + seq += ktime_to_ns(ktime_get_real()); + seq &= (1ull << 48) - 1; + + return seq; +} +EXPORT_SYMBOL(secure_dccp_sequence_number); + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr, + __be16 sport, __be16 dport) +{ + u32 secret[MD5_MESSAGE_BYTES / 4]; + u32 hash[MD5_DIGEST_WORDS]; + u64 seq; + u32 i; + + memcpy(hash, saddr, 16); + for (i = 0; i < 4; i++) + secret[i] = net_secret[i] + daddr[i]; + secret[4] = net_secret[4] + + (((__force u16)sport << 16) + (__force u16)dport); + for (i = 5; i < MD5_MESSAGE_BYTES / 4; i++) + secret[i] = net_secret[i]; + + md5_transform(hash, secret); + + seq = hash[0] | (((u64)hash[1]) << 32); + seq += ktime_to_ns(ktime_get_real()); + seq &= (1ull << 48) - 1; + + return seq; +} +EXPORT_SYMBOL(secure_dccpv6_sequence_number); +#endif +#endif diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 8c36adfd1919..332639b56f4d 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "ackvec.h" #include "ccid.h" diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 8dc4348774a5..b74f76117dcf 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "dccp.h" #include "ipv6.h" @@ -69,13 +70,7 @@ static inline void dccp_v6_send_check(struct sock *sk, struct sk_buff *skb) dh->dccph_checksum = dccp_v6_csum_finish(skb, &np->saddr, &np->daddr); } -static inline __u32 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr, - __be16 sport, __be16 dport ) -{ - return secure_tcpv6_sequence_number(saddr, daddr, sport, dport); -} - -static inline __u32 dccp_v6_init_sequence(struct sk_buff *skb) +static inline __u64 dccp_v6_init_sequence(struct sk_buff *skb) { return secure_dccpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32, ipv6_hdr(skb)->saddr.s6_addr32, diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 3c0369a3a663..984ec656b03b 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -21,6 +21,7 @@ #include #include +#include #include /* diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index e38213817d0a..86f13c67ea85 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -19,6 +19,7 @@ #include #include #include +#include /* * Theory of operations. diff --git a/net/ipv4/netfilter/nf_nat_proto_common.c b/net/ipv4/netfilter/nf_nat_proto_common.c index 3e61faf23a9a..f52d41ea0690 100644 --- a/net/ipv4/netfilter/nf_nat_proto_common.c +++ b/net/ipv4/netfilter/nf_nat_proto_common.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6afc4eb50591..e3dec1c9f09d 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -109,6 +109,7 @@ #include #endif #include +#include #define RT_FL_TOS(oldflp4) \ ((u32)(oldflp4->flowi4_tos & (IPTOS_RT_MASK | RTO_ONLINK))) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 955b8e65b69e..1c12b8ec849d 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -72,6 +72,7 @@ #include #include #include +#include #include #include diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index b53197233709..73f1a00a96af 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -20,6 +20,7 @@ #include #include #include +#include #include int __inet6_hash(struct sock *sk, struct inet_timewait_sock *tw) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 78aa53492b3e..d1fb63f4aeb7 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -61,6 +61,7 @@ #include #include #include +#include #include -- cgit v1.2.3 From 8ff660ab85f524bdc7652eb5d38aaef1d66aa9c7 Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Sat, 6 Aug 2011 19:26:31 -0700 Subject: exofs: Rename raid engine from exofs/ios.c => ore ORE stands for "Objects Raid Engine" This patch is a mechanical rename of everything that was in ios.c and its API declaration to an ore.c and an osd_ore.h header. The ore engine will later be used by the pnfs objects layout driver. * File ios.c => ore.c * Declaration of types and API are moved from exofs.h to a new osd_ore.h * All used types are prefixed by ore_ from their exofs_ name. * Shift includes from exofs.h to osd_ore.h so osd_ore.h is independent, include it from exofs.h. Other than a pure rename there are no other changes. Next patch will move the ore into it's own module and will export the API to be used by exofs and later the layout driver Signed-off-by: Boaz Harrosh --- fs/exofs/Kbuild | 2 +- fs/exofs/exofs.h | 119 +------ fs/exofs/inode.c | 88 +++--- fs/exofs/ios.c | 810 ------------------------------------------------ fs/exofs/ore.c | 820 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/exofs/super.c | 56 ++-- include/scsi/osd_ore.h | 125 ++++++++ 7 files changed, 1030 insertions(+), 990 deletions(-) delete mode 100644 fs/exofs/ios.c create mode 100644 fs/exofs/ore.c create mode 100644 include/scsi/osd_ore.h (limited to 'include') diff --git a/fs/exofs/Kbuild b/fs/exofs/Kbuild index 2d0f757fda3e..c1af2217b087 100644 --- a/fs/exofs/Kbuild +++ b/fs/exofs/Kbuild @@ -12,5 +12,5 @@ # Kbuild - Gets included from the Kernels Makefile and build system # -exofs-y := ios.o inode.o file.o symlink.o namei.o dir.o super.o +exofs-y := ore.o inode.o file.o symlink.o namei.o dir.o super.o obj-$(CONFIG_EXOFS_FS) += exofs.o diff --git a/fs/exofs/exofs.h b/fs/exofs/exofs.h index 39ad50165162..f4e442ec7445 100644 --- a/fs/exofs/exofs.h +++ b/fs/exofs/exofs.h @@ -36,7 +36,8 @@ #include #include #include -#include +#include + #include "common.h" #define EXOFS_ERR(fmt, a...) printk(KERN_ERR "exofs: " fmt, ##a) @@ -52,33 +53,6 @@ /* u64 has problems with printk this will cast it to unsigned long long */ #define _LLU(x) (unsigned long long)(x) -struct exofs_comp { - struct osd_obj_id obj; - u8 cred[OSD_CAP_LEN]; -}; - -struct exofs_layout { - /* Our way of looking at the data_map */ - unsigned stripe_unit; - unsigned mirrors_p1; - - unsigned group_width; - u64 group_depth; - unsigned group_count; -}; - -struct exofs_components { - unsigned numdevs; /* Num of devices in array */ - /* If @single_comp == EC_SINGLE_COMP, @comps points to a single - * component. else there are @numdevs components - */ - enum EC_COMP_USAGE { - EC_SINGLE_COMP = 0, EC_MULTPLE_COMPS = 0xffffffff - } single_comp; - struct exofs_comp *comps; - struct osd_dev **ods; /* osd_dev array */ -}; - /* * our extension to the in-memory superblock */ @@ -95,9 +69,9 @@ struct exofs_sb_info { struct pnfs_osd_data_map data_map; /* Default raid to use * FIXME: Needed ? */ - struct exofs_layout layout; /* Default files layout */ - struct exofs_comp one_comp; /* id & cred of partition id=0*/ - struct exofs_components comps; /* comps for the partition */ + struct ore_layout layout; /* Default files layout */ + struct ore_comp one_comp; /* id & cred of partition id=0*/ + struct ore_components comps; /* comps for the partition */ struct osd_dev *_min_one_dev[1]; /* Place holder for one dev */ }; @@ -111,8 +85,8 @@ struct exofs_i_info { uint32_t i_data[EXOFS_IDATA];/*short symlink names and device #s*/ uint32_t i_dir_start_lookup; /* which page to start lookup */ uint64_t i_commit_size; /* the object's written length */ - struct exofs_comp one_comp; /* same component for all devices */ - struct exofs_components comps; /* inode view of the device table */ + struct ore_comp one_comp; /* same component for all devices */ + struct ore_components comps; /* inode view of the device table */ }; static inline osd_id exofs_oi_objno(struct exofs_i_info *oi) @@ -120,53 +94,6 @@ static inline osd_id exofs_oi_objno(struct exofs_i_info *oi) return oi->vfs_inode.i_ino + EXOFS_OBJ_OFF; } -struct exofs_io_state; -typedef void (*exofs_io_done_fn)(struct exofs_io_state *ios, void *private); - -struct exofs_io_state { - struct kref kref; - - void *private; - exofs_io_done_fn done; - - struct exofs_layout *layout; - struct exofs_components *comps; - - /* Global read/write IO*/ - loff_t offset; - unsigned long length; - void *kern_buff; - - struct page **pages; - unsigned nr_pages; - unsigned pgbase; - unsigned pages_consumed; - - /* Attributes */ - unsigned in_attr_len; - struct osd_attr *in_attr; - unsigned out_attr_len; - struct osd_attr *out_attr; - - bool reading; - - /* Variable array of size numdevs */ - unsigned numdevs; - struct exofs_per_dev_state { - struct osd_request *or; - struct bio *bio; - loff_t offset; - unsigned length; - unsigned dev; - } per_dev[]; -}; - -static inline unsigned exofs_io_state_size(unsigned numdevs) -{ - return sizeof(struct exofs_io_state) + - sizeof(struct exofs_per_dev_state) * numdevs; -} - /* * our inode flags */ @@ -219,30 +146,8 @@ static inline struct exofs_i_info *exofs_i(struct inode *inode) * function declarations * *************************/ -/* ios.c */ -int exofs_get_rw_state(struct exofs_layout *layout, - struct exofs_components *comps, - bool is_reading, u64 offset, u64 length, - struct exofs_io_state **ios); -int exofs_get_io_state(struct exofs_layout *layout, - struct exofs_components *comps, - struct exofs_io_state **ios); -void exofs_put_io_state(struct exofs_io_state *ios); - -int exofs_check_io(struct exofs_io_state *ios, u64 *resid); - -int exofs_sbi_create(struct exofs_io_state *ios); -int exofs_sbi_remove(struct exofs_io_state *ios); -int exofs_sbi_write(struct exofs_io_state *ios); -int exofs_sbi_read(struct exofs_io_state *ios); -int exofs_truncate(struct exofs_layout *layout, struct exofs_components *comps, - u64 size); - -int extract_attr_from_ios(struct exofs_io_state *ios, struct osd_attr *attr); -extern const struct osd_attr g_attr_logical_length; - /* inode.c */ -unsigned exofs_max_io_pages(struct exofs_layout *layout, +unsigned exofs_max_io_pages(struct ore_layout *layout, unsigned expected_pages); int exofs_setattr(struct dentry *, struct iattr *); int exofs_write_begin(struct file *file, struct address_space *mapping, @@ -292,8 +197,8 @@ extern const struct inode_operations exofs_special_inode_operations; extern const struct inode_operations exofs_symlink_inode_operations; extern const struct inode_operations exofs_fast_symlink_inode_operations; -/* exofs_init_comps will initialize an exofs_components device array - * pointing to a single exofs_comp struct, and a round-robin view +/* exofs_init_comps will initialize an ore_components device array + * pointing to a single ore_comp struct, and a round-robin view * of the device table. * The first device of each inode is the [inode->ino % num_devices] * and the rest of the devices sequentially following where the @@ -302,8 +207,8 @@ extern const struct inode_operations exofs_fast_symlink_inode_operations; * bigger and that the device table repeats twice. * See: exofs_read_lookup_dev_table() */ -static inline void exofs_init_comps(struct exofs_components *comps, - struct exofs_comp *one_comp, +static inline void exofs_init_comps(struct ore_components *comps, + struct ore_comp *one_comp, struct exofs_sb_info *sbi, osd_id oid) { unsigned dev_mod = (unsigned)oid, first_dev; diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index 3cc24f25844d..f39a38fc2349 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -43,7 +43,7 @@ enum { BIO_MAX_PAGES_KMALLOC = PAGE_SIZE / sizeof(struct page *), }; -unsigned exofs_max_io_pages(struct exofs_layout *layout, +unsigned exofs_max_io_pages(struct ore_layout *layout, unsigned expected_pages) { unsigned pages = min_t(unsigned, expected_pages, MAX_PAGES_KMALLOC); @@ -58,7 +58,7 @@ struct page_collect { struct exofs_sb_info *sbi; struct inode *inode; unsigned expected_pages; - struct exofs_io_state *ios; + struct ore_io_state *ios; struct page **pages; unsigned alloc_pages; @@ -133,7 +133,7 @@ static void pcol_free(struct page_collect *pcol) pcol->pages = NULL; if (pcol->ios) { - exofs_put_io_state(pcol->ios); + ore_put_io_state(pcol->ios); pcol->ios = NULL; } } @@ -193,7 +193,7 @@ static int __readpages_done(struct page_collect *pcol) u64 resid; u64 good_bytes; u64 length = 0; - int ret = exofs_check_io(pcol->ios, &resid); + int ret = ore_check_io(pcol->ios, &resid); if (likely(!ret)) good_bytes = pcol->length; @@ -234,7 +234,7 @@ static int __readpages_done(struct page_collect *pcol) } /* callback of async reads */ -static void readpages_done(struct exofs_io_state *ios, void *p) +static void readpages_done(struct ore_io_state *ios, void *p) { struct page_collect *pcol = p; @@ -262,7 +262,7 @@ static void _unlock_pcol_pages(struct page_collect *pcol, int ret, int rw) static int read_exec(struct page_collect *pcol) { struct exofs_i_info *oi = exofs_i(pcol->inode); - struct exofs_io_state *ios; + struct ore_io_state *ios; struct page_collect *pcol_copy = NULL; int ret; @@ -270,7 +270,7 @@ static int read_exec(struct page_collect *pcol) return 0; if (!pcol->ios) { - int ret = exofs_get_rw_state(&pcol->sbi->layout, &oi->comps, true, + int ret = ore_get_rw_state(&pcol->sbi->layout, &oi->comps, true, pcol->pg_first << PAGE_CACHE_SHIFT, pcol->length, &pcol->ios); @@ -283,7 +283,7 @@ static int read_exec(struct page_collect *pcol) ios->nr_pages = pcol->nr_pages; if (pcol->read_4_write) { - exofs_sbi_read(pcol->ios); + ore_read(pcol->ios); return __readpages_done(pcol); } @@ -296,7 +296,7 @@ static int read_exec(struct page_collect *pcol) *pcol_copy = *pcol; ios->done = readpages_done; ios->private = pcol_copy; - ret = exofs_sbi_read(ios); + ret = ore_read(ios); if (unlikely(ret)) goto err; @@ -458,14 +458,14 @@ static int exofs_readpage(struct file *file, struct page *page) } /* Callback for osd_write. All writes are asynchronous */ -static void writepages_done(struct exofs_io_state *ios, void *p) +static void writepages_done(struct ore_io_state *ios, void *p) { struct page_collect *pcol = p; int i; u64 resid; u64 good_bytes; u64 length = 0; - int ret = exofs_check_io(ios, &resid); + int ret = ore_check_io(ios, &resid); atomic_dec(&pcol->sbi->s_curr_pending); @@ -508,7 +508,7 @@ static void writepages_done(struct exofs_io_state *ios, void *p) static int write_exec(struct page_collect *pcol) { struct exofs_i_info *oi = exofs_i(pcol->inode); - struct exofs_io_state *ios; + struct ore_io_state *ios; struct page_collect *pcol_copy = NULL; int ret; @@ -516,7 +516,7 @@ static int write_exec(struct page_collect *pcol) return 0; BUG_ON(pcol->ios); - ret = exofs_get_rw_state(&pcol->sbi->layout, &oi->comps, false, + ret = ore_get_rw_state(&pcol->sbi->layout, &oi->comps, false, pcol->pg_first << PAGE_CACHE_SHIFT, pcol->length, &pcol->ios); @@ -538,9 +538,9 @@ static int write_exec(struct page_collect *pcol) ios->done = writepages_done; ios->private = pcol_copy; - ret = exofs_sbi_write(ios); + ret = ore_write(ios); if (unlikely(ret)) { - EXOFS_ERR("write_exec: exofs_sbi_write() Failed\n"); + EXOFS_ERR("write_exec: ore_write() Failed\n"); goto err; } @@ -860,7 +860,7 @@ static int _do_truncate(struct inode *inode, loff_t newsize) inode->i_mtime = inode->i_ctime = CURRENT_TIME; - ret = exofs_truncate(&sbi->layout, &oi->comps, (u64)newsize); + ret = ore_truncate(&sbi->layout, &oi->comps, (u64)newsize); if (likely(!ret)) truncate_setsize(inode, newsize); @@ -923,13 +923,13 @@ static int exofs_get_inode(struct super_block *sb, struct exofs_i_info *oi, [1] = g_attr_inode_file_layout, [2] = g_attr_inode_dir_layout, }; - struct exofs_io_state *ios; + struct ore_io_state *ios; struct exofs_on_disk_inode_layout *layout; int ret; - ret = exofs_get_io_state(&sbi->layout, &oi->comps, &ios); + ret = ore_get_io_state(&sbi->layout, &oi->comps, &ios); if (unlikely(ret)) { - EXOFS_ERR("%s: exofs_get_io_state failed.\n", __func__); + EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__); return ret; } @@ -939,7 +939,7 @@ static int exofs_get_inode(struct super_block *sb, struct exofs_i_info *oi, ios->in_attr = attrs; ios->in_attr_len = ARRAY_SIZE(attrs); - ret = exofs_sbi_read(ios); + ret = ore_read(ios); if (unlikely(ret)) { EXOFS_ERR("object(0x%llx) corrupted, return empty file=>%d\n", _LLU(oi->one_comp.obj.id), ret); @@ -992,7 +992,7 @@ static int exofs_get_inode(struct super_block *sb, struct exofs_i_info *oi, } out: - exofs_put_io_state(ios); + ore_put_io_state(ios); return ret; } @@ -1111,15 +1111,15 @@ int __exofs_wait_obj_created(struct exofs_i_info *oi) * set the obj_created flag so that other methods know that the object exists on * the OSD. */ -static void create_done(struct exofs_io_state *ios, void *p) +static void create_done(struct ore_io_state *ios, void *p) { struct inode *inode = p; struct exofs_i_info *oi = exofs_i(inode); struct exofs_sb_info *sbi = inode->i_sb->s_fs_info; int ret; - ret = exofs_check_io(ios, NULL); - exofs_put_io_state(ios); + ret = ore_check_io(ios, NULL); + ore_put_io_state(ios); atomic_dec(&sbi->s_curr_pending); @@ -1149,7 +1149,7 @@ struct inode *exofs_new_inode(struct inode *dir, int mode) struct exofs_sb_info *sbi = sb->s_fs_info; struct inode *inode; struct exofs_i_info *oi; - struct exofs_io_state *ios; + struct ore_io_state *ios; int ret; inode = new_inode(sb); @@ -1178,18 +1178,18 @@ struct inode *exofs_new_inode(struct inode *dir, int mode) mark_inode_dirty(inode); - ret = exofs_get_io_state(&sbi->layout, &oi->comps, &ios); + ret = ore_get_io_state(&sbi->layout, &oi->comps, &ios); if (unlikely(ret)) { - EXOFS_ERR("exofs_new_inode: exofs_get_io_state failed\n"); + EXOFS_ERR("exofs_new_inode: ore_get_io_state failed\n"); return ERR_PTR(ret); } ios->done = create_done; ios->private = inode; - ret = exofs_sbi_create(ios); + ret = ore_create(ios); if (ret) { - exofs_put_io_state(ios); + ore_put_io_state(ios); return ERR_PTR(ret); } atomic_inc(&sbi->s_curr_pending); @@ -1208,11 +1208,11 @@ struct updatei_args { /* * Callback function from exofs_update_inode(). */ -static void updatei_done(struct exofs_io_state *ios, void *p) +static void updatei_done(struct ore_io_state *ios, void *p) { struct updatei_args *args = p; - exofs_put_io_state(ios); + ore_put_io_state(ios); atomic_dec(&args->sbi->s_curr_pending); @@ -1228,7 +1228,7 @@ static int exofs_update_inode(struct inode *inode, int do_sync) struct exofs_i_info *oi = exofs_i(inode); struct super_block *sb = inode->i_sb; struct exofs_sb_info *sbi = sb->s_fs_info; - struct exofs_io_state *ios; + struct ore_io_state *ios; struct osd_attr attr; struct exofs_fcb *fcb; struct updatei_args *args; @@ -1267,9 +1267,9 @@ static int exofs_update_inode(struct inode *inode, int do_sync) } else memcpy(fcb->i_data, oi->i_data, sizeof(fcb->i_data)); - ret = exofs_get_io_state(&sbi->layout, &oi->comps, &ios); + ret = ore_get_io_state(&sbi->layout, &oi->comps, &ios); if (unlikely(ret)) { - EXOFS_ERR("%s: exofs_get_io_state failed.\n", __func__); + EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__); goto free_args; } @@ -1286,13 +1286,13 @@ static int exofs_update_inode(struct inode *inode, int do_sync) ios->private = args; } - ret = exofs_sbi_write(ios); + ret = ore_write(ios); if (!do_sync && !ret) { atomic_inc(&sbi->s_curr_pending); goto out; /* deallocation in updatei_done */ } - exofs_put_io_state(ios); + ore_put_io_state(ios); free_args: kfree(args); out: @@ -1311,11 +1311,11 @@ int exofs_write_inode(struct inode *inode, struct writeback_control *wbc) * Callback function from exofs_delete_inode() - don't have much cleaning up to * do. */ -static void delete_done(struct exofs_io_state *ios, void *p) +static void delete_done(struct ore_io_state *ios, void *p) { struct exofs_sb_info *sbi = p; - exofs_put_io_state(ios); + ore_put_io_state(ios); atomic_dec(&sbi->s_curr_pending); } @@ -1330,7 +1330,7 @@ void exofs_evict_inode(struct inode *inode) struct exofs_i_info *oi = exofs_i(inode); struct super_block *sb = inode->i_sb; struct exofs_sb_info *sbi = sb->s_fs_info; - struct exofs_io_state *ios; + struct ore_io_state *ios; int ret; truncate_inode_pages(&inode->i_data, 0); @@ -1350,19 +1350,19 @@ void exofs_evict_inode(struct inode *inode) /* ignore the error, attempt a remove anyway */ /* Now Remove the OSD objects */ - ret = exofs_get_io_state(&sbi->layout, &oi->comps, &ios); + ret = ore_get_io_state(&sbi->layout, &oi->comps, &ios); if (unlikely(ret)) { - EXOFS_ERR("%s: exofs_get_io_state failed\n", __func__); + EXOFS_ERR("%s: ore_get_io_state failed\n", __func__); return; } ios->done = delete_done; ios->private = sbi; - ret = exofs_sbi_remove(ios); + ret = ore_remove(ios); if (ret) { - EXOFS_ERR("%s: exofs_sbi_remove failed\n", __func__); - exofs_put_io_state(ios); + EXOFS_ERR("%s: ore_remove failed\n", __func__); + ore_put_io_state(ios); return; } atomic_inc(&sbi->s_curr_pending); diff --git a/fs/exofs/ios.c b/fs/exofs/ios.c deleted file mode 100644 index f9d5c5a823fb..000000000000 --- a/fs/exofs/ios.c +++ /dev/null @@ -1,810 +0,0 @@ -/* - * Copyright (C) 2005, 2006 - * Avishay Traeger (avishay@gmail.com) - * Copyright (C) 2008, 2009 - * Boaz Harrosh - * - * This file is part of exofs. - * - * exofs 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. Since it is based on ext2, and the only - * valid version of GPL for the Linux kernel is version 2, the only valid - * version of GPL for exofs is version 2. - * - * exofs 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 exofs; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include - -#include "exofs.h" - -#define EXOFS_DBGMSG2(M...) do {} while (0) -/* #define EXOFS_DBGMSG2 EXOFS_DBGMSG */ - -static u8 *_ios_cred(struct exofs_io_state *ios, unsigned index) -{ - return ios->comps->comps[index & ios->comps->single_comp].cred; -} - -static struct osd_obj_id *_ios_obj(struct exofs_io_state *ios, unsigned index) -{ - return &ios->comps->comps[index & ios->comps->single_comp].obj; -} - -static struct osd_dev *_ios_od(struct exofs_io_state *ios, unsigned index) -{ - return ios->comps->ods[index]; -} - -int exofs_get_rw_state(struct exofs_layout *layout, - struct exofs_components *comps, - bool is_reading, u64 offset, u64 length, - struct exofs_io_state **pios) -{ - struct exofs_io_state *ios; - - /*TODO: Maybe use kmem_cach per sbi of size - * exofs_io_state_size(layout->s_numdevs) - */ - ios = kzalloc(exofs_io_state_size(comps->numdevs), GFP_KERNEL); - if (unlikely(!ios)) { - EXOFS_DBGMSG("Failed kzalloc bytes=%d\n", - exofs_io_state_size(comps->numdevs)); - *pios = NULL; - return -ENOMEM; - } - - ios->layout = layout; - ios->comps = comps; - ios->offset = offset; - ios->length = length; - ios->reading = is_reading; - - *pios = ios; - return 0; -} - -int exofs_get_io_state(struct exofs_layout *layout, - struct exofs_components *comps, - struct exofs_io_state **ios) -{ - return exofs_get_rw_state(layout, comps, true, 0, 0, ios); -} - -void exofs_put_io_state(struct exofs_io_state *ios) -{ - if (ios) { - unsigned i; - - for (i = 0; i < ios->numdevs; i++) { - struct exofs_per_dev_state *per_dev = &ios->per_dev[i]; - - if (per_dev->or) - osd_end_request(per_dev->or); - if (per_dev->bio) - bio_put(per_dev->bio); - } - - kfree(ios); - } -} - -static void _sync_done(struct exofs_io_state *ios, void *p) -{ - struct completion *waiting = p; - - complete(waiting); -} - -static void _last_io(struct kref *kref) -{ - struct exofs_io_state *ios = container_of( - kref, struct exofs_io_state, kref); - - ios->done(ios, ios->private); -} - -static void _done_io(struct osd_request *or, void *p) -{ - struct exofs_io_state *ios = p; - - kref_put(&ios->kref, _last_io); -} - -static int exofs_io_execute(struct exofs_io_state *ios) -{ - DECLARE_COMPLETION_ONSTACK(wait); - bool sync = (ios->done == NULL); - int i, ret; - - if (sync) { - ios->done = _sync_done; - ios->private = &wait; - } - - for (i = 0; i < ios->numdevs; i++) { - struct osd_request *or = ios->per_dev[i].or; - if (unlikely(!or)) - continue; - - ret = osd_finalize_request(or, 0, _ios_cred(ios, i), NULL); - if (unlikely(ret)) { - EXOFS_DBGMSG("Failed to osd_finalize_request() => %d\n", - ret); - return ret; - } - } - - kref_init(&ios->kref); - - for (i = 0; i < ios->numdevs; i++) { - struct osd_request *or = ios->per_dev[i].or; - if (unlikely(!or)) - continue; - - kref_get(&ios->kref); - osd_execute_request_async(or, _done_io, ios); - } - - kref_put(&ios->kref, _last_io); - ret = 0; - - if (sync) { - wait_for_completion(&wait); - ret = exofs_check_io(ios, NULL); - } - return ret; -} - -static void _clear_bio(struct bio *bio) -{ - struct bio_vec *bv; - unsigned i; - - __bio_for_each_segment(bv, bio, i, 0) { - unsigned this_count = bv->bv_len; - - if (likely(PAGE_SIZE == this_count)) - clear_highpage(bv->bv_page); - else - zero_user(bv->bv_page, bv->bv_offset, this_count); - } -} - -int exofs_check_io(struct exofs_io_state *ios, u64 *resid) -{ - enum osd_err_priority acumulated_osd_err = 0; - int acumulated_lin_err = 0; - int i; - - for (i = 0; i < ios->numdevs; i++) { - struct osd_sense_info osi; - struct osd_request *or = ios->per_dev[i].or; - int ret; - - if (unlikely(!or)) - continue; - - ret = osd_req_decode_sense(or, &osi); - if (likely(!ret)) - continue; - - if (OSD_ERR_PRI_CLEAR_PAGES == osi.osd_err_pri) { - /* start read offset passed endof file */ - _clear_bio(ios->per_dev[i].bio); - EXOFS_DBGMSG("start read offset passed end of file " - "offset=0x%llx, length=0x%llx\n", - _LLU(ios->per_dev[i].offset), - _LLU(ios->per_dev[i].length)); - - continue; /* we recovered */ - } - - if (osi.osd_err_pri >= acumulated_osd_err) { - acumulated_osd_err = osi.osd_err_pri; - acumulated_lin_err = ret; - } - } - - /* TODO: raid specific residual calculations */ - if (resid) { - if (likely(!acumulated_lin_err)) - *resid = 0; - else - *resid = ios->length; - } - - return acumulated_lin_err; -} - -/* - * L - logical offset into the file - * - * U - The number of bytes in a stripe within a group - * - * U = stripe_unit * group_width - * - * T - The number of bytes striped within a group of component objects - * (before advancing to the next group) - * - * T = stripe_unit * group_width * group_depth - * - * S - The number of bytes striped across all component objects - * before the pattern repeats - * - * S = stripe_unit * group_width * group_depth * group_count - * - * M - The "major" (i.e., across all components) stripe number - * - * M = L / S - * - * G - Counts the groups from the beginning of the major stripe - * - * G = (L - (M * S)) / T [or (L % S) / T] - * - * H - The byte offset within the group - * - * H = (L - (M * S)) % T [or (L % S) % T] - * - * N - The "minor" (i.e., across the group) stripe number - * - * N = H / U - * - * C - The component index coresponding to L - * - * C = (H - (N * U)) / stripe_unit + G * group_width - * [or (L % U) / stripe_unit + G * group_width] - * - * O - The component offset coresponding to L - * - * O = L % stripe_unit + N * stripe_unit + M * group_depth * stripe_unit - */ -struct _striping_info { - u64 obj_offset; - u64 group_length; - u64 M; /* for truncate */ - unsigned dev; - unsigned unit_off; -}; - -static void _calc_stripe_info(struct exofs_layout *layout, u64 file_offset, - struct _striping_info *si) -{ - u32 stripe_unit = layout->stripe_unit; - u32 group_width = layout->group_width; - u64 group_depth = layout->group_depth; - - u32 U = stripe_unit * group_width; - u64 T = U * group_depth; - u64 S = T * layout->group_count; - u64 M = div64_u64(file_offset, S); - - /* - G = (L - (M * S)) / T - H = (L - (M * S)) % T - */ - u64 LmodS = file_offset - M * S; - u32 G = div64_u64(LmodS, T); - u64 H = LmodS - G * T; - - u32 N = div_u64(H, U); - - /* "H - (N * U)" is just "H % U" so it's bound to u32 */ - si->dev = (u32)(H - (N * U)) / stripe_unit + G * group_width; - si->dev *= layout->mirrors_p1; - - div_u64_rem(file_offset, stripe_unit, &si->unit_off); - - si->obj_offset = si->unit_off + (N * stripe_unit) + - (M * group_depth * stripe_unit); - - si->group_length = T - H; - si->M = M; -} - -static int _add_stripe_unit(struct exofs_io_state *ios, unsigned *cur_pg, - unsigned pgbase, struct exofs_per_dev_state *per_dev, - int cur_len) -{ - unsigned pg = *cur_pg; - struct request_queue *q = - osd_request_queue(_ios_od(ios, per_dev->dev)); - - per_dev->length += cur_len; - - if (per_dev->bio == NULL) { - unsigned pages_in_stripe = ios->layout->group_width * - (ios->layout->stripe_unit / PAGE_SIZE); - unsigned bio_size = (ios->nr_pages + pages_in_stripe) / - ios->layout->group_width; - - per_dev->bio = bio_kmalloc(GFP_KERNEL, bio_size); - if (unlikely(!per_dev->bio)) { - EXOFS_DBGMSG("Failed to allocate BIO size=%u\n", - bio_size); - return -ENOMEM; - } - } - - while (cur_len > 0) { - unsigned pglen = min_t(unsigned, PAGE_SIZE - pgbase, cur_len); - unsigned added_len; - - BUG_ON(ios->nr_pages <= pg); - cur_len -= pglen; - - added_len = bio_add_pc_page(q, per_dev->bio, ios->pages[pg], - pglen, pgbase); - if (unlikely(pglen != added_len)) - return -ENOMEM; - pgbase = 0; - ++pg; - } - BUG_ON(cur_len); - - *cur_pg = pg; - return 0; -} - -static int _prepare_one_group(struct exofs_io_state *ios, u64 length, - struct _striping_info *si) -{ - unsigned stripe_unit = ios->layout->stripe_unit; - unsigned mirrors_p1 = ios->layout->mirrors_p1; - unsigned devs_in_group = ios->layout->group_width * mirrors_p1; - unsigned dev = si->dev; - unsigned first_dev = dev - (dev % devs_in_group); - unsigned max_comp = ios->numdevs ? ios->numdevs - mirrors_p1 : 0; - unsigned cur_pg = ios->pages_consumed; - int ret = 0; - - while (length) { - struct exofs_per_dev_state *per_dev = &ios->per_dev[dev]; - unsigned cur_len, page_off = 0; - - if (!per_dev->length) { - per_dev->dev = dev; - if (dev < si->dev) { - per_dev->offset = si->obj_offset + stripe_unit - - si->unit_off; - cur_len = stripe_unit; - } else if (dev == si->dev) { - per_dev->offset = si->obj_offset; - cur_len = stripe_unit - si->unit_off; - page_off = si->unit_off & ~PAGE_MASK; - BUG_ON(page_off && (page_off != ios->pgbase)); - } else { /* dev > si->dev */ - per_dev->offset = si->obj_offset - si->unit_off; - cur_len = stripe_unit; - } - - if (max_comp < dev) - max_comp = dev; - } else { - cur_len = stripe_unit; - } - if (cur_len >= length) - cur_len = length; - - ret = _add_stripe_unit(ios, &cur_pg, page_off , per_dev, - cur_len); - if (unlikely(ret)) - goto out; - - dev += mirrors_p1; - dev = (dev % devs_in_group) + first_dev; - - length -= cur_len; - } -out: - ios->numdevs = max_comp + mirrors_p1; - ios->pages_consumed = cur_pg; - return ret; -} - -static int _prepare_for_striping(struct exofs_io_state *ios) -{ - u64 length = ios->length; - u64 offset = ios->offset; - struct _striping_info si; - int ret = 0; - - if (!ios->pages) { - if (ios->kern_buff) { - struct exofs_per_dev_state *per_dev = &ios->per_dev[0]; - - _calc_stripe_info(ios->layout, ios->offset, &si); - per_dev->offset = si.obj_offset; - per_dev->dev = si.dev; - - /* no cross device without page array */ - BUG_ON((ios->layout->group_width > 1) && - (si.unit_off + ios->length > - ios->layout->stripe_unit)); - } - ios->numdevs = ios->layout->mirrors_p1; - return 0; - } - - while (length) { - _calc_stripe_info(ios->layout, offset, &si); - - if (length < si.group_length) - si.group_length = length; - - ret = _prepare_one_group(ios, si.group_length, &si); - if (unlikely(ret)) - goto out; - - offset += si.group_length; - length -= si.group_length; - } - -out: - return ret; -} - -int exofs_sbi_create(struct exofs_io_state *ios) -{ - int i, ret; - - for (i = 0; i < ios->comps->numdevs; i++) { - struct osd_request *or; - - or = osd_start_request(_ios_od(ios, i), GFP_KERNEL); - if (unlikely(!or)) { - EXOFS_ERR("%s: osd_start_request failed\n", __func__); - ret = -ENOMEM; - goto out; - } - ios->per_dev[i].or = or; - ios->numdevs++; - - osd_req_create_object(or, _ios_obj(ios, i)); - } - ret = exofs_io_execute(ios); - -out: - return ret; -} - -int exofs_sbi_remove(struct exofs_io_state *ios) -{ - int i, ret; - - for (i = 0; i < ios->comps->numdevs; i++) { - struct osd_request *or; - - or = osd_start_request(_ios_od(ios, i), GFP_KERNEL); - if (unlikely(!or)) { - EXOFS_ERR("%s: osd_start_request failed\n", __func__); - ret = -ENOMEM; - goto out; - } - ios->per_dev[i].or = or; - ios->numdevs++; - - osd_req_remove_object(or, _ios_obj(ios, i)); - } - ret = exofs_io_execute(ios); - -out: - return ret; -} - -static int _sbi_write_mirror(struct exofs_io_state *ios, int cur_comp) -{ - struct exofs_per_dev_state *master_dev = &ios->per_dev[cur_comp]; - unsigned dev = ios->per_dev[cur_comp].dev; - unsigned last_comp = cur_comp + ios->layout->mirrors_p1; - int ret = 0; - - if (ios->pages && !master_dev->length) - return 0; /* Just an empty slot */ - - for (; cur_comp < last_comp; ++cur_comp, ++dev) { - struct exofs_per_dev_state *per_dev = &ios->per_dev[cur_comp]; - struct osd_request *or; - - or = osd_start_request(_ios_od(ios, dev), GFP_KERNEL); - if (unlikely(!or)) { - EXOFS_ERR("%s: osd_start_request failed\n", __func__); - ret = -ENOMEM; - goto out; - } - per_dev->or = or; - per_dev->offset = master_dev->offset; - - if (ios->pages) { - struct bio *bio; - - if (per_dev != master_dev) { - bio = bio_kmalloc(GFP_KERNEL, - master_dev->bio->bi_max_vecs); - if (unlikely(!bio)) { - EXOFS_DBGMSG( - "Failed to allocate BIO size=%u\n", - master_dev->bio->bi_max_vecs); - ret = -ENOMEM; - goto out; - } - - __bio_clone(bio, master_dev->bio); - bio->bi_bdev = NULL; - bio->bi_next = NULL; - per_dev->length = master_dev->length; - per_dev->bio = bio; - per_dev->dev = dev; - } else { - bio = master_dev->bio; - /* FIXME: bio_set_dir() */ - bio->bi_rw |= REQ_WRITE; - } - - osd_req_write(or, _ios_obj(ios, dev), per_dev->offset, - bio, per_dev->length); - EXOFS_DBGMSG("write(0x%llx) offset=0x%llx " - "length=0x%llx dev=%d\n", - _LLU(_ios_obj(ios, dev)->id), - _LLU(per_dev->offset), - _LLU(per_dev->length), dev); - } else if (ios->kern_buff) { - ret = osd_req_write_kern(or, _ios_obj(ios, dev), - per_dev->offset, - ios->kern_buff, ios->length); - if (unlikely(ret)) - goto out; - EXOFS_DBGMSG2("write_kern(0x%llx) offset=0x%llx " - "length=0x%llx dev=%d\n", - _LLU(_ios_obj(ios, dev)->id), - _LLU(per_dev->offset), - _LLU(ios->length), dev); - } else { - osd_req_set_attributes(or, _ios_obj(ios, dev)); - EXOFS_DBGMSG2("obj(0x%llx) set_attributes=%d dev=%d\n", - _LLU(_ios_obj(ios, dev)->id), - ios->out_attr_len, dev); - } - - if (ios->out_attr) - osd_req_add_set_attr_list(or, ios->out_attr, - ios->out_attr_len); - - if (ios->in_attr) - osd_req_add_get_attr_list(or, ios->in_attr, - ios->in_attr_len); - } - -out: - return ret; -} - -int exofs_sbi_write(struct exofs_io_state *ios) -{ - int i; - int ret; - - ret = _prepare_for_striping(ios); - if (unlikely(ret)) - return ret; - - for (i = 0; i < ios->numdevs; i += ios->layout->mirrors_p1) { - ret = _sbi_write_mirror(ios, i); - if (unlikely(ret)) - return ret; - } - - ret = exofs_io_execute(ios); - return ret; -} - -static int _sbi_read_mirror(struct exofs_io_state *ios, unsigned cur_comp) -{ - struct osd_request *or; - struct exofs_per_dev_state *per_dev = &ios->per_dev[cur_comp]; - struct osd_obj_id *obj = _ios_obj(ios, cur_comp); - unsigned first_dev = (unsigned)obj->id; - - if (ios->pages && !per_dev->length) - return 0; /* Just an empty slot */ - - first_dev = per_dev->dev + first_dev % ios->layout->mirrors_p1; - or = osd_start_request(_ios_od(ios, first_dev), GFP_KERNEL); - if (unlikely(!or)) { - EXOFS_ERR("%s: osd_start_request failed\n", __func__); - return -ENOMEM; - } - per_dev->or = or; - - if (ios->pages) { - osd_req_read(or, obj, per_dev->offset, - per_dev->bio, per_dev->length); - EXOFS_DBGMSG("read(0x%llx) offset=0x%llx length=0x%llx" - " dev=%d\n", _LLU(obj->id), - _LLU(per_dev->offset), _LLU(per_dev->length), - first_dev); - } else if (ios->kern_buff) { - int ret = osd_req_read_kern(or, obj, per_dev->offset, - ios->kern_buff, ios->length); - EXOFS_DBGMSG2("read_kern(0x%llx) offset=0x%llx " - "length=0x%llx dev=%d ret=>%d\n", - _LLU(obj->id), _LLU(per_dev->offset), - _LLU(ios->length), first_dev, ret); - if (unlikely(ret)) - return ret; - } else { - osd_req_get_attributes(or, obj); - EXOFS_DBGMSG2("obj(0x%llx) get_attributes=%d dev=%d\n", - _LLU(obj->id), - ios->in_attr_len, first_dev); - } - if (ios->out_attr) - osd_req_add_set_attr_list(or, ios->out_attr, ios->out_attr_len); - - if (ios->in_attr) - osd_req_add_get_attr_list(or, ios->in_attr, ios->in_attr_len); - - return 0; -} - -int exofs_sbi_read(struct exofs_io_state *ios) -{ - int i; - int ret; - - ret = _prepare_for_striping(ios); - if (unlikely(ret)) - return ret; - - for (i = 0; i < ios->numdevs; i += ios->layout->mirrors_p1) { - ret = _sbi_read_mirror(ios, i); - if (unlikely(ret)) - return ret; - } - - ret = exofs_io_execute(ios); - return ret; -} - -int extract_attr_from_ios(struct exofs_io_state *ios, struct osd_attr *attr) -{ - struct osd_attr cur_attr = {.attr_page = 0}; /* start with zeros */ - void *iter = NULL; - int nelem; - - do { - nelem = 1; - osd_req_decode_get_attr_list(ios->per_dev[0].or, - &cur_attr, &nelem, &iter); - if ((cur_attr.attr_page == attr->attr_page) && - (cur_attr.attr_id == attr->attr_id)) { - attr->len = cur_attr.len; - attr->val_ptr = cur_attr.val_ptr; - return 0; - } - } while (iter); - - return -EIO; -} - -static int _truncate_mirrors(struct exofs_io_state *ios, unsigned cur_comp, - struct osd_attr *attr) -{ - int last_comp = cur_comp + ios->layout->mirrors_p1; - - for (; cur_comp < last_comp; ++cur_comp) { - struct exofs_per_dev_state *per_dev = &ios->per_dev[cur_comp]; - struct osd_request *or; - - or = osd_start_request(_ios_od(ios, cur_comp), GFP_KERNEL); - if (unlikely(!or)) { - EXOFS_ERR("%s: osd_start_request failed\n", __func__); - return -ENOMEM; - } - per_dev->or = or; - - osd_req_set_attributes(or, _ios_obj(ios, cur_comp)); - osd_req_add_set_attr_list(or, attr, 1); - } - - return 0; -} - -struct _trunc_info { - struct _striping_info si; - u64 prev_group_obj_off; - u64 next_group_obj_off; - - unsigned first_group_dev; - unsigned nex_group_dev; - unsigned max_devs; -}; - -void _calc_trunk_info(struct exofs_layout *layout, u64 file_offset, - struct _trunc_info *ti) -{ - unsigned stripe_unit = layout->stripe_unit; - - _calc_stripe_info(layout, file_offset, &ti->si); - - ti->prev_group_obj_off = ti->si.M * stripe_unit; - ti->next_group_obj_off = ti->si.M ? (ti->si.M - 1) * stripe_unit : 0; - - ti->first_group_dev = ti->si.dev - (ti->si.dev % layout->group_width); - ti->nex_group_dev = ti->first_group_dev + layout->group_width; - ti->max_devs = layout->group_width * layout->group_count; -} - -int exofs_truncate(struct exofs_layout *layout, struct exofs_components *comps, - u64 size) -{ - struct exofs_io_state *ios; - struct exofs_trunc_attr { - struct osd_attr attr; - __be64 newsize; - } *size_attrs; - struct _trunc_info ti; - int i, ret; - - ret = exofs_get_io_state(layout, comps, &ios); - if (unlikely(ret)) - return ret; - - _calc_trunk_info(ios->layout, size, &ti); - - size_attrs = kcalloc(ti.max_devs, sizeof(*size_attrs), - GFP_KERNEL); - if (unlikely(!size_attrs)) { - ret = -ENOMEM; - goto out; - } - - ios->numdevs = ios->comps->numdevs; - - for (i = 0; i < ti.max_devs; ++i) { - struct exofs_trunc_attr *size_attr = &size_attrs[i]; - u64 obj_size; - - if (i < ti.first_group_dev) - obj_size = ti.prev_group_obj_off; - else if (i >= ti.nex_group_dev) - obj_size = ti.next_group_obj_off; - else if (i < ti.si.dev) /* dev within this group */ - obj_size = ti.si.obj_offset + - ios->layout->stripe_unit - ti.si.unit_off; - else if (i == ti.si.dev) - obj_size = ti.si.obj_offset; - else /* i > ti.dev */ - obj_size = ti.si.obj_offset - ti.si.unit_off; - - size_attr->newsize = cpu_to_be64(obj_size); - size_attr->attr = g_attr_logical_length; - size_attr->attr.val_ptr = &size_attr->newsize; - - EXOFS_DBGMSG("trunc(0x%llx) obj_offset=0x%llx dev=%d\n", - _LLU(comps->comps->obj.id), _LLU(obj_size), i); - ret = _truncate_mirrors(ios, i * ios->layout->mirrors_p1, - &size_attr->attr); - if (unlikely(ret)) - goto out; - } - ret = exofs_io_execute(ios); - -out: - kfree(size_attrs); - exofs_put_io_state(ios); - return ret; -} - -const struct osd_attr g_attr_logical_length = ATTR_DEF( - OSD_APAGE_OBJECT_INFORMATION, OSD_ATTR_OI_LOGICAL_LENGTH, 8); diff --git a/fs/exofs/ore.c b/fs/exofs/ore.c new file mode 100644 index 000000000000..3a000084cd21 --- /dev/null +++ b/fs/exofs/ore.c @@ -0,0 +1,820 @@ +/* + * Copyright (C) 2005, 2006 + * Avishay Traeger (avishay@gmail.com) + * Copyright (C) 2008, 2009 + * Boaz Harrosh + * + * This file is part of exofs. + * + * exofs 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. Since it is based on ext2, and the only + * valid version of GPL for the Linux kernel is version 2, the only valid + * version of GPL for exofs is version 2. + * + * exofs 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 exofs; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include + +#define ORE_ERR(fmt, a...) printk(KERN_ERR "ore: " fmt, ##a) + +#ifdef CONFIG_EXOFS_DEBUG +#define ORE_DBGMSG(fmt, a...) \ + printk(KERN_NOTICE "ore @%s:%d: " fmt, __func__, __LINE__, ##a) +#else +#define ORE_DBGMSG(fmt, a...) \ + do { if (0) printk(fmt, ##a); } while (0) +#endif + +/* u64 has problems with printk this will cast it to unsigned long long */ +#define _LLU(x) (unsigned long long)(x) + +#define ORE_DBGMSG2(M...) do {} while (0) +/* #define ORE_DBGMSG2 ORE_DBGMSG */ + +static u8 *_ios_cred(struct ore_io_state *ios, unsigned index) +{ + return ios->comps->comps[index & ios->comps->single_comp].cred; +} + +static struct osd_obj_id *_ios_obj(struct ore_io_state *ios, unsigned index) +{ + return &ios->comps->comps[index & ios->comps->single_comp].obj; +} + +static struct osd_dev *_ios_od(struct ore_io_state *ios, unsigned index) +{ + return ios->comps->ods[index]; +} + +int ore_get_rw_state(struct ore_layout *layout, struct ore_components *comps, + bool is_reading, u64 offset, u64 length, + struct ore_io_state **pios) +{ + struct ore_io_state *ios; + + /*TODO: Maybe use kmem_cach per sbi of size + * exofs_io_state_size(layout->s_numdevs) + */ + ios = kzalloc(ore_io_state_size(comps->numdevs), GFP_KERNEL); + if (unlikely(!ios)) { + ORE_DBGMSG("Failed kzalloc bytes=%d\n", + ore_io_state_size(comps->numdevs)); + *pios = NULL; + return -ENOMEM; + } + + ios->layout = layout; + ios->comps = comps; + ios->offset = offset; + ios->length = length; + ios->reading = is_reading; + + *pios = ios; + return 0; +} + +int ore_get_io_state(struct ore_layout *layout, struct ore_components *comps, + struct ore_io_state **ios) +{ + return ore_get_rw_state(layout, comps, true, 0, 0, ios); +} + +void ore_put_io_state(struct ore_io_state *ios) +{ + if (ios) { + unsigned i; + + for (i = 0; i < ios->numdevs; i++) { + struct ore_per_dev_state *per_dev = &ios->per_dev[i]; + + if (per_dev->or) + osd_end_request(per_dev->or); + if (per_dev->bio) + bio_put(per_dev->bio); + } + + kfree(ios); + } +} + +static void _sync_done(struct ore_io_state *ios, void *p) +{ + struct completion *waiting = p; + + complete(waiting); +} + +static void _last_io(struct kref *kref) +{ + struct ore_io_state *ios = container_of( + kref, struct ore_io_state, kref); + + ios->done(ios, ios->private); +} + +static void _done_io(struct osd_request *or, void *p) +{ + struct ore_io_state *ios = p; + + kref_put(&ios->kref, _last_io); +} + +static int ore_io_execute(struct ore_io_state *ios) +{ + DECLARE_COMPLETION_ONSTACK(wait); + bool sync = (ios->done == NULL); + int i, ret; + + if (sync) { + ios->done = _sync_done; + ios->private = &wait; + } + + for (i = 0; i < ios->numdevs; i++) { + struct osd_request *or = ios->per_dev[i].or; + if (unlikely(!or)) + continue; + + ret = osd_finalize_request(or, 0, _ios_cred(ios, i), NULL); + if (unlikely(ret)) { + ORE_DBGMSG("Failed to osd_finalize_request() => %d\n", + ret); + return ret; + } + } + + kref_init(&ios->kref); + + for (i = 0; i < ios->numdevs; i++) { + struct osd_request *or = ios->per_dev[i].or; + if (unlikely(!or)) + continue; + + kref_get(&ios->kref); + osd_execute_request_async(or, _done_io, ios); + } + + kref_put(&ios->kref, _last_io); + ret = 0; + + if (sync) { + wait_for_completion(&wait); + ret = ore_check_io(ios, NULL); + } + return ret; +} + +static void _clear_bio(struct bio *bio) +{ + struct bio_vec *bv; + unsigned i; + + __bio_for_each_segment(bv, bio, i, 0) { + unsigned this_count = bv->bv_len; + + if (likely(PAGE_SIZE == this_count)) + clear_highpage(bv->bv_page); + else + zero_user(bv->bv_page, bv->bv_offset, this_count); + } +} + +int ore_check_io(struct ore_io_state *ios, u64 *resid) +{ + enum osd_err_priority acumulated_osd_err = 0; + int acumulated_lin_err = 0; + int i; + + for (i = 0; i < ios->numdevs; i++) { + struct osd_sense_info osi; + struct osd_request *or = ios->per_dev[i].or; + int ret; + + if (unlikely(!or)) + continue; + + ret = osd_req_decode_sense(or, &osi); + if (likely(!ret)) + continue; + + if (OSD_ERR_PRI_CLEAR_PAGES == osi.osd_err_pri) { + /* start read offset passed endof file */ + _clear_bio(ios->per_dev[i].bio); + ORE_DBGMSG("start read offset passed end of file " + "offset=0x%llx, length=0x%llx\n", + _LLU(ios->per_dev[i].offset), + _LLU(ios->per_dev[i].length)); + + continue; /* we recovered */ + } + + if (osi.osd_err_pri >= acumulated_osd_err) { + acumulated_osd_err = osi.osd_err_pri; + acumulated_lin_err = ret; + } + } + + /* TODO: raid specific residual calculations */ + if (resid) { + if (likely(!acumulated_lin_err)) + *resid = 0; + else + *resid = ios->length; + } + + return acumulated_lin_err; +} + +/* + * L - logical offset into the file + * + * U - The number of bytes in a stripe within a group + * + * U = stripe_unit * group_width + * + * T - The number of bytes striped within a group of component objects + * (before advancing to the next group) + * + * T = stripe_unit * group_width * group_depth + * + * S - The number of bytes striped across all component objects + * before the pattern repeats + * + * S = stripe_unit * group_width * group_depth * group_count + * + * M - The "major" (i.e., across all components) stripe number + * + * M = L / S + * + * G - Counts the groups from the beginning of the major stripe + * + * G = (L - (M * S)) / T [or (L % S) / T] + * + * H - The byte offset within the group + * + * H = (L - (M * S)) % T [or (L % S) % T] + * + * N - The "minor" (i.e., across the group) stripe number + * + * N = H / U + * + * C - The component index coresponding to L + * + * C = (H - (N * U)) / stripe_unit + G * group_width + * [or (L % U) / stripe_unit + G * group_width] + * + * O - The component offset coresponding to L + * + * O = L % stripe_unit + N * stripe_unit + M * group_depth * stripe_unit + */ +struct _striping_info { + u64 obj_offset; + u64 group_length; + u64 M; /* for truncate */ + unsigned dev; + unsigned unit_off; +}; + +static void _calc_stripe_info(struct ore_layout *layout, u64 file_offset, + struct _striping_info *si) +{ + u32 stripe_unit = layout->stripe_unit; + u32 group_width = layout->group_width; + u64 group_depth = layout->group_depth; + + u32 U = stripe_unit * group_width; + u64 T = U * group_depth; + u64 S = T * layout->group_count; + u64 M = div64_u64(file_offset, S); + + /* + G = (L - (M * S)) / T + H = (L - (M * S)) % T + */ + u64 LmodS = file_offset - M * S; + u32 G = div64_u64(LmodS, T); + u64 H = LmodS - G * T; + + u32 N = div_u64(H, U); + + /* "H - (N * U)" is just "H % U" so it's bound to u32 */ + si->dev = (u32)(H - (N * U)) / stripe_unit + G * group_width; + si->dev *= layout->mirrors_p1; + + div_u64_rem(file_offset, stripe_unit, &si->unit_off); + + si->obj_offset = si->unit_off + (N * stripe_unit) + + (M * group_depth * stripe_unit); + + si->group_length = T - H; + si->M = M; +} + +static int _add_stripe_unit(struct ore_io_state *ios, unsigned *cur_pg, + unsigned pgbase, struct ore_per_dev_state *per_dev, + int cur_len) +{ + unsigned pg = *cur_pg; + struct request_queue *q = + osd_request_queue(_ios_od(ios, per_dev->dev)); + + per_dev->length += cur_len; + + if (per_dev->bio == NULL) { + unsigned pages_in_stripe = ios->layout->group_width * + (ios->layout->stripe_unit / PAGE_SIZE); + unsigned bio_size = (ios->nr_pages + pages_in_stripe) / + ios->layout->group_width; + + per_dev->bio = bio_kmalloc(GFP_KERNEL, bio_size); + if (unlikely(!per_dev->bio)) { + ORE_DBGMSG("Failed to allocate BIO size=%u\n", + bio_size); + return -ENOMEM; + } + } + + while (cur_len > 0) { + unsigned pglen = min_t(unsigned, PAGE_SIZE - pgbase, cur_len); + unsigned added_len; + + BUG_ON(ios->nr_pages <= pg); + cur_len -= pglen; + + added_len = bio_add_pc_page(q, per_dev->bio, ios->pages[pg], + pglen, pgbase); + if (unlikely(pglen != added_len)) + return -ENOMEM; + pgbase = 0; + ++pg; + } + BUG_ON(cur_len); + + *cur_pg = pg; + return 0; +} + +static int _prepare_one_group(struct ore_io_state *ios, u64 length, + struct _striping_info *si) +{ + unsigned stripe_unit = ios->layout->stripe_unit; + unsigned mirrors_p1 = ios->layout->mirrors_p1; + unsigned devs_in_group = ios->layout->group_width * mirrors_p1; + unsigned dev = si->dev; + unsigned first_dev = dev - (dev % devs_in_group); + unsigned max_comp = ios->numdevs ? ios->numdevs - mirrors_p1 : 0; + unsigned cur_pg = ios->pages_consumed; + int ret = 0; + + while (length) { + struct ore_per_dev_state *per_dev = &ios->per_dev[dev]; + unsigned cur_len, page_off = 0; + + if (!per_dev->length) { + per_dev->dev = dev; + if (dev < si->dev) { + per_dev->offset = si->obj_offset + stripe_unit - + si->unit_off; + cur_len = stripe_unit; + } else if (dev == si->dev) { + per_dev->offset = si->obj_offset; + cur_len = stripe_unit - si->unit_off; + page_off = si->unit_off & ~PAGE_MASK; + BUG_ON(page_off && (page_off != ios->pgbase)); + } else { /* dev > si->dev */ + per_dev->offset = si->obj_offset - si->unit_off; + cur_len = stripe_unit; + } + + if (max_comp < dev) + max_comp = dev; + } else { + cur_len = stripe_unit; + } + if (cur_len >= length) + cur_len = length; + + ret = _add_stripe_unit(ios, &cur_pg, page_off , per_dev, + cur_len); + if (unlikely(ret)) + goto out; + + dev += mirrors_p1; + dev = (dev % devs_in_group) + first_dev; + + length -= cur_len; + } +out: + ios->numdevs = max_comp + mirrors_p1; + ios->pages_consumed = cur_pg; + return ret; +} + +static int _prepare_for_striping(struct ore_io_state *ios) +{ + u64 length = ios->length; + u64 offset = ios->offset; + struct _striping_info si; + int ret = 0; + + if (!ios->pages) { + if (ios->kern_buff) { + struct ore_per_dev_state *per_dev = &ios->per_dev[0]; + + _calc_stripe_info(ios->layout, ios->offset, &si); + per_dev->offset = si.obj_offset; + per_dev->dev = si.dev; + + /* no cross device without page array */ + BUG_ON((ios->layout->group_width > 1) && + (si.unit_off + ios->length > + ios->layout->stripe_unit)); + } + ios->numdevs = ios->layout->mirrors_p1; + return 0; + } + + while (length) { + _calc_stripe_info(ios->layout, offset, &si); + + if (length < si.group_length) + si.group_length = length; + + ret = _prepare_one_group(ios, si.group_length, &si); + if (unlikely(ret)) + goto out; + + offset += si.group_length; + length -= si.group_length; + } + +out: + return ret; +} + +int ore_create(struct ore_io_state *ios) +{ + int i, ret; + + for (i = 0; i < ios->comps->numdevs; i++) { + struct osd_request *or; + + or = osd_start_request(_ios_od(ios, i), GFP_KERNEL); + if (unlikely(!or)) { + ORE_ERR("%s: osd_start_request failed\n", __func__); + ret = -ENOMEM; + goto out; + } + ios->per_dev[i].or = or; + ios->numdevs++; + + osd_req_create_object(or, _ios_obj(ios, i)); + } + ret = ore_io_execute(ios); + +out: + return ret; +} + +int ore_remove(struct ore_io_state *ios) +{ + int i, ret; + + for (i = 0; i < ios->comps->numdevs; i++) { + struct osd_request *or; + + or = osd_start_request(_ios_od(ios, i), GFP_KERNEL); + if (unlikely(!or)) { + ORE_ERR("%s: osd_start_request failed\n", __func__); + ret = -ENOMEM; + goto out; + } + ios->per_dev[i].or = or; + ios->numdevs++; + + osd_req_remove_object(or, _ios_obj(ios, i)); + } + ret = ore_io_execute(ios); + +out: + return ret; +} + +static int _write_mirror(struct ore_io_state *ios, int cur_comp) +{ + struct ore_per_dev_state *master_dev = &ios->per_dev[cur_comp]; + unsigned dev = ios->per_dev[cur_comp].dev; + unsigned last_comp = cur_comp + ios->layout->mirrors_p1; + int ret = 0; + + if (ios->pages && !master_dev->length) + return 0; /* Just an empty slot */ + + for (; cur_comp < last_comp; ++cur_comp, ++dev) { + struct ore_per_dev_state *per_dev = &ios->per_dev[cur_comp]; + struct osd_request *or; + + or = osd_start_request(_ios_od(ios, dev), GFP_KERNEL); + if (unlikely(!or)) { + ORE_ERR("%s: osd_start_request failed\n", __func__); + ret = -ENOMEM; + goto out; + } + per_dev->or = or; + per_dev->offset = master_dev->offset; + + if (ios->pages) { + struct bio *bio; + + if (per_dev != master_dev) { + bio = bio_kmalloc(GFP_KERNEL, + master_dev->bio->bi_max_vecs); + if (unlikely(!bio)) { + ORE_DBGMSG( + "Failed to allocate BIO size=%u\n", + master_dev->bio->bi_max_vecs); + ret = -ENOMEM; + goto out; + } + + __bio_clone(bio, master_dev->bio); + bio->bi_bdev = NULL; + bio->bi_next = NULL; + per_dev->length = master_dev->length; + per_dev->bio = bio; + per_dev->dev = dev; + } else { + bio = master_dev->bio; + /* FIXME: bio_set_dir() */ + bio->bi_rw |= REQ_WRITE; + } + + osd_req_write(or, _ios_obj(ios, dev), per_dev->offset, + bio, per_dev->length); + ORE_DBGMSG("write(0x%llx) offset=0x%llx " + "length=0x%llx dev=%d\n", + _LLU(_ios_obj(ios, dev)->id), + _LLU(per_dev->offset), + _LLU(per_dev->length), dev); + } else if (ios->kern_buff) { + ret = osd_req_write_kern(or, _ios_obj(ios, dev), + per_dev->offset, + ios->kern_buff, ios->length); + if (unlikely(ret)) + goto out; + ORE_DBGMSG2("write_kern(0x%llx) offset=0x%llx " + "length=0x%llx dev=%d\n", + _LLU(_ios_obj(ios, dev)->id), + _LLU(per_dev->offset), + _LLU(ios->length), dev); + } else { + osd_req_set_attributes(or, _ios_obj(ios, dev)); + ORE_DBGMSG2("obj(0x%llx) set_attributes=%d dev=%d\n", + _LLU(_ios_obj(ios, dev)->id), + ios->out_attr_len, dev); + } + + if (ios->out_attr) + osd_req_add_set_attr_list(or, ios->out_attr, + ios->out_attr_len); + + if (ios->in_attr) + osd_req_add_get_attr_list(or, ios->in_attr, + ios->in_attr_len); + } + +out: + return ret; +} + +int ore_write(struct ore_io_state *ios) +{ + int i; + int ret; + + ret = _prepare_for_striping(ios); + if (unlikely(ret)) + return ret; + + for (i = 0; i < ios->numdevs; i += ios->layout->mirrors_p1) { + ret = _write_mirror(ios, i); + if (unlikely(ret)) + return ret; + } + + ret = ore_io_execute(ios); + return ret; +} + +static int _read_mirror(struct ore_io_state *ios, unsigned cur_comp) +{ + struct osd_request *or; + struct ore_per_dev_state *per_dev = &ios->per_dev[cur_comp]; + struct osd_obj_id *obj = _ios_obj(ios, cur_comp); + unsigned first_dev = (unsigned)obj->id; + + if (ios->pages && !per_dev->length) + return 0; /* Just an empty slot */ + + first_dev = per_dev->dev + first_dev % ios->layout->mirrors_p1; + or = osd_start_request(_ios_od(ios, first_dev), GFP_KERNEL); + if (unlikely(!or)) { + ORE_ERR("%s: osd_start_request failed\n", __func__); + return -ENOMEM; + } + per_dev->or = or; + + if (ios->pages) { + osd_req_read(or, obj, per_dev->offset, + per_dev->bio, per_dev->length); + ORE_DBGMSG("read(0x%llx) offset=0x%llx length=0x%llx" + " dev=%d\n", _LLU(obj->id), + _LLU(per_dev->offset), _LLU(per_dev->length), + first_dev); + } else if (ios->kern_buff) { + int ret = osd_req_read_kern(or, obj, per_dev->offset, + ios->kern_buff, ios->length); + ORE_DBGMSG2("read_kern(0x%llx) offset=0x%llx " + "length=0x%llx dev=%d ret=>%d\n", + _LLU(obj->id), _LLU(per_dev->offset), + _LLU(ios->length), first_dev, ret); + if (unlikely(ret)) + return ret; + } else { + osd_req_get_attributes(or, obj); + ORE_DBGMSG2("obj(0x%llx) get_attributes=%d dev=%d\n", + _LLU(obj->id), + ios->in_attr_len, first_dev); + } + if (ios->out_attr) + osd_req_add_set_attr_list(or, ios->out_attr, ios->out_attr_len); + + if (ios->in_attr) + osd_req_add_get_attr_list(or, ios->in_attr, ios->in_attr_len); + + return 0; +} + +int ore_read(struct ore_io_state *ios) +{ + int i; + int ret; + + ret = _prepare_for_striping(ios); + if (unlikely(ret)) + return ret; + + for (i = 0; i < ios->numdevs; i += ios->layout->mirrors_p1) { + ret = _read_mirror(ios, i); + if (unlikely(ret)) + return ret; + } + + ret = ore_io_execute(ios); + return ret; +} + +int extract_attr_from_ios(struct ore_io_state *ios, struct osd_attr *attr) +{ + struct osd_attr cur_attr = {.attr_page = 0}; /* start with zeros */ + void *iter = NULL; + int nelem; + + do { + nelem = 1; + osd_req_decode_get_attr_list(ios->per_dev[0].or, + &cur_attr, &nelem, &iter); + if ((cur_attr.attr_page == attr->attr_page) && + (cur_attr.attr_id == attr->attr_id)) { + attr->len = cur_attr.len; + attr->val_ptr = cur_attr.val_ptr; + return 0; + } + } while (iter); + + return -EIO; +} + +static int _truncate_mirrors(struct ore_io_state *ios, unsigned cur_comp, + struct osd_attr *attr) +{ + int last_comp = cur_comp + ios->layout->mirrors_p1; + + for (; cur_comp < last_comp; ++cur_comp) { + struct ore_per_dev_state *per_dev = &ios->per_dev[cur_comp]; + struct osd_request *or; + + or = osd_start_request(_ios_od(ios, cur_comp), GFP_KERNEL); + if (unlikely(!or)) { + ORE_ERR("%s: osd_start_request failed\n", __func__); + return -ENOMEM; + } + per_dev->or = or; + + osd_req_set_attributes(or, _ios_obj(ios, cur_comp)); + osd_req_add_set_attr_list(or, attr, 1); + } + + return 0; +} + +struct _trunc_info { + struct _striping_info si; + u64 prev_group_obj_off; + u64 next_group_obj_off; + + unsigned first_group_dev; + unsigned nex_group_dev; + unsigned max_devs; +}; + +void _calc_trunk_info(struct ore_layout *layout, u64 file_offset, + struct _trunc_info *ti) +{ + unsigned stripe_unit = layout->stripe_unit; + + _calc_stripe_info(layout, file_offset, &ti->si); + + ti->prev_group_obj_off = ti->si.M * stripe_unit; + ti->next_group_obj_off = ti->si.M ? (ti->si.M - 1) * stripe_unit : 0; + + ti->first_group_dev = ti->si.dev - (ti->si.dev % layout->group_width); + ti->nex_group_dev = ti->first_group_dev + layout->group_width; + ti->max_devs = layout->group_width * layout->group_count; +} + +int ore_truncate(struct ore_layout *layout, struct ore_components *comps, + u64 size) +{ + struct ore_io_state *ios; + struct exofs_trunc_attr { + struct osd_attr attr; + __be64 newsize; + } *size_attrs; + struct _trunc_info ti; + int i, ret; + + ret = ore_get_io_state(layout, comps, &ios); + if (unlikely(ret)) + return ret; + + _calc_trunk_info(ios->layout, size, &ti); + + size_attrs = kcalloc(ti.max_devs, sizeof(*size_attrs), + GFP_KERNEL); + if (unlikely(!size_attrs)) { + ret = -ENOMEM; + goto out; + } + + ios->numdevs = ios->comps->numdevs; + + for (i = 0; i < ti.max_devs; ++i) { + struct exofs_trunc_attr *size_attr = &size_attrs[i]; + u64 obj_size; + + if (i < ti.first_group_dev) + obj_size = ti.prev_group_obj_off; + else if (i >= ti.nex_group_dev) + obj_size = ti.next_group_obj_off; + else if (i < ti.si.dev) /* dev within this group */ + obj_size = ti.si.obj_offset + + ios->layout->stripe_unit - ti.si.unit_off; + else if (i == ti.si.dev) + obj_size = ti.si.obj_offset; + else /* i > ti.dev */ + obj_size = ti.si.obj_offset - ti.si.unit_off; + + size_attr->newsize = cpu_to_be64(obj_size); + size_attr->attr = g_attr_logical_length; + size_attr->attr.val_ptr = &size_attr->newsize; + + ORE_DBGMSG("trunc(0x%llx) obj_offset=0x%llx dev=%d\n", + _LLU(comps->comps->obj.id), _LLU(obj_size), i); + ret = _truncate_mirrors(ios, i * ios->layout->mirrors_p1, + &size_attr->attr); + if (unlikely(ret)) + goto out; + } + ret = ore_io_execute(ios); + +out: + kfree(size_attrs); + ore_put_io_state(ios); + return ret; +} + +const struct osd_attr g_attr_logical_length = ATTR_DEF( + OSD_APAGE_OBJECT_INFORMATION, OSD_ATTR_OI_LOGICAL_LENGTH, 8); diff --git a/fs/exofs/super.c b/fs/exofs/super.c index 4d6877967c87..274894053b02 100644 --- a/fs/exofs/super.c +++ b/fs/exofs/super.c @@ -263,19 +263,19 @@ static int __sbi_read_stats(struct exofs_sb_info *sbi) struct osd_attr attrs[] = { [0] = g_attr_sb_stats, }; - struct exofs_io_state *ios; + struct ore_io_state *ios; int ret; - ret = exofs_get_io_state(&sbi->layout, &sbi->comps, &ios); + ret = ore_get_io_state(&sbi->layout, &sbi->comps, &ios); if (unlikely(ret)) { - EXOFS_ERR("%s: exofs_get_io_state failed.\n", __func__); + EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__); return ret; } ios->in_attr = attrs; ios->in_attr_len = ARRAY_SIZE(attrs); - ret = exofs_sbi_read(ios); + ret = ore_read(ios); if (unlikely(ret)) { EXOFS_ERR("Error reading super_block stats => %d\n", ret); goto out; @@ -302,13 +302,13 @@ static int __sbi_read_stats(struct exofs_sb_info *sbi) } out: - exofs_put_io_state(ios); + ore_put_io_state(ios); return ret; } -static void stats_done(struct exofs_io_state *ios, void *p) +static void stats_done(struct ore_io_state *ios, void *p) { - exofs_put_io_state(ios); + ore_put_io_state(ios); /* Good thanks nothing to do anymore */ } @@ -318,12 +318,12 @@ int exofs_sbi_write_stats(struct exofs_sb_info *sbi) struct osd_attr attrs[] = { [0] = g_attr_sb_stats, }; - struct exofs_io_state *ios; + struct ore_io_state *ios; int ret; - ret = exofs_get_io_state(&sbi->layout, &sbi->comps, &ios); + ret = ore_get_io_state(&sbi->layout, &sbi->comps, &ios); if (unlikely(ret)) { - EXOFS_ERR("%s: exofs_get_io_state failed.\n", __func__); + EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__); return ret; } @@ -337,10 +337,10 @@ int exofs_sbi_write_stats(struct exofs_sb_info *sbi) ios->out_attr = attrs; ios->out_attr_len = ARRAY_SIZE(attrs); - ret = exofs_sbi_write(ios); + ret = ore_write(ios); if (unlikely(ret)) { - EXOFS_ERR("%s: exofs_sbi_write failed.\n", __func__); - exofs_put_io_state(ios); + EXOFS_ERR("%s: ore_write failed.\n", __func__); + ore_put_io_state(ios); } return ret; @@ -359,9 +359,9 @@ int exofs_sync_fs(struct super_block *sb, int wait) { struct exofs_sb_info *sbi; struct exofs_fscb *fscb; - struct exofs_comp one_comp; - struct exofs_components comps; - struct exofs_io_state *ios; + struct ore_comp one_comp; + struct ore_components comps; + struct ore_io_state *ios; int ret = -ENOMEM; fscb = kmalloc(sizeof(*fscb), GFP_KERNEL); @@ -380,7 +380,7 @@ int exofs_sync_fs(struct super_block *sb, int wait) exofs_init_comps(&comps, &one_comp, sbi, EXOFS_SUPER_ID); - ret = exofs_get_io_state(&sbi->layout, &comps, &ios); + ret = ore_get_io_state(&sbi->layout, &comps, &ios); if (unlikely(ret)) goto out; @@ -397,9 +397,9 @@ int exofs_sync_fs(struct super_block *sb, int wait) ios->offset = 0; ios->kern_buff = fscb; - ret = exofs_sbi_write(ios); + ret = ore_write(ios); if (unlikely(ret)) - EXOFS_ERR("%s: exofs_sbi_write failed.\n", __func__); + EXOFS_ERR("%s: ore_write failed.\n", __func__); else sb->s_dirt = 0; @@ -407,7 +407,7 @@ int exofs_sync_fs(struct super_block *sb, int wait) unlock_super(sb); out: EXOFS_DBGMSG("s_nextid=0x%llx ret=%d\n", _LLU(sbi->s_nextid), ret); - exofs_put_io_state(ios); + ore_put_io_state(ios); kfree(fscb); return ret; } @@ -562,7 +562,7 @@ static int _read_and_match_data_map(struct exofs_sb_info *sbi, unsigned numdevs, return 0; } -static unsigned __ra_pages(struct exofs_layout *layout) +static unsigned __ra_pages(struct ore_layout *layout) { const unsigned _MIN_RA = 32; /* min 128K read-ahead */ unsigned ra_pages = layout->group_width * layout->stripe_unit / @@ -609,7 +609,7 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info *sbi, struct osd_dev *fscb_od, unsigned table_count) { - struct exofs_comp comp; + struct ore_comp comp; struct exofs_device_table *dt; unsigned table_bytes = table_count * sizeof(dt->dt_dev_table[0]) + sizeof(*dt); @@ -747,7 +747,7 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent) struct exofs_sb_info *sbi; /*extended info */ struct osd_dev *od; /* Master device */ struct exofs_fscb fscb; /*on-disk superblock info */ - struct exofs_comp comp; + struct ore_comp comp; unsigned table_count; int ret; @@ -913,7 +913,7 @@ static int exofs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; struct exofs_sb_info *sbi = sb->s_fs_info; - struct exofs_io_state *ios; + struct ore_io_state *ios; struct osd_attr attrs[] = { ATTR_DEF(OSD_APAGE_PARTITION_QUOTAS, OSD_ATTR_PQ_CAPACITY_QUOTA, sizeof(__be64)), @@ -924,16 +924,16 @@ static int exofs_statfs(struct dentry *dentry, struct kstatfs *buf) uint64_t used = ULLONG_MAX; int ret; - ret = exofs_get_io_state(&sbi->layout, &sbi->comps, &ios); + ret = ore_get_io_state(&sbi->layout, &sbi->comps, &ios); if (ret) { - EXOFS_DBGMSG("exofs_get_io_state failed.\n"); + EXOFS_DBGMSG("ore_get_io_state failed.\n"); return ret; } ios->in_attr = attrs; ios->in_attr_len = ARRAY_SIZE(attrs); - ret = exofs_sbi_read(ios); + ret = ore_read(ios); if (unlikely(ret)) goto out; @@ -962,7 +962,7 @@ static int exofs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_namelen = EXOFS_NAME_LEN; out: - exofs_put_io_state(ios); + ore_put_io_state(ios); return ret; } diff --git a/include/scsi/osd_ore.h b/include/scsi/osd_ore.h new file mode 100644 index 000000000000..c5c5e008e6de --- /dev/null +++ b/include/scsi/osd_ore.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2011 + * Boaz Harrosh + * + * Public Declarations of the ORE API + * + * This file is part of the ORE (Object Raid Engine) library. + * + * ORE 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. (GPL v2) + * + * ORE 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 the ORE; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __ORE_H__ +#define __ORE_H__ + +#include +#include +#include +#include + +struct ore_comp { + struct osd_obj_id obj; + u8 cred[OSD_CAP_LEN]; +}; + +struct ore_layout { + /* Our way of looking at the data_map */ + unsigned stripe_unit; + unsigned mirrors_p1; + + unsigned group_width; + u64 group_depth; + unsigned group_count; +}; + +struct ore_components { + unsigned numdevs; /* Num of devices in array */ + /* If @single_comp == EC_SINGLE_COMP, @comps points to a single + * component. else there are @numdevs components + */ + enum EC_COMP_USAGE { + EC_SINGLE_COMP = 0, EC_MULTPLE_COMPS = 0xffffffff + } single_comp; + struct ore_comp *comps; + struct osd_dev **ods; /* osd_dev array */ +}; + +struct ore_io_state; +typedef void (*ore_io_done_fn)(struct ore_io_state *ios, void *private); + +struct ore_io_state { + struct kref kref; + + void *private; + ore_io_done_fn done; + + struct ore_layout *layout; + struct ore_components *comps; + + /* Global read/write IO*/ + loff_t offset; + unsigned long length; + void *kern_buff; + + struct page **pages; + unsigned nr_pages; + unsigned pgbase; + unsigned pages_consumed; + + /* Attributes */ + unsigned in_attr_len; + struct osd_attr *in_attr; + unsigned out_attr_len; + struct osd_attr *out_attr; + + bool reading; + + /* Variable array of size numdevs */ + unsigned numdevs; + struct ore_per_dev_state { + struct osd_request *or; + struct bio *bio; + loff_t offset; + unsigned length; + unsigned dev; + } per_dev[]; +}; + +static inline unsigned ore_io_state_size(unsigned numdevs) +{ + return sizeof(struct ore_io_state) + + sizeof(struct ore_per_dev_state) * numdevs; +} + +/* ore.c */ +int ore_get_rw_state(struct ore_layout *layout, struct ore_components *comps, + bool is_reading, u64 offset, u64 length, + struct ore_io_state **ios); +int ore_get_io_state(struct ore_layout *layout, struct ore_components *comps, + struct ore_io_state **ios); +void ore_put_io_state(struct ore_io_state *ios); + +int ore_check_io(struct ore_io_state *ios, u64 *resid); + +int ore_create(struct ore_io_state *ios); +int ore_remove(struct ore_io_state *ios); +int ore_write(struct ore_io_state *ios); +int ore_read(struct ore_io_state *ios); +int ore_truncate(struct ore_layout *layout, struct ore_components *comps, + u64 size); + +int extract_attr_from_ios(struct ore_io_state *ios, struct osd_attr *attr); + +extern const struct osd_attr g_attr_logical_length; + +#endif -- cgit v1.2.3 From 830c0f0edca67403d361fe976a25b17356c11f19 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 6 Aug 2011 22:41:50 -0700 Subject: vfs: renumber DCACHE_xyz flags, remove some stale ones Gcc tends to generate better code with small integers, including the DCACHE_xyz flag tests - so move the common ones to be first in the list. Also just remove the unused DCACHE_INOTIFY_PARENT_WATCHED and DCACHE_AUTOFS_PENDING values, their users no longer exists in the source tree. And add a "unlikely()" to the DCACHE_OP_COMPARE test, since we want the common case to be a nice straight-line fall-through. Signed-off-by: Linus Torvalds --- fs/dcache.c | 2 +- include/linux/dcache.h | 30 +++++++++++++----------------- 2 files changed, 14 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/fs/dcache.c b/fs/dcache.c index c83cae19161e..a88948b8bd17 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1729,7 +1729,7 @@ seqretry: */ if (read_seqcount_retry(&dentry->d_seq, *seq)) goto seqretry; - if (parent->d_flags & DCACHE_OP_COMPARE) { + if (unlikely(parent->d_flags & DCACHE_OP_COMPARE)) { if (parent->d_op->d_compare(parent, *inode, dentry, i, tlen, tname, name)) diff --git a/include/linux/dcache.h b/include/linux/dcache.h index d37d2a793099..62157c03caf7 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -180,12 +180,12 @@ struct dentry_operations { */ /* d_flags entries */ -#define DCACHE_AUTOFS_PENDING 0x0001 /* autofs: "under construction" */ -#define DCACHE_NFSFS_RENAMED 0x0002 - /* this dentry has been "silly renamed" and has to be deleted on the last - * dput() */ +#define DCACHE_OP_HASH 0x0001 +#define DCACHE_OP_COMPARE 0x0002 +#define DCACHE_OP_REVALIDATE 0x0004 +#define DCACHE_OP_DELETE 0x0008 -#define DCACHE_DISCONNECTED 0x0004 +#define DCACHE_DISCONNECTED 0x0010 /* This dentry is possibly not currently connected to the dcache tree, in * which case its parent will either be itself, or will have this flag as * well. nfsd will not use a dentry with this bit set, but will first @@ -196,22 +196,18 @@ struct dentry_operations { * dentry into place and return that dentry rather than the passed one, * typically using d_splice_alias. */ -#define DCACHE_REFERENCED 0x0008 /* Recently used, don't discard. */ -#define DCACHE_RCUACCESS 0x0010 /* Entry has ever been RCU-visible */ -#define DCACHE_INOTIFY_PARENT_WATCHED 0x0020 - /* Parent inode is watched by inotify */ - -#define DCACHE_COOKIE 0x0040 /* For use by dcookie subsystem */ -#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080 - /* Parent inode is watched by some fsnotify listener */ +#define DCACHE_REFERENCED 0x0020 /* Recently used, don't discard. */ +#define DCACHE_RCUACCESS 0x0040 /* Entry has ever been RCU-visible */ #define DCACHE_CANT_MOUNT 0x0100 #define DCACHE_GENOCIDE 0x0200 -#define DCACHE_OP_HASH 0x1000 -#define DCACHE_OP_COMPARE 0x2000 -#define DCACHE_OP_REVALIDATE 0x4000 -#define DCACHE_OP_DELETE 0x8000 +#define DCACHE_NFSFS_RENAMED 0x1000 + /* this dentry has been "silly renamed" and has to be deleted on the last + * dput() */ +#define DCACHE_COOKIE 0x2000 /* For use by dcookie subsystem */ +#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x4000 + /* Parent inode is watched by some fsnotify listener */ #define DCACHE_MOUNTED 0x10000 /* is a mountpoint */ #define DCACHE_NEED_AUTOMOUNT 0x20000 /* handle automount on this dir */ -- cgit v1.2.3 From 3ddcd0569cd68f00f3beae9a7959b72918bb91f4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 6 Aug 2011 22:45:50 -0700 Subject: vfs: optimize inode cache access patterns The inode structure layout is largely random, and some of the vfs paths really do care. The path lookup in particular is already quite D$ intensive, and profiles show that accessing the 'inode->i_op->xyz' fields is quite costly. We already optimized the dcache to not unnecessarily load the d_op structure for members that are often NULL using the DCACHE_OP_xyz bits in dentry->d_flags, and this does something very similar for the inode ops that are used during pathname lookup. It also re-orders the fields so that the fields accessed by 'stat' are together at the beginning of the inode structure, and roughly in the order accessed. The effect of this seems to be in the 1-2% range for an empty kernel "make -j" run (which is fairly kernel-intensive, mostly in filename lookup), so it's visible. The numbers are fairly noisy, though, and likely depend a lot on exact microarchitecture. So there's more tuning to be done. Signed-off-by: Linus Torvalds --- fs/inode.c | 1 + fs/namei.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++------- fs/stat.c | 4 +-- include/linux/fs.h | 59 ++++++++++++++++++++++++++---------------- 4 files changed, 106 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/fs/inode.c b/fs/inode.c index 5aab80dc008c..73920d555c88 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -143,6 +143,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode) inode->i_op = &empty_iops; inode->i_fop = &empty_fops; inode->i_nlink = 1; + inode->i_opflags = 0; inode->i_uid = 0; inode->i_gid = 0; atomic_set(&inode->i_writecount, 0); diff --git a/fs/namei.c b/fs/namei.c index 3d607bd80e09..4a98bf154d88 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -308,6 +308,26 @@ int generic_permission(struct inode *inode, int mask) return -EACCES; } +/* + * We _really_ want to just do "generic_permission()" without + * even looking at the inode->i_op values. So we keep a cache + * flag in inode->i_opflags, that says "this has not special + * permission function, use the fast case". + */ +static inline int do_inode_permission(struct inode *inode, int mask) +{ + if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) { + if (likely(inode->i_op->permission)) + return inode->i_op->permission(inode, mask); + + /* This gets set once for the inode lifetime */ + spin_lock(&inode->i_lock); + inode->i_opflags |= IOP_FASTPERM; + spin_unlock(&inode->i_lock); + } + return generic_permission(inode, mask); +} + /** * inode_permission - check for access rights to a given inode * @inode: inode to check permission on @@ -322,7 +342,7 @@ int inode_permission(struct inode *inode, int mask) { int retval; - if (mask & MAY_WRITE) { + if (unlikely(mask & MAY_WRITE)) { umode_t mode = inode->i_mode; /* @@ -339,11 +359,7 @@ int inode_permission(struct inode *inode, int mask) return -EACCES; } - if (inode->i_op->permission) - retval = inode->i_op->permission(inode, mask); - else - retval = generic_permission(inode, mask); - + retval = do_inode_permission(inode, mask); if (retval) return retval; @@ -1245,6 +1261,26 @@ static void terminate_walk(struct nameidata *nd) } } +/* + * Do we need to follow links? We _really_ want to be able + * to do this check without having to look at inode->i_op, + * so we keep a cache of "no, this doesn't need follow_link" + * for the common case. + */ +static inline int do_follow_link(struct inode *inode, int follow) +{ + if (unlikely(!(inode->i_opflags & IOP_NOFOLLOW))) { + if (likely(inode->i_op->follow_link)) + return follow; + + /* This gets set once for the inode lifetime */ + spin_lock(&inode->i_lock); + inode->i_opflags |= IOP_NOFOLLOW; + spin_unlock(&inode->i_lock); + } + return 0; +} + static inline int walk_component(struct nameidata *nd, struct path *path, struct qstr *name, int type, int follow) { @@ -1267,7 +1303,7 @@ static inline int walk_component(struct nameidata *nd, struct path *path, terminate_walk(nd); return -ENOENT; } - if (unlikely(inode->i_op->follow_link) && follow) { + if (do_follow_link(inode, follow)) { if (nd->flags & LOOKUP_RCU) { if (unlikely(unlazy_walk(nd, path->dentry))) { terminate_walk(nd); @@ -1319,6 +1355,26 @@ static inline int nested_symlink(struct path *path, struct nameidata *nd) return res; } +/* + * We really don't want to look at inode->i_op->lookup + * when we don't have to. So we keep a cache bit in + * the inode ->i_opflags field that says "yes, we can + * do lookup on this inode". + */ +static inline int can_lookup(struct inode *inode) +{ + if (likely(inode->i_opflags & IOP_LOOKUP)) + return 1; + if (likely(!inode->i_op->lookup)) + return 0; + + /* We do this once for the lifetime of the inode */ + spin_lock(&inode->i_lock); + inode->i_opflags |= IOP_LOOKUP; + spin_unlock(&inode->i_lock); + return 1; +} + /* * Name resolution. * This is the basic name resolution function, turning a pathname into @@ -1398,10 +1454,10 @@ static int link_path_walk(const char *name, struct nameidata *nd) if (err) return err; } + if (can_lookup(nd->inode)) + continue; err = -ENOTDIR; - if (!nd->inode->i_op->lookup) - break; - continue; + break; /* here ends the main loop */ last_component: diff --git a/fs/stat.c b/fs/stat.c index 961039121cb8..ba5316ffac61 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -27,12 +27,12 @@ void generic_fillattr(struct inode *inode, struct kstat *stat) stat->uid = inode->i_uid; stat->gid = inode->i_gid; stat->rdev = inode->i_rdev; + stat->size = i_size_read(inode); stat->atime = inode->i_atime; stat->mtime = inode->i_mtime; stat->ctime = inode->i_ctime; - stat->size = i_size_read(inode); - stat->blocks = inode->i_blocks; stat->blksize = (1 << inode->i_blkbits); + stat->blocks = inode->i_blocks; } EXPORT_SYMBOL(generic_fillattr); diff --git a/include/linux/fs.h b/include/linux/fs.h index 786b3b1113cf..178cdb4f1d4a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -738,22 +738,54 @@ static inline int mapping_writably_mapped(struct address_space *mapping) struct posix_acl; #define ACL_NOT_CACHED ((void *)(-1)) +#define IOP_FASTPERM 0x0001 +#define IOP_LOOKUP 0x0002 +#define IOP_NOFOLLOW 0x0004 + +/* + * Keep mostly read-only and often accessed (especially for + * the RCU path lookup and 'stat' data) fields at the beginning + * of the 'struct inode' + */ struct inode { - /* RCU path lookup touches following: */ umode_t i_mode; + unsigned short i_opflags; uid_t i_uid; gid_t i_gid; + unsigned int i_flags; + +#ifdef CONFIG_FS_POSIX_ACL + struct posix_acl *i_acl; + struct posix_acl *i_default_acl; +#endif + const struct inode_operations *i_op; struct super_block *i_sb; + struct address_space *i_mapping; - spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ - unsigned int i_flags; - unsigned long i_state; #ifdef CONFIG_SECURITY void *i_security; #endif - struct mutex i_mutex; + /* Stat data, not accessed from path walking */ + unsigned long i_ino; + unsigned int i_nlink; + dev_t i_rdev; + loff_t i_size; + struct timespec i_atime; + struct timespec i_mtime; + struct timespec i_ctime; + unsigned int i_blkbits; + blkcnt_t i_blocks; + +#ifdef __NEED_I_SIZE_ORDERED + seqcount_t i_size_seqcount; +#endif + + /* Misc */ + unsigned long i_state; + spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ + struct mutex i_mutex; unsigned long dirtied_when; /* jiffies of first dirtying */ @@ -765,25 +797,12 @@ struct inode { struct list_head i_dentry; struct rcu_head i_rcu; }; - unsigned long i_ino; atomic_t i_count; - unsigned int i_nlink; - dev_t i_rdev; - unsigned int i_blkbits; u64 i_version; - loff_t i_size; -#ifdef __NEED_I_SIZE_ORDERED - seqcount_t i_size_seqcount; -#endif - struct timespec i_atime; - struct timespec i_mtime; - struct timespec i_ctime; - blkcnt_t i_blocks; unsigned short i_bytes; atomic_t i_dio_count; const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ struct file_lock *i_flock; - struct address_space *i_mapping; struct address_space i_data; #ifdef CONFIG_QUOTA struct dquot *i_dquot[MAXQUOTAS]; @@ -806,10 +825,6 @@ struct inode { atomic_t i_readcount; /* struct files open RO */ #endif atomic_t i_writecount; -#ifdef CONFIG_FS_POSIX_ACL - struct posix_acl *i_acl; - struct posix_acl *i_default_acl; -#endif void *i_private; /* fs or device private pointer */ }; -- cgit v1.2.3 From 3295514841c2112d94451ba5deaf54f5afb78ea9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 7 Aug 2011 18:55:11 +0100 Subject: fix rcu annotations noise in cred.h task->cred is declared as __rcu, and access to other tasks' ->cred is, indeed, protected. Access to current->cred does not need rcu_dereference() at all, since only the task itself can change its ->cred. sparse, of course, has no way of knowing that... Add force-cast in current_cred(), make current_fsuid() et.al. use it. Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- include/linux/cred.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/cred.h b/include/linux/cred.h index 48e82af1159b..98f46efbe2d2 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -265,10 +265,11 @@ static inline void put_cred(const struct cred *_cred) /** * current_cred - Access the current task's subjective credentials * - * Access the subjective credentials of the current task. + * Access the subjective credentials of the current task. RCU-safe, + * since nobody else can modify it. */ #define current_cred() \ - (current->cred) + (*(__force struct cred **)¤t->cred) /** * __task_cred - Access a task's objective credentials @@ -307,7 +308,7 @@ static inline void put_cred(const struct cred *_cred) ({ \ struct user_struct *__u; \ struct cred *__cred; \ - __cred = (struct cred *) current_cred(); \ + __cred = current_cred(); \ __u = get_uid(__cred->user); \ __u; \ }) @@ -322,7 +323,7 @@ static inline void put_cred(const struct cred *_cred) ({ \ struct group_info *__groups; \ struct cred *__cred; \ - __cred = (struct cred *) current_cred(); \ + __cred = current_cred(); \ __groups = get_group_info(__cred->group_info); \ __groups; \ }) @@ -341,7 +342,7 @@ static inline void put_cred(const struct cred *_cred) #define current_cred_xxx(xxx) \ ({ \ - current->cred->xxx; \ + current_cred()->xxx; \ }) #define current_uid() (current_cred_xxx(uid)) -- cgit v1.2.3