summaryrefslogtreecommitdiff
path: root/drivers/net/netconsole.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/netconsole.c')
-rw-r--r--drivers/net/netconsole.c197
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)
{