Commit 76485078 authored by Sven Schnelle's avatar Sven Schnelle Committed by Heiko Carstens
Browse files

s390/con3270: rewrite command line recalling



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: default avatarSven Schnelle <svens@linux.ibm.com>
Acked-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Tested-by: default avatarNiklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
parent 164eb669
Loading
Loading
Loading
Loading
+74 −91
Original line number Diff line number Diff line
@@ -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 @@ static struct tty3270 *tty3270_alloc_view(void)
	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 @@ static struct tty3270 *tty3270_alloc_view(void)
 */
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 @@ static struct tty3270_line *tty3270_alloc_screen(struct tty3270 *tp, unsigned in
	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);
		}
	}
+0 −79
Original line number Diff line number Diff line
@@ -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);
}