summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/futex.c54
-rw-r--r--kernel/irq/Makefile2
-rw-r--r--kernel/irq/irqdomain.c215
-rw-r--r--kernel/power/consoleearlysuspend.c10
4 files changed, 263 insertions, 18 deletions
diff --git a/kernel/futex.c b/kernel/futex.c
index 0a308970c24..11cbe052b2e 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -218,6 +218,8 @@ static void drop_futex_key_refs(union futex_key *key)
* @uaddr: virtual address of the futex
* @fshared: 0 for a PROCESS_PRIVATE futex, 1 for PROCESS_SHARED
* @key: address where result is stored.
+ * @rw: mapping needs to be read/write (values: VERIFY_READ,
+ * VERIFY_WRITE)
*
* Returns a negative error code or 0
* The key words are stored in *key on success.
@@ -229,12 +231,12 @@ static void drop_futex_key_refs(union futex_key *key)
* lock_page() might sleep, the caller should not hold a spinlock.
*/
static int
-get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
+get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw)
{
unsigned long address = (unsigned long)uaddr;
struct mm_struct *mm = current->mm;
struct page *page, *page_head;
- int err;
+ int err, ro = 0;
/*
* The futex address must be "naturally" aligned.
@@ -262,8 +264,18 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
again:
err = get_user_pages_fast(address, 1, 1, &page);
+ /*
+ * If write access is not required (eg. FUTEX_WAIT), try
+ * and get read-only access.
+ */
+ if (err == -EFAULT && rw == VERIFY_READ) {
+ err = get_user_pages_fast(address, 1, 0, &page);
+ ro = 1;
+ }
if (err < 0)
return err;
+ else
+ err = 0;
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
page_head = page;
@@ -305,6 +317,13 @@ again:
if (!page_head->mapping) {
unlock_page(page_head);
put_page(page_head);
+ /*
+ * ZERO_PAGE pages don't have a mapping. Avoid a busy loop
+ * trying to find one. RW mapping would have COW'd (and thus
+ * have a mapping) so this page is RO and won't ever change.
+ */
+ if ((page_head == ZERO_PAGE(address)))
+ return -EFAULT;
goto again;
}
@@ -316,6 +335,15 @@ again:
* the object not the particular process.
*/
if (PageAnon(page_head)) {
+ /*
+ * A RO anonymous page will never change and thus doesn't make
+ * sense for futex operations.
+ */
+ if (ro) {
+ err = -EFAULT;
+ goto out;
+ }
+
key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */
key->private.mm = mm;
key->private.address = address;
@@ -327,9 +355,10 @@ again:
get_futex_key_refs(key);
+out:
unlock_page(page_head);
put_page(page_head);
- return 0;
+ return err;
}
static inline void put_futex_key(union futex_key *key)
@@ -940,7 +969,7 @@ futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)
if (!bitset)
return -EINVAL;
- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key);
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key, VERIFY_READ);
if (unlikely(ret != 0))
goto out;
@@ -986,10 +1015,10 @@ futex_wake_op(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2,
int ret, op_ret;
retry:
- ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1);
+ ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, VERIFY_READ);
if (unlikely(ret != 0))
goto out;
- ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2);
+ ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE);
if (unlikely(ret != 0))
goto out_put_key1;
@@ -1243,10 +1272,11 @@ retry:
pi_state = NULL;
}
- ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1);
+ ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, VERIFY_READ);
if (unlikely(ret != 0))
goto out;
- ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2);
+ ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2,
+ requeue_pi ? VERIFY_WRITE : VERIFY_READ);
if (unlikely(ret != 0))
goto out_put_key1;
@@ -1790,7 +1820,7 @@ static int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
* while the syscall executes.
*/
retry:
- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key);
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key, VERIFY_READ);
if (unlikely(ret != 0))
return ret;
@@ -1941,7 +1971,7 @@ static int futex_lock_pi(u32 __user *uaddr, unsigned int flags, int detect,
}
retry:
- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q.key);
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q.key, VERIFY_WRITE);
if (unlikely(ret != 0))
goto out;
@@ -2060,7 +2090,7 @@ retry:
if ((uval & FUTEX_TID_MASK) != vpid)
return -EPERM;
- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key);
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key, VERIFY_WRITE);
if (unlikely(ret != 0))
goto out;
@@ -2249,7 +2279,7 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
debug_rt_mutex_init_waiter(&rt_waiter);
rt_waiter.task = NULL;
- ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2);
+ ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE);
if (unlikely(ret != 0))
goto out;
diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
index 73290056cfb..aa8feed23b0 100644
--- a/kernel/irq/Makefile
+++ b/kernel/irq/Makefile
@@ -1,5 +1,5 @@
-obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o
+obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o irqdomain.o
obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o
obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
obj-$(CONFIG_PROC_FS) += proc.o
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
new file mode 100644
index 00000000000..7e251cd810b
--- /dev/null
+++ b/kernel/irq/irqdomain.c
@@ -0,0 +1,215 @@
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+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)
+{
+ 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)
+{
+ mutex_lock(&irq_domain_mutex);
+ list_del(&domain->list);
+ mutex_unlock(&irq_domain_mutex);
+}
+
+/**
+ * irq_domain_map() - Allocate and/or increment a reference to a hwirq
+ *
+ * TODO: Establish a linux irq number mapping for a hardware irq. If the
+ * mapping already exists, then increment the reference count and return the
+ * linux irq number.
+ *
+ * At the moment this function is an empty stub since irq_domain initially
+ * only supports the common case of mapping hw irq numbers into a contiguous
+ * range of pre-allocated linux irq_descs based at irq_domain->irq_base. When
+ * irq_domains are extended either to support non-contiguous mappings (ie. to
+ * support MSI interrupts) or to remove preallocation of all irq_descs (as
+ * powerpc does so that irq_descs are only allocated for in-use irq inputs),
+ * then this function will be extended to implement the irq_desc allocation
+ * and reference counting.
+ *
+ * Any caller to this function must arrange to also call irq_domain_unmap()
+ * if the irq ever becomes unused again.
+ */
+unsigned int irq_domain_map(struct irq_domain *domain, irq_hw_number_t hwirq)
+{
+ int irq = irq_domain_to_irq(domain, hwirq);
+ struct irq_data *d = irq_get_irq_data(irq);
+
+ d->domain = domain;
+ d->hwirq = hwirq;
+
+ return irq;
+}
+
+/**
+ * irq_domain_unmap() - Release a reference to a hwirq
+ *
+ * TODO: decrement the reference count on a hardware irq number. If the ref
+ * count reaches zero, then the irq_desc can be freed.
+ *
+ * At the moment this function is an empty stub. See the comment on
+ * irq_domain_map() for details.
+ */
+void irq_domain_unmap(struct irq_domain *domain, irq_hw_number_t hwirq)
+{
+ int irq = irq_domain_to_irq(domain, hwirq);
+ struct irq_data *d = irq_get_irq_data(irq);
+
+ d->domain = NULL;
+}
+
+#if defined(CONFIG_OF_IRQ) && !defined(CONFIG_PPC)
+
+/*
+ * A handful of architectures use NO_IRQ, which must be used to avoid breaking
+ * things when using the device tree. This is intended to be temporary until
+ * the remaining NO_IRQ users can be removed
+ */
+#ifndef NO_IRQ
+#define NO_IRQ 0
+#endif
+
+/**
+ * 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;
+ irq_hw_number_t hwirq;
+ unsigned int irq, type;
+ int rc = -ENODEV;
+
+ /* 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 defined(CONFIG_MIPS) || defined(CONFIG_MICROBLAZE)
+ /*
+ * Temporary: preserves current behaviour until mips/microblaze
+ * register an irq_domain
+ */
+ if (rc != 0) {
+ pr_debug("%s: fallback mapping for irq=%i\n",
+ controller->full_name, irq);
+ return intspec[0];
+ }
+#else
+ if (rc != 0)
+ return NO_IRQ;
+#endif
+
+ irq = irq_domain_map(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)
+{
+ struct irq_data *d = irq_get_irq_data(irq);
+ irq_domain_unmap(d->domain, d->hwirq);
+}
+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,
+ irq_hw_number_t *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;
+ return 0;
+}
+
+struct irq_domain_ops irq_domain_simple_ops = {
+ .dt_translate = irq_domain_simple_dt_translate,
+};
+
+/**
+ * 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
diff --git a/kernel/power/consoleearlysuspend.c b/kernel/power/consoleearlysuspend.c
index a3edcb26738..3f0c8685fe1 100644
--- a/kernel/power/consoleearlysuspend.c
+++ b/kernel/power/consoleearlysuspend.c
@@ -25,28 +25,28 @@
static int orig_fgconsole;
static void console_early_suspend(struct early_suspend *h)
{
- acquire_console_sem();
+ console_lock();
orig_fgconsole = fg_console;
if (vc_allocate(EARLY_SUSPEND_CONSOLE))
goto err;
if (set_console(EARLY_SUSPEND_CONSOLE))
goto err;
- release_console_sem();
+ console_unlock();
if (vt_waitactive(EARLY_SUSPEND_CONSOLE + 1))
pr_warning("console_early_suspend: Can't switch VCs.\n");
return;
err:
pr_warning("console_early_suspend: Can't set console\n");
- release_console_sem();
+ console_unlock();
}
static void console_late_resume(struct early_suspend *h)
{
int ret;
- acquire_console_sem();
+ console_lock();
ret = set_console(orig_fgconsole);
- release_console_sem();
+ console_unlock();
if (ret) {
pr_warning("console_late_resume: Can't set console.\n");
return;