patch: fast console output ioctl(PIO_DISPLAY)

Tuukka Toivonen (tuukkat@ees2.oulu.fi)
Fri, 17 Jul 1998 19:54:29 +0300 (EET DST)


Consider me as a newbie kernel hacker as this is my first patch...

The problem: Linux 2.0.34 text console is extremely slow. The hardware
scrolling is fine, but text output especially with lots of colors is
awful. Just look the "dialog" program or even worse, run BB-demo.
In my 116x51 screen on Pentium 120 MHz the screen is updated only
couple of times per second.

Question: is 2.1.x faster?

The solution: to optimize console output code. In fact, somebody had
already done a patch for 2.0.x that buffered the display output and
was supposed to make it faster. However, it didn't work very well:
if I did "cat some-big-file" the display stopped after couple of
screenfuls (ctrl-c worked still fine, though). Sorry, I don't remember
now where to get this patch or who made it.

Another solution: to map VGA display memory in user space. KDMAPDISP
looked promising, but it wasn't implemented. Another problem would
be that this would be hardware-dependant way.

As I am a newbie and know pretty much nothing about the kernel,
my solution is a lot simplier than any of the above.

I made a new console ioctl() call named PIO_DISPLAY. The user
programs prepares a image buffer and the ioctl call then simply
copies it directly to screen. It's still very unoptimized (I'm
planning to write x86 assembly for it) but it easily redraws the
screen 300 times per second. The benefits are that it is hardware
independent and binary clean (you can output _any_ character).

What I would appreciate a lot, are _comments_. This works in my
non-SMP x86, but would it work on SMP/non-x86? Are there any
security issues I missed? Generally, what mistakes I made?
(remember that this is my first kernel modification).

And what should I do if(tty->stopped) ?

Is there some existing way to speed up console output, what I missed
(something that would make my patch useless)?

The patch with a test program can be downloaded also from
http://www.ee.oulu.fi/~tuukkat/pio_display0-2.0.34.patch.tar.gz
After applying the patch, enable Char Devices/Fast Console Access
from kernel config and recompile and reinstall the kernel. :)

diff -PruN --exclude=.depend --exclude=.hdepend --exclude=.config /tmp/linux-2.0.34/arch/i386/Makefile linux-2.0.34-p/arch/i386/Makefile
--- /tmp/linux-2.0.34/arch/i386/Makefile Sat Jun 13 17:21:50 1998
+++ linux-2.0.34-p/arch/i386/Makefile Thu Jul 16 18:30:32 1998
@@ -70,7 +70,7 @@
endif

ifdef CONFIG_M586
-CFLAGS := $(CFLAGS) -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=586
+CFLAGS := $(CFLAGS) -m486 -malign-loops=0 -malign-jumps=0 -malign-functions=0 -DCPU=586
endif

ifdef CONFIG_M686
diff -PruN --exclude=.depend --exclude=.hdepend --exclude=.config /tmp/linux-2.0.34/drivers/char/Config.in linux-2.0.34-p/drivers/char/Config.in
--- /tmp/linux-2.0.34/drivers/char/Config.in Sat Jun 13 17:24:20 1998
+++ linux-2.0.34-p/drivers/char/Config.in Thu Jul 16 18:26:47 1998
@@ -76,4 +76,8 @@
tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
fi
bool 'Enhanced Real Time Clock Support' CONFIG_RTC
+
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool 'Fast console access ioctl(fd, PIO_DISPLAY, buffer)' CONFIG_PIO_DISPLAY
+fi
endmenu
diff -PruN --exclude=.depend --exclude=.hdepend --exclude=.config /tmp/linux-2.0.34/drivers/char/console.c linux-2.0.34-p/drivers/char/console.c
--- /tmp/linux-2.0.34/drivers/char/console.c Sat Jun 13 17:24:21 1998
+++ linux-2.0.34-p/drivers/char/console.c Fri Jul 17 10:05:30 1998
@@ -155,6 +155,10 @@
extern unsigned long con_type_init(unsigned long, const char **);
extern int set_get_cmap(unsigned char *, int);
extern int set_get_font(unsigned char *, int, int);
+#ifdef CONFIG_PIO_DISPLAY
+extern int con_disp_user(struct tty_struct * tty, char *buffer,
+ int coord_x, int coord_y, int width, int height, int line_width);
+#endif

/* Description of the hardware situation */
unsigned char video_type; /* Type of display being used */
@@ -2337,3 +2341,80 @@
if (tty) *tty = console_table[fg_console];
return fg_console;
}
+
+#ifdef CONFIG_PIO_DISPLAY
+/*
+ * buffer is a square array with width being line_width bytes. Each element is
+ * two bytes; first byte is character code to put into display memory; and the
+ * another byte is attribute: bits 0..4 are foreground color and bits 5..7 are
+ * background color. coord_x and coord_y tell where in the screen to display the
+ * block (0,0 is upper left corner) and width and height are height and width of
+ * the displayed block. - <tuukkat@ee.oulu.fi>
+ */
+int con_disp_user(struct tty_struct * tty, char *buffer,
+ int coord_x, int coord_y, int width, int height, int line_width)
+{
+ unsigned char c,a;
+ unsigned char *start;
+ unsigned int currcons;
+ struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
+ int i,j;
+
+ /* First check that all arguments are sane */
+
+ currcons = vt->vc_num;
+ if (!vc_cons_allocated(currcons)) {
+ /* could this happen? */
+ static int error = 0;
+ if (!error) {
+ error = 1;
+ printk("con_disp_user: tty %d not allocated\n", currcons+1);
+ }
+ return 0;
+ }
+
+ if (coord_x<0 || coord_y<0 || width<0 || height<0 || line_width<0)
+ return -EINVAL;
+
+ if (coord_x>=video_num_columns || coord_y>=video_num_lines)
+ return -EINVAL;
+
+ if (width==0 || height==0)
+ return 0;
+
+ /* The user might give line_width < width*2 */
+ i = verify_area(VERIFY_READ, buffer, (height-1)*line_width + width*2);
+ if (i)
+ return i;
+
+ if ((coord_x+width) > video_num_columns)
+ width = video_num_columns - coord_x;
+
+ if ((coord_y+height) > video_num_lines)
+ height = video_num_lines - coord_y;
+
+
+ /* Arguments have been checked now. Do the blitting. */
+
+ if (currcons == sel_cons)
+ clear_selection();
+
+ start = (char *)origin;
+ disable_bh(CONSOLE_BH); /* What this does? */
+ if (tty->stopped) goto tty_stopped; /* How this *should* be done? */
+
+ for (i=0; i<height; i++) for (j=0; j<width; j++) {
+ c = get_user(line_width*i + j*2 + buffer);
+ a = get_user(line_width*i + j*2 + buffer + 1);
+ a = color_table[a>>4]<<4 | color_table[a&0xf];
+ scr_writew((short)a<<8 | c,
+ (unsigned short *)
+ (((coord_y+i)*video_num_columns+(coord_x+j))*2 + start));
+ }
+
+tty_stopped:
+ enable_bh(CONSOLE_BH);
+
+ return 0;
+}
+#endif
diff -PruN --exclude=.depend --exclude=.hdepend --exclude=.config /tmp/linux-2.0.34/drivers/char/vt.c linux-2.0.34-p/drivers/char/vt.c
--- /tmp/linux-2.0.34/drivers/char/vt.c Mon May 13 07:36:19 1996
+++ linux-2.0.34-p/drivers/char/vt.c Fri Jul 17 07:12:06 1998
@@ -81,6 +82,10 @@
extern int con_get_cmap(unsigned char *cmap);
extern void reset_palette(int currcons);
extern int con_adjust_height(unsigned long fontheight);
+#ifdef CONFIG_PIO_DISPLAY
+extern int con_disp_user(struct tty_struct * tty, char *buffer,
+ int coord_x, int coord_y, int width, int height, int line_width);
+#endif

extern int video_mode_512ch;
extern unsigned long video_font_height;
@@ -1132,6 +1137,25 @@
return -EPERM;
vt_dont_switch = 0;
return 0;
+
+#ifdef CONFIG_PIO_DISPLAY
+ case PIO_DISPLAY: /* Copy display memory from user space */
+ {
+ struct displaydesc * const desc = (struct displaydesc *)arg;
+ if (!perm)
+ return -EPERM;
+ i = verify_area(VERIFY_READ, (const void *)desc, sizeof(struct displaydesc));
+ if (i)
+ return i;
+ return con_disp_user(tty,
+ get_user(&desc->buffer),
+ get_user(&desc->coord_x), get_user(&desc->coord_y),
+ get_user(&desc->width), get_user(&desc->height),
+ get_user(&desc->line_width));
+ /* con_disp_user() defined in console.c */
+ }
+#endif
+
default:
return -ENOIOCTLCMD;
}
diff -PruN --exclude=.depend --exclude=.hdepend --exclude=.config /tmp/linux-2.0.34/include/linux/kd.h linux-2.0.34-p/include/linux/kd.h
--- /tmp/linux-2.0.34/include/linux/kd.h Mon Jun 3 12:16:52 1996
+++ linux-2.0.34-p/include/linux/kd.h Fri Jul 17 07:41:29 1998
@@ -136,4 +136,12 @@
don't reuse for the time being */
/* note: 0x4B60-0x4B6D, 0x4B70, 0x4B71 used above */

+#define PIO_DISPLAY 0x4B4F /* copy display from user space */
+struct displaydesc {
+ char *buffer; /* points to display buffer in user memory */
+ int coord_x, coord_y; /* Screen coordinates where to write */
+ int width, height; /* Dimensions of the rectangle to write */
+ int line_width; /* Image width in bytes in user space */
+};
+
#endif /* _LINUX_KD_H */

--
| Tuukka Toivonen <tuukkat@ee.oulu.fi>       [PGP public key
| Homepage: http://www.ee.oulu.fi/~tuukkat/       available]
| Try also finger -l tuukkat@ee.oulu.fi
| Studying information engineering at the University of Oulu
+-----------------------------------------------------------

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.altern.org/andrebalsa/doc/lkml-faq.html