[PATCH v3 2/2] staging: speakup: refactor to use existing code in vt
Samuel Thibault
samuel.thibault at ens-lyon.org
Thu Apr 4 07:37:10 EDT 2019
Okash Khawaja, le jeu. 04 avril 2019 10:04:42 +0100, a ecrit:
> This patch replaces speakup's implementations with calls to functions
> in tty/vt/selection.c. Those functions are:
>
> cancel_selection()
> do_set_selection()
> paste_selection()
>
> Currently setting selection is done in interrupt context. However,
> do_set_selection() can sleep - for instance, it requires console_lock
> which can sleep. Therefore we offload that work to a work_struct thread,
> following the same pattern which was already set for paste_selection().
> When setting selection, we also get a reference to tty and make sure to
> release the reference before returning.
>
> struct speakup_paste_work has been renamed to the more generic
> speakup_selection_work because it is now used for both pasting as well
> as setting selection. When paste work is cancelled, the code wasn't
> setting tty to NULL. This patch also fixes that by setting tty to NULL
> so that in case of failure we don't get EBUSY forever.
>
> Signed-off-by: Okash Khawaja <okash.khawaja at gmail.com>
Reviewed-by: Samuel Thibault <samuel.thibault at ens-lyon.org>
> ---
> drivers/staging/speakup/main.c | 1 +
> drivers/staging/speakup/selection.c | 212 +++++++++++-----------------
> drivers/staging/speakup/speakup.h | 1 +
> 3 files changed, 88 insertions(+), 126 deletions(-)
>
> diff --git a/drivers/staging/speakup/main.c b/drivers/staging/speakup/main.c
> index b6a65b0c1896..488f2539aa9a 100644
> --- a/drivers/staging/speakup/main.c
> +++ b/drivers/staging/speakup/main.c
> @@ -2319,6 +2319,7 @@ static void __exit speakup_exit(void)
> unregister_keyboard_notifier(&keyboard_notifier_block);
> unregister_vt_notifier(&vt_notifier_block);
> speakup_unregister_devsynth();
> + speakup_cancel_selection();
> speakup_cancel_paste();
> del_timer_sync(&cursor_timer);
> kthread_stop(speakup_task);
> diff --git a/drivers/staging/speakup/selection.c b/drivers/staging/speakup/selection.c
> index 0ed1fefee0e9..6c6d77f8bc24 100644
> --- a/drivers/staging/speakup/selection.c
> +++ b/drivers/staging/speakup/selection.c
> @@ -9,178 +9,138 @@
> #include <linux/tty.h>
> #include <linux/tty_flip.h>
> #include <linux/atomic.h>
> +#include <linux/console.h>
>
> #include "speakup.h"
>
> -/* ------ cut and paste ----- */
> -/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
> -#define ishardspace(c) ((c) == ' ')
> -
> unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
> -
> -/* Variables for selection control. */
> -/* must not be deallocated */
> struct vc_data *spk_sel_cons;
> -/* cleared by clear_selection */
> -static int sel_start = -1;
> -static int sel_end;
> -static int sel_buffer_lth;
> -static char *sel_buffer;
>
> -static unsigned char sel_pos(int n)
> -{
> - return inverse_translate(spk_sel_cons,
> - screen_glyph(spk_sel_cons, n), 0);
> -}
> +struct speakup_selection_work {
> + struct work_struct work;
> + struct tiocl_selection sel;
> + struct tty_struct *tty;
> +};
>
> void speakup_clear_selection(void)
> {
> - sel_start = -1;
> + console_lock();
> + clear_selection();
> + console_unlock();
> }
>
> -/* does screen address p correspond to character at LH/RH edge of screen? */
> -static int atedge(const int p, int size_row)
> +static void __speakup_set_selection(struct work_struct *work)
> {
> - return !(p % size_row) || !((p + 2) % size_row);
> -}
> + struct speakup_selection_work *ssw =
> + container_of(work, struct speakup_selection_work, work);
>
> -/* constrain v such that v <= u */
> -static unsigned short limit(const unsigned short v, const unsigned short u)
> -{
> - return (v > u) ? u : v;
> -}
> + struct tty_struct *tty;
> + struct tiocl_selection sel;
>
> -int speakup_set_selection(struct tty_struct *tty)
> -{
> - int new_sel_start, new_sel_end;
> - char *bp, *obp;
> - int i, ps, pe;
> - struct vc_data *vc = vc_cons[fg_console].d;
> + sel = ssw->sel;
>
> - spk_xs = limit(spk_xs, vc->vc_cols - 1);
> - spk_ys = limit(spk_ys, vc->vc_rows - 1);
> - spk_xe = limit(spk_xe, vc->vc_cols - 1);
> - spk_ye = limit(spk_ye, vc->vc_rows - 1);
> - ps = spk_ys * vc->vc_size_row + (spk_xs << 1);
> - pe = spk_ye * vc->vc_size_row + (spk_xe << 1);
> + /* this ensures we copy sel before releasing the lock below */
> + rmb();
>
> - if (ps > pe) /* make sel_start <= sel_end */
> - swap(ps, pe);
> + /* release the lock by setting tty of the struct to NULL */
> + tty = xchg(&ssw->tty, NULL);
>
> if (spk_sel_cons != vc_cons[fg_console].d) {
> - speakup_clear_selection();
> spk_sel_cons = vc_cons[fg_console].d;
> - dev_warn(tty->dev,
> - "Selection: mark console not the same as cut\n");
> - return -EINVAL;
> + pr_warn("Selection: mark console not the same as cut\n");
> + goto unref;
> }
>
> - new_sel_start = ps;
> - new_sel_end = pe;
> -
> - /* select to end of line if on trailing space */
> - if (new_sel_end > new_sel_start &&
> - !atedge(new_sel_end, vc->vc_size_row) &&
> - ishardspace(sel_pos(new_sel_end))) {
> - for (pe = new_sel_end + 2; ; pe += 2)
> - if (!ishardspace(sel_pos(pe)) ||
> - atedge(pe, vc->vc_size_row))
> - break;
> - if (ishardspace(sel_pos(pe)))
> - new_sel_end = pe;
> - }
> - if ((new_sel_start == sel_start) && (new_sel_end == sel_end))
> - return 0; /* no action required */
> -
> - sel_start = new_sel_start;
> - sel_end = new_sel_end;
> - /* Allocate a new buffer before freeing the old one ... */
> - bp = kmalloc((sel_end - sel_start) / 2 + 1, GFP_ATOMIC);
> - if (!bp) {
> - speakup_clear_selection();
> - return -ENOMEM;
> - }
> - kfree(sel_buffer);
> - sel_buffer = bp;
> -
> - obp = bp;
> - for (i = sel_start; i <= sel_end; i += 2) {
> - *bp = sel_pos(i);
> - if (!ishardspace(*bp++))
> - obp = bp;
> - if (!((i + 2) % vc->vc_size_row)) {
> - /* strip trailing blanks from line and add newline,
> - * unless non-space at end of line.
> - */
> - if (obp != bp) {
> - bp = obp;
> - *bp++ = '\r';
> - }
> - obp = bp;
> - }
> + console_lock();
> + do_set_selection(&sel, tty);
> + console_unlock();
> +
> +unref:
> + tty_kref_put(tty);
> +}
> +
> +static struct speakup_selection_work speakup_sel_work = {
> + .work = __WORK_INITIALIZER(speakup_sel_work.work,
> + __speakup_set_selection)
> +};
> +
> +int speakup_set_selection(struct tty_struct *tty)
> +{
> + /* we get kref here first in order to avoid a subtle race when
> + * cancelling selection work. getting kref first establishes the
> + * invariant that if speakup_sel_work.tty is not NULL when
> + * speakup_cancel_selection() is called, it must be the case that a put
> + * kref is pending.
> + */
> + tty_kref_get(tty);
> + if (cmpxchg(&speakup_sel_work.tty, NULL, tty)) {
> + tty_kref_put(tty);
> + return -EBUSY;
> }
> - sel_buffer_lth = bp - sel_buffer;
> + /* now we have the 'lock' by setting tty member of
> + * speakup_selection_work. wmb() ensures that writes to
> + * speakup_sel_work don't happen before cmpxchg() above.
> + */
> + wmb();
> +
> + speakup_sel_work.sel.xs = spk_xs + 1;
> + speakup_sel_work.sel.ys = spk_ys + 1;
> + speakup_sel_work.sel.xe = spk_xe + 1;
> + speakup_sel_work.sel.ye = spk_ye + 1;
> + speakup_sel_work.sel.sel_mode = TIOCL_SELCHAR;
> +
> + schedule_work_on(WORK_CPU_UNBOUND, &speakup_sel_work.work);
> +
> return 0;
> }
>
> -struct speakup_paste_work {
> - struct work_struct work;
> +void speakup_cancel_selection(void)
> +{
> struct tty_struct *tty;
> -};
> +
> + cancel_work_sync(&speakup_sel_work.work);
> + /* setting to null so that if work fails to run and we cancel it,
> + * we can run it again without getting EBUSY forever from there on.
> + * we need to use xchg here to avoid race with speakup_set_selection()
> + */
> + tty = xchg(&speakup_sel_work.tty, NULL);
> + if (tty)
> + tty_kref_put(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;
> - struct tty_ldisc *ld;
> - DECLARE_WAITQUEUE(wait, current);
> -
> - ld = tty_ldisc_ref(tty);
> - if (!ld)
> - goto tty_unref;
> - 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);
> - if (tty_throttled(tty)) {
> - schedule();
> - continue;
> - }
> - count = sel_buffer_lth - pasted;
> - count = tty_ldisc_receive_buf(ld, sel_buffer + pasted, NULL,
> - count);
> - pasted += count;
> - }
> - remove_wait_queue(&vc->paste_wait, &wait);
> - __set_current_state(TASK_RUNNING);
> + struct speakup_selection_work *ssw =
> + container_of(work, struct speakup_selection_work, work);
> + struct tty_struct *tty = xchg(&ssw->tty, NULL);
>
> - tty_buffer_unlock_exclusive(&vc->port);
> - tty_ldisc_deref(ld);
> -tty_unref:
> + paste_selection(tty);
> tty_kref_put(tty);
> }
>
> -static struct speakup_paste_work speakup_paste_work = {
> +static struct speakup_selection_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))
> + tty_kref_get(tty);
> + if (cmpxchg(&speakup_paste_work.tty, NULL, tty)) {
> + tty_kref_put(tty);
> return -EBUSY;
> + }
>
> - tty_kref_get(tty);
> schedule_work_on(WORK_CPU_UNBOUND, &speakup_paste_work.work);
> return 0;
> }
>
> void speakup_cancel_paste(void)
> {
> + struct tty_struct *tty;
> +
> cancel_work_sync(&speakup_paste_work.work);
> - tty_kref_put(speakup_paste_work.tty);
> + tty = xchg(&speakup_paste_work.tty, NULL);
> + if (tty)
> + tty_kref_put(tty);
> }
> diff --git a/drivers/staging/speakup/speakup.h b/drivers/staging/speakup/speakup.h
> index e4f4f00be2dc..74fe49c2c511 100644
> --- a/drivers/staging/speakup/speakup.h
> +++ b/drivers/staging/speakup/speakup.h
> @@ -72,6 +72,7 @@ void synth_buffer_add(u16 ch);
> void synth_buffer_clear(void);
> void speakup_clear_selection(void);
> int speakup_set_selection(struct tty_struct *tty);
> +void speakup_cancel_selection(void);
> int speakup_paste_selection(struct tty_struct *tty);
> void speakup_cancel_paste(void);
> void speakup_register_devsynth(void);
> --
> 2.21.0
>
--
Samuel
tohi.cybercable.fr (212.198.0.3) si une personne se reconnait derriere
cette adresse que ce soit un pirate ou une victime qu'il se manifeste,
cette personne pourrait bien etre un petit malin
-+- Fred in NPC : Mamaaaaan, y a le routeur qui veut me hacker -+-
More information about the Speakup
mailing list