mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	printk: reimplement log_cont using record extension
Use the record extending feature of the ringbuffer to implement continuous messages. This preserves the existing continuous message behavior. Signed-off-by: John Ogness <john.ogness@linutronix.de> Reviewed-by: Petr Mladek <pmladek@suse.com> Signed-off-by: Petr Mladek <pmladek@suse.com> Link: https://lore.kernel.org/r/20200914123354.832-7-john.ogness@linutronix.de
This commit is contained in:
		
							parent
							
								
									4cfc7258f8
								
							
						
					
					
						commit
						f5f022e53b
					
				
					 1 changed files with 19 additions and 77 deletions
				
			
		| 
						 | 
					@ -535,7 +535,10 @@ static int log_store(u32 caller_id, int facility, int level,
 | 
				
			||||||
	r.info->caller_id = caller_id;
 | 
						r.info->caller_id = caller_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* insert message */
 | 
						/* insert message */
 | 
				
			||||||
 | 
						if ((flags & LOG_CONT) || !(flags & LOG_NEWLINE))
 | 
				
			||||||
		prb_commit(&e);
 | 
							prb_commit(&e);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							prb_final_commit(&e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (text_len + trunc_msg_len);
 | 
						return (text_len + trunc_msg_len);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1084,7 +1087,7 @@ static unsigned int __init add_to_rb(struct printk_ringbuffer *rb,
 | 
				
			||||||
	dest_r.info->ts_nsec = r->info->ts_nsec;
 | 
						dest_r.info->ts_nsec = r->info->ts_nsec;
 | 
				
			||||||
	dest_r.info->caller_id = r->info->caller_id;
 | 
						dest_r.info->caller_id = r->info->caller_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	prb_commit(&e);
 | 
						prb_final_commit(&e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return prb_record_text_space(&e);
 | 
						return prb_record_text_space(&e);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1884,87 +1887,26 @@ static inline u32 printk_caller_id(void)
 | 
				
			||||||
		0x80000000 + raw_smp_processor_id();
 | 
							0x80000000 + raw_smp_processor_id();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * Continuation lines are buffered, and not committed to the record buffer
 | 
					 | 
				
			||||||
 * until the line is complete, or a race forces it. The line fragments
 | 
					 | 
				
			||||||
 * though, are printed immediately to the consoles to ensure everything has
 | 
					 | 
				
			||||||
 * reached the console in case of a kernel crash.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static struct cont {
 | 
					 | 
				
			||||||
	char buf[LOG_LINE_MAX];
 | 
					 | 
				
			||||||
	size_t len;			/* length == 0 means unused buffer */
 | 
					 | 
				
			||||||
	u32 caller_id;			/* printk_caller_id() of first print */
 | 
					 | 
				
			||||||
	u64 ts_nsec;			/* time of first print */
 | 
					 | 
				
			||||||
	u8 level;			/* log level of first message */
 | 
					 | 
				
			||||||
	u8 facility;			/* log facility of first message */
 | 
					 | 
				
			||||||
	enum log_flags flags;		/* prefix, newline flags */
 | 
					 | 
				
			||||||
} cont;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void cont_flush(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (cont.len == 0)
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	log_store(cont.caller_id, cont.facility, cont.level, cont.flags,
 | 
					 | 
				
			||||||
		  cont.ts_nsec, NULL, 0, cont.buf, cont.len);
 | 
					 | 
				
			||||||
	cont.len = 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool cont_add(u32 caller_id, int facility, int level,
 | 
					 | 
				
			||||||
		     enum log_flags flags, const char *text, size_t len)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* If the line gets too long, split it up in separate records. */
 | 
					 | 
				
			||||||
	if (cont.len + len > sizeof(cont.buf)) {
 | 
					 | 
				
			||||||
		cont_flush();
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!cont.len) {
 | 
					 | 
				
			||||||
		cont.facility = facility;
 | 
					 | 
				
			||||||
		cont.level = level;
 | 
					 | 
				
			||||||
		cont.caller_id = caller_id;
 | 
					 | 
				
			||||||
		cont.ts_nsec = local_clock();
 | 
					 | 
				
			||||||
		cont.flags = flags;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	memcpy(cont.buf + cont.len, text, len);
 | 
					 | 
				
			||||||
	cont.len += len;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// The original flags come from the first line,
 | 
					 | 
				
			||||||
	// but later continuations can add a newline.
 | 
					 | 
				
			||||||
	if (flags & LOG_NEWLINE) {
 | 
					 | 
				
			||||||
		cont.flags |= LOG_NEWLINE;
 | 
					 | 
				
			||||||
		cont_flush();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static size_t log_output(int facility, int level, enum log_flags lflags, const char *dict, size_t dictlen, char *text, size_t text_len)
 | 
					static size_t log_output(int facility, int level, enum log_flags lflags, const char *dict, size_t dictlen, char *text, size_t text_len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const u32 caller_id = printk_caller_id();
 | 
						const u32 caller_id = printk_caller_id();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						if (lflags & LOG_CONT) {
 | 
				
			||||||
	 * If an earlier line was buffered, and we're a continuation
 | 
							struct prb_reserved_entry e;
 | 
				
			||||||
	 * write from the same context, try to add it to the buffer.
 | 
							struct printk_record r;
 | 
				
			||||||
	 */
 | 
					
 | 
				
			||||||
	if (cont.len) {
 | 
							prb_rec_init_wr(&r, text_len, 0);
 | 
				
			||||||
		if (cont.caller_id == caller_id && (lflags & LOG_CONT)) {
 | 
							if (prb_reserve_in_last(&e, prb, &r, caller_id)) {
 | 
				
			||||||
			if (cont_add(caller_id, facility, level, lflags, text, text_len))
 | 
								memcpy(&r.text_buf[r.info->text_len], text, text_len);
 | 
				
			||||||
 | 
								r.info->text_len += text_len;
 | 
				
			||||||
 | 
								if (lflags & LOG_NEWLINE) {
 | 
				
			||||||
 | 
									r.info->flags |= LOG_NEWLINE;
 | 
				
			||||||
 | 
									prb_final_commit(&e);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									prb_commit(&e);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			return text_len;
 | 
								return text_len;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		/* Otherwise, make sure it's flushed */
 | 
					 | 
				
			||||||
		cont_flush();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Skip empty continuation lines that couldn't be added - they just flush */
 | 
					 | 
				
			||||||
	if (!text_len && (lflags & LOG_CONT))
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If it doesn't end in a newline, try to buffer the current line */
 | 
					 | 
				
			||||||
	if (!(lflags & LOG_NEWLINE)) {
 | 
					 | 
				
			||||||
		if (cont_add(caller_id, facility, level, lflags, text, text_len))
 | 
					 | 
				
			||||||
			return text_len;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Store it in the record log */
 | 
						/* Store it in the record log */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue