s390/con3270: rewrite command line recalling
authorSven Schnelle <svens@linux.ibm.com>
Mon, 28 Nov 2022 10:34:52 +0000 (11:34 +0100)
committerHeiko Carstens <hca@linux.ibm.com>
Mon, 9 Jan 2023 13:34:01 +0000 (14:34 +0100)
Command line recalling is the last user of the 3270 custom malloc()
like allocator. Remove this dependency by using a statically allocated
buffer for the saved command lines, and also remove the allocator.

Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
Acked-by: Heiko Carstens <hca@linux.ibm.com>
Tested-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
drivers/s390/char/con3270.c
drivers/s390/char/raw3270.h

index e3eee0492fea95f494bcb49f083afeac1df7a2cb..71012c20ec7bddfbe73b6f1238d08ab1edccf017 100644 (file)
@@ -34,9 +34,8 @@
 
 #define TTY3270_CHAR_BUF_SIZE 256
 #define TTY3270_OUTPUT_BUFFER_SIZE 4096
-#define TTY3270_STRING_PAGES 5
-
 #define TTY3270_SCREEN_PAGES 8 /* has to be power-of-two */
+#define TTY3270_RECALL_SIZE 16 /* has to be power-of-two */
 
 static struct tty_driver *tty3270_driver;
 static int tty3270_max_index;
@@ -77,13 +76,9 @@ static const unsigned char sfq_read_partition[] = {
 struct tty3270 {
        struct raw3270_view view;
        struct tty_port port;
-       void **freemem_pages;           /* Array of pages used for freemem. */
-       struct list_head freemem;       /* List of free memory for strings. */
 
        /* Output stuff. */
-       struct list_head lines;         /* List of lines. */
        unsigned char wcc;              /* Write control character. */
-       int nr_lines;                   /* # lines in list. */
        int nr_up;                      /* # lines up in history. */
        unsigned long update_flags;     /* Update indication bits. */
        struct raw3270_request *write;  /* Single write request. */
@@ -117,9 +112,9 @@ struct tty3270 {
        unsigned int saved_cx, saved_cy;
 
        /* Command recalling. */
-       struct list_head rcl_lines;     /* List of recallable lines. */
-       struct list_head *rcl_walk;     /* Point in rcl_lines list. */
-       int rcl_nr, rcl_max;            /* Number/max number of rcl_lines. */
+       char **rcl_lines;               /* Array of recallable lines */
+       int rcl_write_index;            /* Write index of recallable items */
+       int rcl_read_index;             /* Read index of recallable items */
 
        /* Character array for put_char/flush_chars. */
        unsigned int char_count;
@@ -203,10 +198,9 @@ static int tty3270_input_size(int cols)
        return cols * 2 - 11;
 }
 
-static void tty3270_update_prompt(struct tty3270 *tp, char *input, int count)
+static void tty3270_update_prompt(struct tty3270 *tp, char *input)
 {
-       memcpy(tp->prompt, input, count);
-       tp->prompt[count] = '\0';
+       strcpy(tp->prompt, input);
        tp->update_flags |= TTY_UPDATE_INPUT;
        tty3270_set_timer(tp, 1);
 }
@@ -271,25 +265,6 @@ static void tty3270_update_string(struct tty3270 *tp, char *line, int len, int n
                raw3270_buffer_address(tp->view.dev, cp + 1, 0, nr + 1);
 }
 
-/*
- * Alloc string for size bytes. If there is not enough room in
- * freemem, free strings until there is room.
- */
-static struct string *tty3270_alloc_string(struct tty3270 *tp, size_t size)
-{
-       struct string *s, *n;
-
-       s = alloc_string(&tp->freemem, size);
-       if (s)
-               return s;
-       list_for_each_entry_safe(s, n, &tp->lines, list) {
-               list_del(&s->list);
-               if (free_string(&tp->freemem, s) >= size)
-                       break;
-       }
-       return alloc_string(&tp->freemem, size);
-}
-
 static void tty3270_blank_screen(struct tty3270 *tp)
 {
        struct tty3270_line *line;
@@ -542,44 +517,29 @@ static void tty3270_update(struct timer_list *t)
  */
 static void tty3270_rcl_add(struct tty3270 *tp, char *input, int len)
 {
-       struct string *s;
-
-       tp->rcl_walk = NULL;
+       char *p;
        if (len <= 0)
                return;
-       if (tp->rcl_nr >= tp->rcl_max) {
-               s = list_entry(tp->rcl_lines.next, struct string, list);
-               list_del(&s->list);
-               free_string(&tp->freemem, s);
-               tp->rcl_nr--;
-       }
-       s = tty3270_alloc_string(tp, len);
-       if (!s)
-               return;
-       memcpy(s->string, input, len);
-       list_add_tail(&s->list, &tp->rcl_lines);
-       tp->rcl_nr++;
+       p = tp->rcl_lines[tp->rcl_write_index++];
+       tp->rcl_write_index &= TTY3270_RECALL_SIZE - 1;
+       memcpy(p, input, len);
+       p[len] = '\0';
+       tp->rcl_read_index = tp->rcl_write_index;
 }
 
 static void tty3270_rcl_backward(struct kbd_data *kbd)
 {
        struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
-       struct string *s;
+       int i = 0;
 
        spin_lock_irq(&tp->view.lock);
        if (tp->inattr == TF_INPUT) {
-               if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines)
-                       tp->rcl_walk = tp->rcl_walk->prev;
-               else if (!list_empty(&tp->rcl_lines))
-                       tp->rcl_walk = tp->rcl_lines.prev;
-               s = tp->rcl_walk ? 
-                       list_entry(tp->rcl_walk, struct string, list) : NULL;
-               if (tp->rcl_walk) {
-                       s = list_entry(tp->rcl_walk, struct string, list);
-                       tty3270_update_prompt(tp, s->string, s->len);
-               } else
-                       tty3270_update_prompt(tp, NULL, 0);
-               tty3270_set_timer(tp, 1);
+               do {
+                       tp->rcl_read_index--;
+                       tp->rcl_read_index &= TTY3270_RECALL_SIZE - 1;
+               } while (!*tp->rcl_lines[tp->rcl_read_index] &&
+                        i++ < TTY3270_RECALL_SIZE - 1);
+               tty3270_update_prompt(tp, tp->rcl_lines[tp->rcl_read_index]);
        }
        spin_unlock_irq(&tp->view.lock);
 }
@@ -664,7 +624,7 @@ static void tty3270_read_tasklet(unsigned long data)
                if (tp->nr_up > 0)
                        tp->nr_up = 0;
                /* Clear input area. */
-               tty3270_update_prompt(tp, NULL, 0);
+               tty3270_update_prompt(tp, "");
                tty3270_set_timer(tp, 1);
                break;
        case AID_CLEAR:
@@ -796,32 +756,14 @@ static void tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct i
 static struct tty3270 *tty3270_alloc_view(void)
 {
        struct tty3270 *tp;
-       int pages;
 
        tp = kzalloc(sizeof(struct tty3270), GFP_KERNEL);
        if (!tp)
                goto out_err;
-       tp->freemem_pages =
-               kmalloc_array(TTY3270_STRING_PAGES, sizeof(void *),
-                             GFP_KERNEL);
-       if (!tp->freemem_pages)
-               goto out_tp;
-       INIT_LIST_HEAD(&tp->freemem);
-       INIT_LIST_HEAD(&tp->lines);
-       INIT_LIST_HEAD(&tp->rcl_lines);
-       tp->rcl_max = 20;
-
-       for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) {
-               tp->freemem_pages[pages] = (void *)
-                       __get_free_pages(GFP_KERNEL|GFP_DMA, 0);
-               if (!tp->freemem_pages[pages])
-                       goto out_pages;
-               add_string_memory(&tp->freemem,
-                                 tp->freemem_pages[pages], PAGE_SIZE);
-       }
+
        tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE);
        if (IS_ERR(tp->write))
-               goto out_pages;
+               goto out_tp;
        tp->read = raw3270_request_alloc(0);
        if (IS_ERR(tp->read))
                goto out_write;
@@ -851,11 +793,6 @@ out_read:
        raw3270_request_free(tp->read);
 out_write:
        raw3270_request_free(tp->write);
-out_pages:
-       while (pages--)
-               free_pages((unsigned long) tp->freemem_pages[pages], 0);
-       kfree(tp->freemem_pages);
-       tty_port_destroy(&tp->port);
 out_tp:
        kfree(tp);
 out_err:
@@ -867,15 +804,10 @@ out_err:
  */
 static void tty3270_free_view(struct tty3270 *tp)
 {
-       int pages;
-
        kbd_free(tp->kbd);
        raw3270_request_free(tp->kreset);
        raw3270_request_free(tp->read);
        raw3270_request_free(tp->write);
-       for (pages = 0; pages < TTY3270_STRING_PAGES; pages++)
-               free_pages((unsigned long) tp->freemem_pages[pages], 0);
-       kfree(tp->freemem_pages);
        free_page((unsigned long)tp->converted_line);
        tty_port_destroy(&tp->port);
        kfree(tp);
@@ -909,6 +841,38 @@ out_err:
        return ERR_PTR(-ENOMEM);
 }
 
+static char **tty3270_alloc_recall(int cols)
+{
+       char **lines;
+       int i;
+
+       lines = kmalloc_array(TTY3270_RECALL_SIZE, sizeof(char *), GFP_KERNEL);
+       if (!lines)
+               return NULL;
+       for (i = 0; i < TTY3270_RECALL_SIZE; i++) {
+               lines[i] = kcalloc(1, tty3270_input_size(cols) + 1, GFP_KERNEL);
+               if (!lines[i])
+                       break;
+       }
+
+       if (i == TTY3270_RECALL_SIZE)
+               return lines;
+
+       while (i--)
+               kfree(lines[i]);
+       kfree(lines);
+       return NULL;
+}
+
+static void tty3270_free_recall(char **lines)
+{
+       int i;
+
+       for (i = 0; i < TTY3270_RECALL_SIZE; i++)
+               kfree(lines[i]);
+       kfree(lines);
+}
+
 /*
  * Free tty3270 screen.
  */
@@ -930,6 +894,7 @@ static void tty3270_resize(struct raw3270_view *view,
 {
        struct tty3270 *tp = container_of(view, struct tty3270, view);
        struct tty3270_line *screen, *oscreen;
+       char **old_rcl_lines, **new_rcl_lines;
        char *old_prompt, *new_prompt;
        char *old_input, *new_input;
        struct tty_struct *tty;
@@ -954,6 +919,9 @@ static void tty3270_resize(struct raw3270_view *view,
        screen = tty3270_alloc_screen(tp, new_rows, new_cols, &new_allocated);
        if (IS_ERR(screen))
                goto out_prompt;
+       new_rcl_lines = tty3270_alloc_recall(new_cols);
+       if (!new_rcl_lines)
+               goto out_screen;
 
        /* Switch to new output size */
        spin_lock_irq(&tp->view.lock);
@@ -967,12 +935,17 @@ static void tty3270_resize(struct raw3270_view *view,
        tp->update_flags = TTY_UPDATE_ALL;
        old_input = tp->input;
        old_prompt = tp->prompt;
+       old_rcl_lines = tp->rcl_lines;
        tp->input = new_input;
        tp->prompt = new_prompt;
+       tp->rcl_lines = new_rcl_lines;
+       tp->rcl_read_index = 0;
+       tp->rcl_write_index = 0;
        spin_unlock_irq(&tp->view.lock);
        tty3270_free_screen(oscreen, old_allocated);
        kfree(old_input);
        kfree(old_prompt);
+       tty3270_free_recall(old_rcl_lines);
        tty3270_set_timer(tp, 1);
        /* Informat tty layer about new size */
        tty = tty_port_tty_get(&tp->port);
@@ -983,6 +956,8 @@ static void tty3270_resize(struct raw3270_view *view,
        tty_do_resize(tty, &ws);
        tty_kref_put(tty);
        return;
+out_screen:
+       tty3270_free_screen(screen, new_rows);
 out_prompt:
        kfree(new_prompt);
 out_input:
@@ -1089,6 +1064,12 @@ tty3270_create_view(int index, struct tty3270 **newtp)
                goto out_free_input;
        }
 
+       tp->rcl_lines = tty3270_alloc_recall(tp->view.cols);
+       if (!tp->rcl_lines) {
+               rc = -ENOMEM;
+               goto out_free_prompt;
+       }
+
        /* Create blank line for every line in the tty output area. */
        tty3270_blank_screen(tp);
 
@@ -1104,6 +1085,8 @@ tty3270_create_view(int index, struct tty3270 **newtp)
        *newtp = tp;
        return 0;
 
+out_free_prompt:
+       kfree(tp->prompt);
 out_free_input:
        kfree(tp->input);
 out_free_converted_line:
@@ -1813,7 +1796,7 @@ static void tty3270_set_termios(struct tty_struct *tty, const struct ktermios *o
                new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN;
                if (new != tp->inattr) {
                        tp->inattr = new;
-                       tty3270_update_prompt(tp, NULL, 0);
+                       tty3270_update_prompt(tp, "");
                        tty3270_set_timer(tp, 1);
                }
        }
index 5ca1af6df93591960b2627d0895b75ba9644142e..19f2bc1ee72e69d8fa6f0a096539d175ec540180 100644 (file)
@@ -213,82 +213,3 @@ struct raw3270_notifier {
 int raw3270_register_notifier(struct raw3270_notifier *);
 void raw3270_unregister_notifier(struct raw3270_notifier *);
 
-/*
- * Little memory allocator for string objects. 
- */
-struct string
-{
-       struct list_head list;
-       unsigned long size;
-       unsigned long len;
-       char string[];
-} __attribute__ ((aligned(8)));
-
-static inline struct string *
-alloc_string(struct list_head *free_list, unsigned long len)
-{
-       struct string *cs, *tmp;
-       unsigned long size;
-
-       size = (len + 7L) & -8L;
-       list_for_each_entry(cs, free_list, list) {
-               if (cs->size < size)
-                       continue;
-               if (cs->size > size + sizeof(struct string)) {
-                       char *endaddr = (char *) (cs + 1) + cs->size;
-                       tmp = (struct string *) (endaddr - size) - 1;
-                       tmp->size = size;
-                       cs->size -= size + sizeof(struct string);
-                       cs = tmp;
-               } else
-                       list_del(&cs->list);
-               cs->len = len;
-               INIT_LIST_HEAD(&cs->list);
-               return cs;
-       }
-       return NULL;
-}
-
-static inline unsigned long
-free_string(struct list_head *free_list, struct string *cs)
-{
-       struct string *tmp;
-       struct list_head *p, *left;
-
-       /* Find out the left neighbour in free memory list. */
-       left = free_list;
-       list_for_each(p, free_list) {
-               if (list_entry(p, struct string, list) > cs)
-                       break;
-               left = p;
-       }
-       /* Try to merge with right neighbour = next element from left. */
-       if (left->next != free_list) {
-               tmp = list_entry(left->next, struct string, list);
-               if ((char *) (cs + 1) + cs->size == (char *) tmp) {
-                       list_del(&tmp->list);
-                       cs->size += tmp->size + sizeof(struct string);
-               }
-       }
-       /* Try to merge with left neighbour. */
-       if (left != free_list) {
-               tmp = list_entry(left, struct string, list);
-               if ((char *) (tmp + 1) + tmp->size == (char *) cs) {
-                       tmp->size += cs->size + sizeof(struct string);
-                       return tmp->size;
-               }
-       }
-       __list_add(&cs->list, left, left->next);
-       return cs->size;
-}
-
-static inline void
-add_string_memory(struct list_head *free_list, void *mem, unsigned long size)
-{
-       struct string *cs;
-
-       cs = (struct string *) mem;
-       cs->size = size - sizeof(struct string);
-       free_string(free_list, cs);
-}
-