diff options
Diffstat (limited to 'drivers/net/netconsole.c')
-rw-r--r-- | drivers/net/netconsole.c | 197 |
1 files changed, 132 insertions, 65 deletions
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index de20928f7402..4ea44a2f48f7 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -1058,102 +1058,105 @@ static struct notifier_block netconsole_netdev_notifier = { .notifier_call = netconsole_netdev_event, }; -/** - * send_ext_msg_udp - send extended log message to target - * @nt: target to send message to - * @msg: extended log message to send - * @msg_len: length of message - * - * Transfer extended log @msg to @nt. If @msg is longer than - * MAX_PRINT_CHUNK, it'll be split and transmitted in multiple chunks with - * ncfrag header field added to identify them. - */ -static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg, - int msg_len) +static void send_msg_no_fragmentation(struct netconsole_target *nt, + const char *msg, + int msg_len, + int release_len) { static char buf[MAX_PRINT_CHUNK]; /* protected by target_list_lock */ - const char *header, *body; - int offset = 0; - int header_len, body_len; - const char *msg_ready = msg; + const char *userdata = NULL; const char *release; - int release_len = 0; - int userdata_len = 0; - char *userdata = NULL; #ifdef CONFIG_NETCONSOLE_DYNAMIC userdata = nt->userdata_complete; - userdata_len = nt->userdata_length; #endif - if (nt->release) { + if (release_len) { release = init_utsname()->release; - release_len = strlen(release) + 1; + + scnprintf(buf, MAX_PRINT_CHUNK, "%s,%s", release, msg); + msg_len += release_len; + } else { + memcpy(buf, msg, msg_len); } - if (msg_len + release_len + userdata_len <= MAX_PRINT_CHUNK) { - /* No fragmentation needed */ - if (nt->release) { - scnprintf(buf, MAX_PRINT_CHUNK, "%s,%s", release, msg); - msg_len += release_len; - } else { - memcpy(buf, msg, msg_len); - } + if (userdata) + msg_len += scnprintf(&buf[msg_len], + MAX_PRINT_CHUNK - msg_len, + "%s", userdata); - if (userdata) - msg_len += scnprintf(&buf[msg_len], - MAX_PRINT_CHUNK - msg_len, - "%s", userdata); + netpoll_send_udp(&nt->np, buf, msg_len); +} - msg_ready = buf; - netpoll_send_udp(&nt->np, msg_ready, msg_len); - return; - } +static void append_release(char *buf) +{ + const char *release; - /* need to insert extra header fields, detect header and body */ - header = msg; - body = memchr(msg, ';', msg_len); - if (WARN_ON_ONCE(!body)) - return; + release = init_utsname()->release; + scnprintf(buf, MAX_PRINT_CHUNK, "%s,", release); +} - header_len = body - header; - body_len = msg_len - header_len - 1; - body++; +static void send_fragmented_body(struct netconsole_target *nt, char *buf, + const char *msgbody, int header_len, + int msgbody_len) +{ + const char *userdata = NULL; + int body_len, offset = 0; + int userdata_len = 0; - /* - * Transfer multiple chunks with the following extra header. - * "ncfrag=<byte-offset>/<total-bytes>" +#ifdef CONFIG_NETCONSOLE_DYNAMIC + userdata = nt->userdata_complete; + userdata_len = nt->userdata_length; +#endif + + /* body_len represents the number of bytes that will be sent. This is + * bigger than MAX_PRINT_CHUNK, thus, it will be split in multiple + * packets */ - if (nt->release) - scnprintf(buf, MAX_PRINT_CHUNK, "%s,", release); - memcpy(buf + release_len, header, header_len); - header_len += release_len; + body_len = msgbody_len + userdata_len; - while (offset < body_len + userdata_len) { + /* In each iteration of the while loop below, we send a packet + * containing the header and a portion of the body. The body is + * composed of two parts: msgbody and userdata. We keep track of how + * many bytes have been sent so far using the offset variable, which + * ranges from 0 to the total length of the body. + */ + while (offset < body_len) { int this_header = header_len; + bool msgbody_written = false; int this_offset = 0; int this_chunk = 0; this_header += scnprintf(buf + this_header, - sizeof(buf) - this_header, + MAX_PRINT_CHUNK - this_header, ",ncfrag=%d/%d;", offset, - body_len + userdata_len); + body_len); - /* Not all body data has been written yet */ - if (offset < body_len) { - this_chunk = min(body_len - offset, + /* Not all msgbody data has been written yet */ + if (offset < msgbody_len) { + this_chunk = min(msgbody_len - offset, MAX_PRINT_CHUNK - this_header); if (WARN_ON_ONCE(this_chunk <= 0)) return; - memcpy(buf + this_header, body + offset, this_chunk); + memcpy(buf + this_header, msgbody + offset, this_chunk); this_offset += this_chunk; } - /* Body is fully written and there is pending userdata to write, - * append userdata in this chunk + + /* msgbody was finally written, either in the previous + * messages and/or in the current buf. Time to write + * the userdata. */ - if (offset + this_offset >= body_len && - offset + this_offset < userdata_len + body_len) { - int sent_userdata = (offset + this_offset) - body_len; + msgbody_written |= offset + this_offset >= msgbody_len; + + /* Msg body is fully written and there is pending userdata to + * write, append userdata in this chunk + */ + if (msgbody_written && offset + this_offset < body_len) { + /* Track how much user data was already sent. First + * time here, sent_userdata is zero + */ + int sent_userdata = (offset + this_offset) - msgbody_len; + /* offset of bytes used in current buf */ int preceding_bytes = this_chunk + this_header; if (WARN_ON_ONCE(sent_userdata < 0)) @@ -1180,6 +1183,70 @@ static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg, } } +static void send_msg_fragmented(struct netconsole_target *nt, + const char *msg, + int msg_len, + int release_len) +{ + static char buf[MAX_PRINT_CHUNK]; /* protected by target_list_lock */ + int header_len, msgbody_len; + const char *msgbody; + + /* need to insert extra header fields, detect header and msgbody */ + msgbody = memchr(msg, ';', msg_len); + if (WARN_ON_ONCE(!msgbody)) + return; + + header_len = msgbody - msg; + msgbody_len = msg_len - header_len - 1; + msgbody++; + + /* + * Transfer multiple chunks with the following extra header. + * "ncfrag=<byte-offset>/<total-bytes>" + */ + if (release_len) + append_release(buf); + + /* Copy the header into the buffer */ + memcpy(buf + release_len, msg, header_len); + header_len += release_len; + + /* for now on, the header will be persisted, and the msgbody + * will be replaced + */ + send_fragmented_body(nt, buf, msgbody, header_len, msgbody_len); +} + +/** + * send_ext_msg_udp - send extended log message to target + * @nt: target to send message to + * @msg: extended log message to send + * @msg_len: length of message + * + * Transfer extended log @msg to @nt. If @msg is longer than + * MAX_PRINT_CHUNK, it'll be split and transmitted in multiple chunks with + * ncfrag header field added to identify them. + */ +static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg, + int msg_len) +{ + int userdata_len = 0; + int release_len = 0; + +#ifdef CONFIG_NETCONSOLE_DYNAMIC + userdata_len = nt->userdata_length; +#endif + + if (nt->release) + release_len = strlen(init_utsname()->release) + 1; + + if (msg_len + release_len + userdata_len <= MAX_PRINT_CHUNK) + return send_msg_no_fragmentation(nt, msg, msg_len, release_len); + + return send_msg_fragmented(nt, msg, msg_len, release_len); +} + static void write_ext_msg(struct console *con, const char *msg, unsigned int len) { |