Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tty: Halve flip buffer GFP_ATOMIC memory consumption

tty flip buffers use GFP_ATOMIC allocations for received data
which is to be processed by the line discipline. For each byte
received, an extra byte is used to indicate the error status of
that byte.

Instead, if the received data is error-free, encode the entire
buffer without status bytes.

Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Peter Hurley and committed by
Greg Kroah-Hartman
acc0f67f 9bbc3dca

+45 -12
+35 -10
drivers/tty/tty_buffer.c
··· 101 101 p->next = NULL; 102 102 p->commit = 0; 103 103 p->read = 0; 104 + p->flags = 0; 104 105 } 105 106 106 107 /** ··· 230 229 * tty_buffer_request_room - grow tty buffer if needed 231 230 * @tty: tty structure 232 231 * @size: size desired 232 + * @flags: buffer flags if new buffer allocated (default = 0) 233 233 * 234 234 * Make at least size bytes of linear space available for the tty 235 235 * buffer. If we fail return the size we managed to find. 236 + * 237 + * Will change over to a new buffer if the current buffer is encoded as 238 + * TTY_NORMAL (so has no flags buffer) and the new buffer requires 239 + * a flags buffer. 236 240 */ 237 - int tty_buffer_request_room(struct tty_port *port, size_t size) 241 + static int __tty_buffer_request_room(struct tty_port *port, size_t size, 242 + int flags) 238 243 { 239 244 struct tty_bufhead *buf = &port->buf; 240 245 struct tty_buffer *b, *n; 241 - int left; 246 + int left, change; 242 247 243 248 b = buf->tail; 244 - left = b->size - b->used; 249 + if (b->flags & TTYB_NORMAL) 250 + left = 2 * b->size - b->used; 251 + else 252 + left = b->size - b->used; 245 253 246 - if (left < size) { 254 + change = (b->flags & TTYB_NORMAL) && (~flags & TTYB_NORMAL); 255 + if (change || left < size) { 247 256 /* This is the slow path - looking for new buffers to use */ 248 257 if ((n = tty_buffer_alloc(port, size)) != NULL) { 258 + n->flags = flags; 249 259 buf->tail = n; 250 260 b->commit = b->used; 251 261 smp_mb(); 252 262 b->next = n; 253 - } else 263 + } else if (change) 264 + size = 0; 265 + else 254 266 size = left; 255 267 } 256 268 return size; 269 + } 270 + 271 + int tty_buffer_request_room(struct tty_port *port, size_t size) 272 + { 273 + return __tty_buffer_request_room(port, size, 0); 257 274 } 258 275 EXPORT_SYMBOL_GPL(tty_buffer_request_room); 259 276 ··· 292 273 int copied = 0; 293 274 do { 294 275 int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); 295 - int space = tty_buffer_request_room(port, goal); 276 + int flags = (flag == TTY_NORMAL) ? TTYB_NORMAL : 0; 277 + int space = __tty_buffer_request_room(port, goal, flags); 296 278 struct tty_buffer *tb = port->buf.tail; 297 279 if (unlikely(space == 0)) 298 280 break; 299 281 memcpy(char_buf_ptr(tb, tb->used), chars, space); 300 - memset(flag_buf_ptr(tb, tb->used), flag, space); 282 + if (~tb->flags & TTYB_NORMAL) 283 + memset(flag_buf_ptr(tb, tb->used), flag, space); 301 284 tb->used += space; 302 285 copied += space; 303 286 chars += space; ··· 382 361 int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, 383 362 size_t size) 384 363 { 385 - int space = tty_buffer_request_room(port, size); 364 + int space = __tty_buffer_request_room(port, size, TTYB_NORMAL); 386 365 if (likely(space)) { 387 366 struct tty_buffer *tb = port->buf.tail; 388 367 *chars = char_buf_ptr(tb, tb->used); 389 - memset(flag_buf_ptr(tb, tb->used), TTY_NORMAL, space); 368 + if (~tb->flags & TTYB_NORMAL) 369 + memset(flag_buf_ptr(tb, tb->used), TTY_NORMAL, space); 390 370 tb->used += space; 391 371 } 392 372 return space; ··· 400 378 { 401 379 struct tty_ldisc *disc = tty->ldisc; 402 380 unsigned char *p = char_buf_ptr(head, head->read); 403 - char *f = flag_buf_ptr(head, head->read); 381 + char *f = NULL; 382 + 383 + if (~head->flags & TTYB_NORMAL) 384 + f = flag_buf_ptr(head, head->read); 404 385 405 386 if (disc->ops->receive_buf2) 406 387 count = disc->ops->receive_buf2(tty, p, f, count);
+4
include/linux/tty.h
··· 39 39 int size; 40 40 int commit; 41 41 int read; 42 + int flags; 42 43 /* Data points here */ 43 44 unsigned long data[0]; 44 45 }; 46 + 47 + /* Values for .flags field of tty_buffer */ 48 + #define TTYB_NORMAL 1 /* buffer has no flags buffer */ 45 49 46 50 static inline unsigned char *char_buf_ptr(struct tty_buffer *b, int ofs) 47 51 {
+6 -2
include/linux/tty_flip.h
··· 17 17 unsigned char ch, char flag) 18 18 { 19 19 struct tty_buffer *tb = port->buf.tail; 20 - if (tb && tb->used < tb->size) { 21 - *flag_buf_ptr(tb, tb->used) = flag; 20 + int change; 21 + 22 + change = (tb->flags & TTYB_NORMAL) && (flag != TTY_NORMAL); 23 + if (!change && tb->used < tb->size) { 24 + if (~tb->flags & TTYB_NORMAL) 25 + *flag_buf_ptr(tb, tb->used) = flag; 22 26 *char_buf_ptr(tb, tb->used++) = ch; 23 27 return 1; 24 28 }