Staging: speakup: Move pasting into a work item and update it to match vt (Closes: #735202, #744015)

svn path=/dists/trunk/linux/; revision=21276
This commit is contained in:
Ben Hutchings 2014-04-28 16:10:55 +00:00
parent 2e05cf18be
commit 35178ff3f3
4 changed files with 200 additions and 0 deletions

2
debian/changelog vendored
View File

@ -19,6 +19,8 @@ linux (3.14.2-1~exp1) UNRELEASED; urgency=medium
- Enable PHY_SUN4I_USB, RTC_DRV_SUNXI, SPI_SUN6I, USB_EHCI_HCD_PLATFORM,
USB_OHCI_HCD_PLATFORM and CONFIG_SUNXI_WATCHDOG as modules
* Set ABI to 1
* Staging: speakup: Move pasting into a work item and update it to match vt
(Closes: #735202, #744015)
-- Ben Hutchings <ben@decadent.org.uk> Sun, 20 Apr 2014 21:54:54 +0100

View File

@ -0,0 +1,114 @@
From: Ben Hutchings <ben@decadent.org.uk>
Date: Sat, 15 Mar 2014 22:34:09 +0000
Subject: [PATCH 1/2] Staging: speakup: Move pasting into a work item
Bug-Debian: https://bugs.debian.org/735202
Bug-Debian: https://bugs.debian.org/744015
Input is handled in softirq context, but when pasting we may
need to sleep. speakup_paste_selection() currently tries to
bodge this by busy-waiting if in_atomic(), but that doesn't
help because the ldisc may also sleep.
For bonus breakage, speakup_paste_selection() changes the
state of current, even though it's not running in process
context.
Move it into a work item and make sure to cancel it on exit.
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
---
drivers/staging/speakup/main.c | 1 +
drivers/staging/speakup/selection.c | 38 +++++++++++++++++++++++++++++++------
drivers/staging/speakup/speakup.h | 1 +
3 files changed, 34 insertions(+), 6 deletions(-)
--- a/drivers/staging/speakup/main.c
+++ b/drivers/staging/speakup/main.c
@@ -2218,6 +2218,7 @@ static void __exit speakup_exit(void)
unregister_keyboard_notifier(&keyboard_notifier_block);
unregister_vt_notifier(&vt_notifier_block);
speakup_unregister_devsynth();
+ speakup_cancel_paste();
del_timer(&cursor_timer);
kthread_stop(speakup_task);
speakup_task = NULL;
--- a/drivers/staging/speakup/selection.c
+++ b/drivers/staging/speakup/selection.c
@@ -4,6 +4,8 @@
#include <linux/sched.h>
#include <linux/device.h> /* for dev_warn */
#include <linux/selection.h>
+#include <linux/workqueue.h>
+#include <asm/cmpxchg.h>
#include "speakup.h"
@@ -121,20 +123,24 @@ int speakup_set_selection(struct tty_str
return 0;
}
-/* TODO: move to some helper thread, probably. That'd fix having to check for
- * in_atomic(). */
-int speakup_paste_selection(struct tty_struct *tty)
-{
+struct speakup_paste_work {
+ struct work_struct work;
+ struct tty_struct *tty;
+};
+
+static void __speakup_paste_selection(struct work_struct *work)
+{
+ struct speakup_paste_work *spw =
+ container_of(work, struct speakup_paste_work, work);
+ struct tty_struct *tty = xchg(&spw->tty, NULL);
struct vc_data *vc = (struct vc_data *) tty->driver_data;
int pasted = 0, count;
DECLARE_WAITQUEUE(wait, current);
+
add_wait_queue(&vc->paste_wait, &wait);
while (sel_buffer && sel_buffer_lth > pasted) {
set_current_state(TASK_INTERRUPTIBLE);
if (test_bit(TTY_THROTTLED, &tty->flags)) {
- if (in_atomic())
- /* if we are in an interrupt handler, abort */
- break;
schedule();
continue;
}
@@ -146,6 +152,26 @@ int speakup_paste_selection(struct tty_s
}
remove_wait_queue(&vc->paste_wait, &wait);
current->state = TASK_RUNNING;
+ tty_kref_put(tty);
+}
+
+static struct speakup_paste_work speakup_paste_work = {
+ .work = __WORK_INITIALIZER(speakup_paste_work.work,
+ __speakup_paste_selection)
+};
+
+int speakup_paste_selection(struct tty_struct *tty)
+{
+ if (cmpxchg(&speakup_paste_work.tty, NULL, tty) != NULL)
+ return -EBUSY;
+
+ tty_kref_get(tty);
+ schedule_work_on(WORK_CPU_UNBOUND, &speakup_paste_work.work);
return 0;
}
+void speakup_cancel_paste(void)
+{
+ cancel_work_sync(&speakup_paste_work.work);
+ tty_kref_put(speakup_paste_work.tty);
+}
--- a/drivers/staging/speakup/speakup.h
+++ b/drivers/staging/speakup/speakup.h
@@ -77,6 +77,7 @@ extern void synth_buffer_clear(void);
extern void speakup_clear_selection(void);
extern int speakup_set_selection(struct tty_struct *tty);
extern int speakup_paste_selection(struct tty_struct *tty);
+extern void speakup_cancel_paste(void);
extern void speakup_register_devsynth(void);
extern void speakup_unregister_devsynth(void);
extern void synth_write(const char *buf, size_t count);

View File

@ -0,0 +1,82 @@
From: Ben Hutchings <ben@decadent.org.uk>
Date: Sat, 15 Mar 2014 22:42:27 +0000
Subject: [PATCH 2/2] Staging: speakup: Update __speakup_paste_selection() tty
(ab)usage to match vt
Bug-Debian: https://bugs.debian.org/735202
Bug-Debian: https://bugs.debian.org/744015
This function is largely a duplicate of paste_selection() in
drivers/tty/vt/selection.c, but with its own selection state. The
speakup selection mechanism should really be merged with vt.
For now, apply the changes from 'TTY: vt, fix paste_selection ldisc
handling', 'tty: Make ldisc input flow control concurrency-friendly',
and 'tty: Fix unsafe vt paste_selection()'.
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
---
drivers/staging/speakup/selection.c | 14 +++++++++++---
drivers/tty/tty_buffer.c | 2 ++
2 files changed, 13 insertions(+), 3 deletions(-)
--- a/drivers/staging/speakup/selection.c
+++ b/drivers/staging/speakup/selection.c
@@ -5,6 +5,8 @@
#include <linux/device.h> /* for dev_warn */
#include <linux/selection.h>
#include <linux/workqueue.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
#include <asm/cmpxchg.h>
#include "speakup.h"
@@ -135,8 +137,12 @@ static void __speakup_paste_selection(st
struct tty_struct *tty = xchg(&spw->tty, NULL);
struct vc_data *vc = (struct vc_data *) tty->driver_data;
int pasted = 0, count;
+ struct tty_ldisc *ld;
DECLARE_WAITQUEUE(wait, current);
+ ld = tty_ldisc_ref_wait(tty);
+ tty_buffer_lock_exclusive(&vc->port);
+
add_wait_queue(&vc->paste_wait, &wait);
while (sel_buffer && sel_buffer_lth > pasted) {
set_current_state(TASK_INTERRUPTIBLE);
@@ -145,13 +151,15 @@ static void __speakup_paste_selection(st
continue;
}
count = sel_buffer_lth - pasted;
- count = min_t(int, count, tty->receive_room);
- tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted,
- NULL, count);
+ count = tty_ldisc_receive_buf(ld, sel_buffer + pasted, NULL,
+ count);
pasted += count;
}
remove_wait_queue(&vc->paste_wait, &wait);
current->state = TASK_RUNNING;
+
+ tty_buffer_unlock_exclusive(&vc->port);
+ tty_ldisc_deref(ld);
tty_kref_put(tty);
}
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -60,6 +60,7 @@ void tty_buffer_lock_exclusive(struct tt
atomic_inc(&buf->priority);
mutex_lock(&buf->lock);
}
+EXPORT_SYMBOL_GPL(tty_buffer_lock_exclusive);
void tty_buffer_unlock_exclusive(struct tty_port *port)
{
@@ -73,6 +74,7 @@ void tty_buffer_unlock_exclusive(struct
if (restart)
queue_work(system_unbound_wq, &buf->work);
}
+EXPORT_SYMBOL_GPL(tty_buffer_unlock_exclusive);
/**
* tty_buffer_space_avail - return unused buffer space

View File

@ -65,6 +65,8 @@ bugfix/all/misc-bmp085-Enable-building-as-a-module.patch
bugfix/all/kbuild-use-nostdinc-in-compile-tests.patch
bugfix/all/disable-some-marvell-phys.patch
bugfix/all/bluetooth-allocate-static-minor-for-vhci.patch
bugfix/all/0001-Staging-speakup-Move-pasting-into-a-work-item.patch
bugfix/all/0002-Staging-speakup-Update-__speakup_paste_selection-tty.patch
# Miscellaneous features
features/all/x86-memtest-WARN-if-bad-RAM-found.patch