Skip to contentSkip to conversation

relay - Remove padding-related code from relay_read()/relay_splice_read() et al.

RFC PATCH In review Tom Zanussi 3 participants 8 messages Sep 29, 2008 – Oct 7, 2008

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 7/11 relay - Remove padding-related code from relay_read()/relay_splice_read() et al.

Remove padding-related code from relay_read()/relay_splice_read() et al.

Because we no longer write padding, we no longer have to read it or account for it anywhere else, greatly simplifying the related code.

---
 kernel/relay.c |  149
++++++++------------------------------------------------
 1 files changed, 20 insertions(+), 129 deletions(-)
kernel/relay.c +0 -0
index d382528..b55466d 100644
@@ -965,72 +965,13 @@ static void relay_file_read_consume(struct

rchan_buf *buf,
size_t bytes_consumed)
{
size_t subbuf_size = buf->chan->subbuf_size;
- size_t n_subbufs = buf->chan->n_subbufs;
- size_t read_subbuf;
-
- if (buf->subbufs_produced == buf->subbufs_consumed &&
- buf->offset == buf->bytes_consumed)
- return;
-
- if (buf->bytes_consumed + bytes_consumed > subbuf_size) {
- relay_subbufs_consumed(buf->chan, buf->cpu, 1);
- buf->bytes_consumed = 0;
- }

buf->bytes_consumed += bytes_consumed;
- if (!read_pos)
- read_subbuf = buf->subbufs_consumed % n_subbufs;
- else
- read_subbuf = read_pos / buf->chan->subbuf_size;
- if (buf->bytes_consumed + buf->padding[read_subbuf] == subbuf_size) {
- if ((read_subbuf == buf->subbufs_produced % n_subbufs) &&
- (buf->offset == subbuf_size))
- return;
- relay_subbufs_consumed(buf->chan, buf->cpu, 1);
- buf->bytes_consumed = 0;
- }
-}

-/*
- * relay_file_read_avail - boolean, are there unconsumed bytes
available?
- */
-static int relay_file_read_avail(struct rchan_buf *buf, size_t read_pos)
-{
- size_t subbuf_size = buf->chan->subbuf_size;
- size_t n_subbufs = buf->chan->n_subbufs;
- size_t produced = buf->subbufs_produced;
- size_t consumed = buf->subbufs_consumed;
-
- relay_file_read_consume(buf, read_pos, 0);
-
- consumed = buf->subbufs_consumed;
-
- if (unlikely(buf->offset > subbuf_size)) {
- if (produced == consumed)
- return 0;
- return 1;
- }
-
- if (unlikely(produced - consumed >= n_subbufs)) {
- consumed = produced - n_subbufs + 1;
- buf->subbufs_consumed = consumed;
+ if (buf->bytes_consumed == subbuf_size) {
+ relay_subbufs_consumed(buf->chan, buf->cpu, 1);
buf->bytes_consumed = 0;
}
-
- produced = (produced % n_subbufs) * subbuf_size + buf->offset;
- consumed = (consumed % n_subbufs) * subbuf_size + buf->bytes_consumed;
-
- if (consumed > produced)
- produced += n_subbufs * subbuf_size;
-
- if (consumed == produced) {
- if (buf->offset == subbuf_size &&
- buf->subbufs_produced > buf->subbufs_consumed)
- return 1;
- return 0;
- }
-
- return 1;
}

/**
@@ -1041,21 +982,19 @@ static int relay_file_read_avail(struct rchan_buf *buf, size_t read_pos)
static size_t relay_file_read_subbuf_avail(size_t read_pos,
struct rchan_buf *buf)
{
- size_t padding, avail = 0;
+ size_t avail;
size_t read_subbuf, read_offset, write_subbuf, write_offset; size_t subbuf_size = buf->chan->subbuf_size;

write_subbuf = (buf->data - buf->start) / subbuf_size;
- write_offset = buf->offset > subbuf_size ? subbuf_size : buf->offset;
+ write_offset = buf->offset;
read_subbuf = read_pos / subbuf_size; read_offset = read_pos % subbuf_size;
- padding = buf->padding[read_subbuf];

- if (read_subbuf == write_subbuf) {
- if (read_offset + padding < write_offset)
- avail = write_offset - (read_offset + padding);
- } else
- avail = (subbuf_size - padding) - read_offset;
+ avail = subbuf_size - read_offset;
+
+ if (read_subbuf == write_subbuf && read_offset < write_offset)
+ avail = write_offset - read_offset;

return avail;
}
@@ -1065,28 +1004,17 @@ static size_t relay_file_read_subbuf_avail(size_t read_pos,
* @read_pos: file read position
* @buf: relay channel buffer
*
- * If the @read_pos is in the middle of padding, return the
- * position of the first actually available byte, otherwise
- * return the original value.
+ * If the @read_pos is 0, return the position of the first
+ * unconsumed byte, otherwise return the original value.
*/
static size_t relay_file_read_start_pos(size_t read_pos,
struct rchan_buf *buf)
{
- size_t read_subbuf, padding, padding_start, padding_end;
size_t subbuf_size = buf->chan->subbuf_size;
- size_t n_subbufs = buf->chan->n_subbufs;
- size_t consumed = buf->subbufs_consumed % n_subbufs;
+ size_t consumed = buf->subbufs_consumed % buf->chan->n_subbufs;

if (!read_pos)
read_pos = consumed * subbuf_size + buf->bytes_consumed;
- read_subbuf = read_pos / subbuf_size;
- padding = buf->padding[read_subbuf];
- padding_start = (read_subbuf + 1) * subbuf_size - padding;
- padding_end = (read_subbuf + 1) * subbuf_size;
- if (read_pos >= padding_start && read_pos < padding_end) {
- read_subbuf = (read_subbuf + 1) % n_subbufs;
- read_pos = read_subbuf * subbuf_size;
- }

return read_pos;
}
@@ -1101,17 +1029,9 @@ static size_t relay_file_read_end_pos(struct rchan_buf *buf,
size_t read_pos, size_t count)
{
- size_t read_subbuf, padding, end_pos;
- size_t subbuf_size = buf->chan->subbuf_size;
- size_t n_subbufs = buf->chan->n_subbufs;
+ size_t end_pos = read_pos + count;

- read_subbuf = read_pos / subbuf_size;
- padding = buf->padding[read_subbuf];
- if (read_pos % subbuf_size + count + padding == subbuf_size)
- end_pos = (read_subbuf + 1) * subbuf_size;
- else
- end_pos = read_pos + count;
- if (end_pos >= subbuf_size * n_subbufs)
+ if (end_pos >= buf->chan->subbuf_size * buf->chan->n_subbufs)
end_pos = 0;

return end_pos;
@@ -1165,9 +1085,6 @@ static ssize_t relay_file_read_subbufs(struct file *filp, loff_t *ppos,

mutex_lock(&filp->f_path.dentry->d_inode->i_mutex); do {
- if (!relay_file_read_avail(buf, *ppos))
- break;
-
read_start = relay_file_read_start_pos(*ppos, buf); avail = relay_file_read_subbuf_avail(read_start, buf); if (!avail)
@@ -1242,8 +1159,7 @@ static int subbuf_splice_actor(struct file *in,
loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,

-			       unsigned int flags,
-			       int *nonpad_ret)
+			       unsigned int flags)

{
unsigned int pidx, poff, total_len, subbuf_pages, nr_pages, ret; struct rchan_buf *rbuf = in->private_data;
@@ -1251,9 +1167,6 @@ static int subbuf_splice_actor(struct file *in,
uint64_t pos = (uint64_t) *ppos;
uint32_t alloc_size = (uint32_t) rbuf->chan->alloc_size; size_t read_start = (size_t) do_div(pos, alloc_size);
- size_t read_subbuf = read_start / subbuf_size;
- size_t padding = rbuf->padding[read_subbuf];
- size_t nonpad_end = read_subbuf * subbuf_size + subbuf_size - padding;
struct page *pages[PIPE_BUFFERS];
struct partial_page partial[PIPE_BUFFERS];
struct splice_pipe_desc spd = {
@@ -1265,7 +1178,8 @@ static int subbuf_splice_actor(struct file *in,
.spd_release = relay_page_release,
};

- if (rbuf->subbufs_produced == rbuf->subbufs_consumed)
+ if (rbuf->subbufs_produced == rbuf->subbufs_consumed &&
+ rbuf->offset == rbuf->bytes_consumed)
return 0;

/*
@@ -1280,46 +1194,25 @@ static int subbuf_splice_actor(struct file *in,
nr_pages = min_t(unsigned int, subbuf_pages, PIPE_BUFFERS);

for (total_len = 0; spd.nr_pages < nr_pages; spd.nr_pages++) {
- unsigned int this_len, this_end, private;
- unsigned int cur_pos = read_start + total_len;
+ unsigned int this_len;

if (!len)
break;

this_len = min_t(unsigned long, len, PAGE_SIZE - poff);
- private = this_len;

spd.pages[spd.nr_pages] = rbuf->page_array[pidx]; spd.partial[spd.nr_pages].offset = poff;

- this_end = cur_pos + this_len;
- if (this_end >= nonpad_end) {
- this_len = nonpad_end - cur_pos;
- private = this_len + padding;
- }
spd.partial[spd.nr_pages].len = this_len;
- spd.partial[spd.nr_pages].private = private;

len -= this_len;
total_len += this_len;
poff = 0;
pidx = (pidx + 1) % subbuf_pages;
-
- if (this_end >= nonpad_end) {
- spd.nr_pages++;
- break;
- }
}

- if (!spd.nr_pages)
- return 0;
-
- ret = *nonpad_ret = splice_to_pipe(pipe, &spd);
- if (ret < 0 || ret < total_len)
- return ret;
-
- if (read_start + ret == nonpad_end)
- ret += padding;
+ ret = splice_to_pipe(pipe, &spd);

         return ret;

}
@@ -1332,13 +1225,12 @@ static ssize_t relay_file_splice_read(struct file *in,
{
ssize_t spliced;
int ret;
- int nonpad_ret = 0;

ret = 0;
spliced = 0;

while (len && !spliced) {
- ret = subbuf_splice_actor(in, ppos, pipe, len, flags, &nonpad_ret);
+ ret = subbuf_splice_actor(in, ppos, pipe, len, flags);
if (ret < 0)
break;
else if (!ret) {
@@ -1355,8 +1247,7 @@ static ssize_t relay_file_splice_read(struct file *in,
len = 0;
else
len -= ret;
- spliced += nonpad_ret;
- nonpad_ret = 0;
+ spliced += ret;
}

if (spliced)

Signature · 1 line
1.5.3.5
#1 lore raw
Quoted 7 lines · * Tom Zanussi ([email protected]) wrote:

Remove padding-related code from relay_read()/relay_splice_read() et al.

Because we no longer write padding, we no longer have to read it or account for it anywhere else, greatly simplifying the related code.

Hi Tom,

This question might sound a bit dumb, but I'll ask anyway : why do you implement a splice_read rather than a splice_write in relay ?

splice_read allows reading information from a file or from a socket to a pipe, while splice_write does the opposite.

So if you implement a relay splice_read, you probably consider the relay buffers to be a "file", so you really have to send the information to a pipe, and then you have to use this pipe to send the data elsewhere.

My first reaction when looking at the splice implementation is that what we would really want is a splice_write which would take the data from a pipe (actually, we would have to write an actor which would make the relay buffer behave like a pipe) and write it either to disk or to a socket.

Is there something I am misunderstanding here ?

Thanks,

Mathieu

Quoted 292 lines
---
 kernel/relay.c |  149
++++++++------------------------------------------------
 1 files changed, 20 insertions(+), 129 deletions(-)
kernel/relay.c +0 -25
index d382528..b55466d 100644
@@ -965,72 +965,13 @@ static void relay_file_read_consume(struct
 rchan_buf *buf,
 				    size_t bytes_consumed)
 {
 	size_t subbuf_size = buf->chan->subbuf_size;
-	size_t n_subbufs = buf->chan->n_subbufs;
-	size_t read_subbuf;
-
-	if (buf->subbufs_produced == buf->subbufs_consumed &&
-	    buf->offset == buf->bytes_consumed)
-		return;
-
-	if (buf->bytes_consumed + bytes_consumed > subbuf_size) {
-		relay_subbufs_consumed(buf->chan, buf->cpu, 1);
-		buf->bytes_consumed = 0;
-	}
 
 	buf->bytes_consumed += bytes_consumed;
-	if (!read_pos)
-		read_subbuf = buf->subbufs_consumed % n_subbufs;
-	else
-		read_subbuf = read_pos / buf->chan->subbuf_size;
-	if (buf->bytes_consumed + buf->padding[read_subbuf] == subbuf_size) {
-		if ((read_subbuf == buf->subbufs_produced % n_subbufs) &&
-		    (buf->offset == subbuf_size))
-			return;
-		relay_subbufs_consumed(buf->chan, buf->cpu, 1);
-		buf->bytes_consumed = 0;
-	}
-}
 
-/*
- *	relay_file_read_avail - boolean, are there unconsumed bytes

available?
- */
-static int relay_file_read_avail(struct rchan_buf *buf, size_t read_pos)

kernel/relay.c +8 -42
-{
-	size_t subbuf_size = buf->chan->subbuf_size;
-	size_t n_subbufs = buf->chan->n_subbufs;
-	size_t produced = buf->subbufs_produced;
-	size_t consumed = buf->subbufs_consumed;
-
-	relay_file_read_consume(buf, read_pos, 0);
-
-	consumed = buf->subbufs_consumed;
-
-	if (unlikely(buf->offset > subbuf_size)) {
-		if (produced == consumed)
-			return 0;
-		return 1;
-	}
-
-	if (unlikely(produced - consumed >= n_subbufs)) {
-		consumed = produced - n_subbufs + 1;
-		buf->subbufs_consumed = consumed;
+	if (buf->bytes_consumed == subbuf_size) {
+		relay_subbufs_consumed(buf->chan, buf->cpu, 1);
 		buf->bytes_consumed = 0;
 	}
-
-	produced = (produced % n_subbufs) * subbuf_size + buf->offset;
-	consumed = (consumed % n_subbufs) * subbuf_size + buf->bytes_consumed;
-
-	if (consumed > produced)
-		produced += n_subbufs * subbuf_size;
-
-	if (consumed == produced) {
-		if (buf->offset == subbuf_size &&
-		    buf->subbufs_produced > buf->subbufs_consumed)
-			return 1;
-		return 0;
-	}
-
-	return 1;
 }
 
 /**
@@ -1041,21 +982,19 @@ static int relay_file_read_avail(struct rchan_buf
 *buf, size_t read_pos)
 static size_t relay_file_read_subbuf_avail(size_t read_pos,
 					   struct rchan_buf *buf)
 {
-	size_t padding, avail = 0;
+	size_t avail;
 	size_t read_subbuf, read_offset, write_subbuf, write_offset;
 	size_t subbuf_size = buf->chan->subbuf_size;
 
 	write_subbuf = (buf->data - buf->start) / subbuf_size;
-	write_offset = buf->offset > subbuf_size ? subbuf_size : buf->offset;
+	write_offset = buf->offset;
 	read_subbuf = read_pos / subbuf_size;
 	read_offset = read_pos % subbuf_size;
-	padding = buf->padding[read_subbuf];
 
-	if (read_subbuf == write_subbuf) {
-		if (read_offset + padding < write_offset)
-			avail = write_offset - (read_offset + padding);
-	} else
-		avail = (subbuf_size - padding) - read_offset;
+	avail = subbuf_size - read_offset;
+
+	if (read_subbuf == write_subbuf && read_offset < write_offset)
+		avail = write_offset - read_offset;
 
 	return avail;

}

kernel/relay.c +3 -14
@@ -1065,28 +1004,17 @@ static size_t
 relay_file_read_subbuf_avail(size_t read_pos,
  *	@read_pos: file read position
  *	@buf: relay channel buffer
  *
- *	If the @read_pos is in the middle of padding, return the
- *	position of the first actually available byte, otherwise
- *	return the original value.
+ *	If the @read_pos is 0, return the position of the first
+ *	unconsumed byte, otherwise return the original value.
  */
 static size_t relay_file_read_start_pos(size_t read_pos,
 					struct rchan_buf *buf)
 {
-	size_t read_subbuf, padding, padding_start, padding_end;
 	size_t subbuf_size = buf->chan->subbuf_size;
-	size_t n_subbufs = buf->chan->n_subbufs;
-	size_t consumed = buf->subbufs_consumed % n_subbufs;
+	size_t consumed = buf->subbufs_consumed % buf->chan->n_subbufs;
 
 	if (!read_pos)
 		read_pos = consumed * subbuf_size + buf->bytes_consumed;
-	read_subbuf = read_pos / subbuf_size;
-	padding = buf->padding[read_subbuf];
-	padding_start = (read_subbuf + 1) * subbuf_size - padding;
-	padding_end = (read_subbuf + 1) * subbuf_size;
-	if (read_pos >= padding_start && read_pos < padding_end) {
-		read_subbuf = (read_subbuf + 1) % n_subbufs;
-		read_pos = read_subbuf * subbuf_size;
-	}
 
 	return read_pos;

}

kernel/relay.c +2 -10
@@ -1101,17 +1029,9 @@ static size_t relay_file_read_end_pos(struct
 rchan_buf *buf,
 				      size_t read_pos,
 				      size_t count)
 {
-	size_t read_subbuf, padding, end_pos;
-	size_t subbuf_size = buf->chan->subbuf_size;
-	size_t n_subbufs = buf->chan->n_subbufs;
+	size_t end_pos = read_pos + count;
 
-	read_subbuf = read_pos / subbuf_size;
-	padding = buf->padding[read_subbuf];
-	if (read_pos % subbuf_size + count + padding == subbuf_size)
-		end_pos = (read_subbuf + 1) * subbuf_size;
-	else
-		end_pos = read_pos + count;
-	if (end_pos >= subbuf_size * n_subbufs)
+	if (end_pos >= buf->chan->subbuf_size * buf->chan->n_subbufs)
 		end_pos = 0;
 
kernel/relay.c +0 -3
 	return end_pos;
@@ -1165,9 +1085,6 @@ static ssize_t relay_file_read_subbufs(struct file
 *filp, loff_t *ppos,
 
 	mutex_lock(&filp->f_path.dentry->d_inode->i_mutex);
 	do {
-		if (!relay_file_read_avail(buf, *ppos))
-			break;
-
 		read_start = relay_file_read_start_pos(*ppos, buf);
 		avail = relay_file_read_subbuf_avail(read_start, buf);
kernel/relay.c +6 -31
 		if (!avail)
@@ -1242,8 +1159,7 @@ static int subbuf_splice_actor(struct file *in,
 			       loff_t *ppos,
 			       struct pipe_inode_info *pipe,
 			       size_t len,
-			       unsigned int flags,
-			       int *nonpad_ret)
+			       unsigned int flags)
 {
 	unsigned int pidx, poff, total_len, subbuf_pages, nr_pages, ret;
 	struct rchan_buf *rbuf = in->private_data;
@@ -1251,9 +1167,6 @@ static int subbuf_splice_actor(struct file *in,
 	uint64_t pos = (uint64_t) *ppos;
 	uint32_t alloc_size = (uint32_t) rbuf->chan->alloc_size;
 	size_t read_start = (size_t) do_div(pos, alloc_size);
-	size_t read_subbuf = read_start / subbuf_size;
-	size_t padding = rbuf->padding[read_subbuf];
-	size_t nonpad_end = read_subbuf * subbuf_size + subbuf_size - padding;
 	struct page *pages[PIPE_BUFFERS];
 	struct partial_page partial[PIPE_BUFFERS];
 	struct splice_pipe_desc spd = {
@@ -1265,7 +1178,8 @@ static int subbuf_splice_actor(struct file *in,
 		.spd_release = relay_page_release,
 	};
 
-	if (rbuf->subbufs_produced == rbuf->subbufs_consumed)
+	if (rbuf->subbufs_produced == rbuf->subbufs_consumed &&
+	    rbuf->offset == rbuf->bytes_consumed)
 		return 0;
 
 	/*
@@ -1280,46 +1194,25 @@ static int subbuf_splice_actor(struct file *in,
 	nr_pages = min_t(unsigned int, subbuf_pages, PIPE_BUFFERS);
 
 	for (total_len = 0; spd.nr_pages < nr_pages; spd.nr_pages++) {
-		unsigned int this_len, this_end, private;
-		unsigned int cur_pos = read_start + total_len;
+		unsigned int this_len;
 
 		if (!len)
 			break;
 
 		this_len = min_t(unsigned long, len, PAGE_SIZE - poff);
-		private = this_len;
 
 		spd.pages[spd.nr_pages] = rbuf->page_array[pidx];
 		spd.partial[spd.nr_pages].offset = poff;
 
-		this_end = cur_pos + this_len;
-		if (this_end >= nonpad_end) {
-			this_len = nonpad_end - cur_pos;
-			private = this_len + padding;
-		}
 		spd.partial[spd.nr_pages].len = this_len;
-		spd.partial[spd.nr_pages].private = private;
 
 		len -= this_len;
 		total_len += this_len;
 		poff = 0;
 		pidx = (pidx + 1) % subbuf_pages;
-
-		if (this_end >= nonpad_end) {
-			spd.nr_pages++;
-			break;
-		}
 	}
 
-	if (!spd.nr_pages)
-		return 0;
-
-	ret = *nonpad_ret = splice_to_pipe(pipe, &spd);
-	if (ret < 0 || ret < total_len)
-		return ret;
-
-        if (read_start + ret == nonpad_end)
-                ret += padding;
+	ret = splice_to_pipe(pipe, &spd);
 
         return ret;
 }
@@ -1332,13 +1225,12 @@ static ssize_t relay_file_splice_read(struct
 file *in,
 {
 	ssize_t spliced;
 	int ret;
-	int nonpad_ret = 0;
 
 	ret = 0;
 	spliced = 0;
 
 	while (len && !spliced) {
-		ret = subbuf_splice_actor(in, ppos, pipe, len, flags, &nonpad_ret);
+		ret = subbuf_splice_actor(in, ppos, pipe, len, flags);
 		if (ret < 0)
 			break;
kernel/relay.c +1 -2
 		else if (!ret) {
@@ -1355,8 +1247,7 @@ static ssize_t relay_file_splice_read(struct file
 *in,
 			len = 0;
 		else
 			len -= ret;
-		spliced += nonpad_ret;
-		nonpad_ret = 0;
+		spliced += ret;
 	}
 

if (spliced)

Signature · 1 line
1.5.3.5
Signature · 2 lines
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68
#2 lore raw
Quoted 14 lines · Mathieu Desnoyers
Quoted 7 lines · * Tom Zanussi ([email protected]) wrote:

Remove padding-related code from relay_read()/relay_splice_read() et al.

Because we no longer write padding, we no longer have to read it or account for it anywhere else, greatly simplifying the related code.

Hi Tom,

This question might sound a bit dumb, but I'll ask anyway : why do you implement a splice_read rather than a splice_write in relay ?

Quoted 8 lines

splice_read allows reading information from a file or from a socket to a pipe, while splice_write does the opposite.

So if you implement a relay splice_read, you probably consider the relay buffers to be a "file", so you really have to send the information to a pipe, and then you have to use this pipe to send the data elsewhere.

Yeah, that's pretty much it. In the case of splicing from a relay file to an output file, the path is basically relay->pipe->outfile. To do the relay->pipe part, do_splice_to() needs a splice_read() implementation, which for relay is relay_splice_read().

Quoted 6 lines

My first reaction when looking at the splice implementation is that what we would really want is a splice_write which would take the data from a pipe (actually, we would have to write an actor which would make the relay buffer behave like a pipe) and write it either to disk or to a socket.

Yes, I think a relay splice_write implementation would take the data from a pipe but would write it into a relay buffer i.e. input->pipe->relay with do_splice_from() calling relay's splice_write() implementation. Once the data is in relay, getting it from there to the disk or socket would follow the same path as the relay->outfile path. At least that's the way I think of it.

For tracing, it probably doesn't make sense to splice_write file data into relay, but it would to splice in pages of data from other tracing sources, such as for example pages of trace data from userspace tracers, which they could do using vmsplice i.e. userpages->pipe->relay.

If you used SPLICE_F_MOVE for the relay->outfile part and SPLICE_F_GIFT for the userpages->relay part, you'd get the trace data directly from the writer to the destination without any copying. Unfortunately, SPLICE_F_MOVE support was removed so this won't really work right now. Also, pages would have to be kept in a linked list instead of an array for this.

Also, for this scheme to work, your trace stream would need to be able to tolerate having pages from different sources inserted basically anywhere in the stream, which means each page would have to be self-contained. If you could atomically insert a set of pages, you could I guess make that a self-contained unit as well and have some kind of flag that says the next n pages in the stream are part of the current event. That might provide a simple way to have multi-page events or to log blobs of binary data - rather than reserving space for them, you'd just splice the pages directly into the buffer.

So maybe splice_read and splice_write on a list of pages, some write functions for tracing into the pages and a userspace post-processor to demultiplex it all is all you need, at least for the streaming-to-disk type of tracing lttng does.

Tom

Quoted 299 lines

Is there something I am misunderstanding here ?

Thanks,

Mathieu

Quoted 292 lines
---
 kernel/relay.c |  149
++++++++------------------------------------------------
 1 files changed, 20 insertions(+), 129 deletions(-)
kernel/relay.c +0 -25
index d382528..b55466d 100644
@@ -965,72 +965,13 @@ static void relay_file_read_consume(struct
 rchan_buf *buf,
 				    size_t bytes_consumed)
 {
 	size_t subbuf_size = buf->chan->subbuf_size;
-	size_t n_subbufs = buf->chan->n_subbufs;
-	size_t read_subbuf;
-
-	if (buf->subbufs_produced == buf->subbufs_consumed &&
-	    buf->offset == buf->bytes_consumed)
-		return;
-
-	if (buf->bytes_consumed + bytes_consumed > subbuf_size) {
-		relay_subbufs_consumed(buf->chan, buf->cpu, 1);
-		buf->bytes_consumed = 0;
-	}
 
 	buf->bytes_consumed += bytes_consumed;
-	if (!read_pos)
-		read_subbuf = buf->subbufs_consumed % n_subbufs;
-	else
-		read_subbuf = read_pos / buf->chan->subbuf_size;
-	if (buf->bytes_consumed + buf->padding[read_subbuf] == subbuf_size) {
-		if ((read_subbuf == buf->subbufs_produced % n_subbufs) &&
-		    (buf->offset == subbuf_size))
-			return;
-		relay_subbufs_consumed(buf->chan, buf->cpu, 1);
-		buf->bytes_consumed = 0;
-	}
-}
 
-/*
- *	relay_file_read_avail - boolean, are there unconsumed bytes

available?
- */
-static int relay_file_read_avail(struct rchan_buf *buf, size_t read_pos)

kernel/relay.c +8 -42
-{
-	size_t subbuf_size = buf->chan->subbuf_size;
-	size_t n_subbufs = buf->chan->n_subbufs;
-	size_t produced = buf->subbufs_produced;
-	size_t consumed = buf->subbufs_consumed;
-
-	relay_file_read_consume(buf, read_pos, 0);
-
-	consumed = buf->subbufs_consumed;
-
-	if (unlikely(buf->offset > subbuf_size)) {
-		if (produced == consumed)
-			return 0;
-		return 1;
-	}
-
-	if (unlikely(produced - consumed >= n_subbufs)) {
-		consumed = produced - n_subbufs + 1;
-		buf->subbufs_consumed = consumed;
+	if (buf->bytes_consumed == subbuf_size) {
+		relay_subbufs_consumed(buf->chan, buf->cpu, 1);
 		buf->bytes_consumed = 0;
 	}
-
-	produced = (produced % n_subbufs) * subbuf_size + buf->offset;
-	consumed = (consumed % n_subbufs) * subbuf_size + buf->bytes_consumed;
-
-	if (consumed > produced)
-		produced += n_subbufs * subbuf_size;
-
-	if (consumed == produced) {
-		if (buf->offset == subbuf_size &&
-		    buf->subbufs_produced > buf->subbufs_consumed)
-			return 1;
-		return 0;
-	}
-
-	return 1;
 }
 
 /**
@@ -1041,21 +982,19 @@ static int relay_file_read_avail(struct rchan_buf
 *buf, size_t read_pos)
 static size_t relay_file_read_subbuf_avail(size_t read_pos,
 					   struct rchan_buf *buf)
 {
-	size_t padding, avail = 0;
+	size_t avail;
 	size_t read_subbuf, read_offset, write_subbuf, write_offset;
 	size_t subbuf_size = buf->chan->subbuf_size;
 
 	write_subbuf = (buf->data - buf->start) / subbuf_size;
-	write_offset = buf->offset > subbuf_size ? subbuf_size : buf->offset;
+	write_offset = buf->offset;
 	read_subbuf = read_pos / subbuf_size;
 	read_offset = read_pos % subbuf_size;
-	padding = buf->padding[read_subbuf];
 
-	if (read_subbuf == write_subbuf) {
-		if (read_offset + padding < write_offset)
-			avail = write_offset - (read_offset + padding);
-	} else
-		avail = (subbuf_size - padding) - read_offset;
+	avail = subbuf_size - read_offset;
+
+	if (read_subbuf == write_subbuf && read_offset < write_offset)
+		avail = write_offset - read_offset;
 
 	return avail;

}

kernel/relay.c +3 -14
@@ -1065,28 +1004,17 @@ static size_t
 relay_file_read_subbuf_avail(size_t read_pos,
  *	@read_pos: file read position
  *	@buf: relay channel buffer
  *
- *	If the @read_pos is in the middle of padding, return the
- *	position of the first actually available byte, otherwise
- *	return the original value.
+ *	If the @read_pos is 0, return the position of the first
+ *	unconsumed byte, otherwise return the original value.
  */
 static size_t relay_file_read_start_pos(size_t read_pos,
 					struct rchan_buf *buf)
 {
-	size_t read_subbuf, padding, padding_start, padding_end;
 	size_t subbuf_size = buf->chan->subbuf_size;
-	size_t n_subbufs = buf->chan->n_subbufs;
-	size_t consumed = buf->subbufs_consumed % n_subbufs;
+	size_t consumed = buf->subbufs_consumed % buf->chan->n_subbufs;
 
 	if (!read_pos)
 		read_pos = consumed * subbuf_size + buf->bytes_consumed;
-	read_subbuf = read_pos / subbuf_size;
-	padding = buf->padding[read_subbuf];
-	padding_start = (read_subbuf + 1) * subbuf_size - padding;
-	padding_end = (read_subbuf + 1) * subbuf_size;
-	if (read_pos >= padding_start && read_pos < padding_end) {
-		read_subbuf = (read_subbuf + 1) % n_subbufs;
-		read_pos = read_subbuf * subbuf_size;
-	}
 
 	return read_pos;

}

kernel/relay.c +2 -10
@@ -1101,17 +1029,9 @@ static size_t relay_file_read_end_pos(struct
 rchan_buf *buf,
 				      size_t read_pos,
 				      size_t count)
 {
-	size_t read_subbuf, padding, end_pos;
-	size_t subbuf_size = buf->chan->subbuf_size;
-	size_t n_subbufs = buf->chan->n_subbufs;
+	size_t end_pos = read_pos + count;
 
-	read_subbuf = read_pos / subbuf_size;
-	padding = buf->padding[read_subbuf];
-	if (read_pos % subbuf_size + count + padding == subbuf_size)
-		end_pos = (read_subbuf + 1) * subbuf_size;
-	else
-		end_pos = read_pos + count;
-	if (end_pos >= subbuf_size * n_subbufs)
+	if (end_pos >= buf->chan->subbuf_size * buf->chan->n_subbufs)
 		end_pos = 0;
 
kernel/relay.c +0 -3
 	return end_pos;
@@ -1165,9 +1085,6 @@ static ssize_t relay_file_read_subbufs(struct file
 *filp, loff_t *ppos,
 
 	mutex_lock(&filp->f_path.dentry->d_inode->i_mutex);
 	do {
-		if (!relay_file_read_avail(buf, *ppos))
-			break;
-
 		read_start = relay_file_read_start_pos(*ppos, buf);
 		avail = relay_file_read_subbuf_avail(read_start, buf);
kernel/relay.c +6 -31
 		if (!avail)
@@ -1242,8 +1159,7 @@ static int subbuf_splice_actor(struct file *in,
 			       loff_t *ppos,
 			       struct pipe_inode_info *pipe,
 			       size_t len,
-			       unsigned int flags,
-			       int *nonpad_ret)
+			       unsigned int flags)
 {
 	unsigned int pidx, poff, total_len, subbuf_pages, nr_pages, ret;
 	struct rchan_buf *rbuf = in->private_data;
@@ -1251,9 +1167,6 @@ static int subbuf_splice_actor(struct file *in,
 	uint64_t pos = (uint64_t) *ppos;
 	uint32_t alloc_size = (uint32_t) rbuf->chan->alloc_size;
 	size_t read_start = (size_t) do_div(pos, alloc_size);
-	size_t read_subbuf = read_start / subbuf_size;
-	size_t padding = rbuf->padding[read_subbuf];
-	size_t nonpad_end = read_subbuf * subbuf_size + subbuf_size - padding;
 	struct page *pages[PIPE_BUFFERS];
 	struct partial_page partial[PIPE_BUFFERS];
 	struct splice_pipe_desc spd = {
@@ -1265,7 +1178,8 @@ static int subbuf_splice_actor(struct file *in,
 		.spd_release = relay_page_release,
 	};
 
-	if (rbuf->subbufs_produced == rbuf->subbufs_consumed)
+	if (rbuf->subbufs_produced == rbuf->subbufs_consumed &&
+	    rbuf->offset == rbuf->bytes_consumed)
 		return 0;
 
 	/*
@@ -1280,46 +1194,25 @@ static int subbuf_splice_actor(struct file *in,
 	nr_pages = min_t(unsigned int, subbuf_pages, PIPE_BUFFERS);
 
 	for (total_len = 0; spd.nr_pages < nr_pages; spd.nr_pages++) {
-		unsigned int this_len, this_end, private;
-		unsigned int cur_pos = read_start + total_len;
+		unsigned int this_len;
 
 		if (!len)
 			break;
 
 		this_len = min_t(unsigned long, len, PAGE_SIZE - poff);
-		private = this_len;
 
 		spd.pages[spd.nr_pages] = rbuf->page_array[pidx];
 		spd.partial[spd.nr_pages].offset = poff;
 
-		this_end = cur_pos + this_len;
-		if (this_end >= nonpad_end) {
-			this_len = nonpad_end - cur_pos;
-			private = this_len + padding;
-		}
 		spd.partial[spd.nr_pages].len = this_len;
-		spd.partial[spd.nr_pages].private = private;
 
 		len -= this_len;
 		total_len += this_len;
 		poff = 0;
 		pidx = (pidx + 1) % subbuf_pages;
-
-		if (this_end >= nonpad_end) {
-			spd.nr_pages++;
-			break;
-		}
 	}
 
-	if (!spd.nr_pages)
-		return 0;
-
-	ret = *nonpad_ret = splice_to_pipe(pipe, &spd);
-	if (ret < 0 || ret < total_len)
-		return ret;
-
-        if (read_start + ret == nonpad_end)
-                ret += padding;
+	ret = splice_to_pipe(pipe, &spd);
 
         return ret;
 }
@@ -1332,13 +1225,12 @@ static ssize_t relay_file_splice_read(struct
 file *in,
 {
 	ssize_t spliced;
 	int ret;
-	int nonpad_ret = 0;
 
 	ret = 0;
 	spliced = 0;
 
 	while (len && !spliced) {
-		ret = subbuf_splice_actor(in, ppos, pipe, len, flags, &nonpad_ret);
+		ret = subbuf_splice_actor(in, ppos, pipe, len, flags);
 		if (ret < 0)
 			break;
kernel/relay.c +1 -2
 		else if (!ret) {
@@ -1355,8 +1247,7 @@ static ssize_t relay_file_splice_read(struct file
 *in,
 			len = 0;
 		else
 			len -= ret;
-		spliced += nonpad_ret;
-		nonpad_ret = 0;
+		spliced += ret;
 	}
 

if (spliced)

Signature · 1 line
1.5.3.5
#3 lore raw

cover relay revamp v5

I decided to go another round with this after all...

The patch following this mail contains the full patch; because there have been so many changes and it's hard to see from just looking at the patch the end result, I'm including relay.c and relay.h at the end of this mail. The full patch also includes the two new files, relay_pagewriter.c and .h in for anyone interested in seeing what those look like.

Basically the patch includes the changes from the previous 11 that I posted and in addition completely separates the reading part of relay from the writing part. With the new changes, relay really does become just what its name says and and nothing more - it accepts pages from tracers, and relays the data to userspace via read(2) or splice(2) (and therefore sendfile(2)). It doesn't allocate any buffer space and provides no write functions - those are expected to be supplied by some other component such as possibly the unified ring-buffer or any other tracer that might want relay pages of trace data to userspace.

One example of such a component would be the original relay write functions and buffers (the no-vmap page-based versions of the previous patchset), which have been split out into a new file called relay_pagewriter.c and provide one means of writing into pages and feeding them into relay. blktrace and kvmtrace have been 'ported' over to using pagewriter instead of relay directly.

I've only tested the new relay lightly via blktrace, which seems to work fine, and haven't looked at plugging anything else into it, but after applying the full patch you should be able to use it to stream e.g. ftrace/unified trace buffer/ring-buffer trace data to disk or over the network...

Anyway, here's a brief overview of the new API (see code for details):

- relay_open():

Creates a per-cpu relay channel and by default associates debugfs files with each per-cpu 'buffer'. No buffer space is allocated for the 'buffers', rather they collect pages added by tracers in a list which is drained by read()/splice(), etc. Tracers add pages to the 'buffers' using relay_write_page() and relay_write_pages(). One of the parameters to relay_open() is n_pages_wakeup, which specifies that readers should be woken up every time n_pages have been added; if this is 0, readers are never woken up.

- relay_add_page():

Adds a page of trace data to relay. After it has been consumed by userspace, the tracer is notified by the 'relay page' callback function page_released(). The page passed via the callback can then be re-used by the tracer (see for example the pagewriter code, which simply adds the page back into pagewriter's per-cpu page pool). If the page has been stolen instead (if SPLICE_F_MOVE succeeded, which can't happen in current kernels since support for it isn't there), the page_stolen() callback is called, at which point the tracer can allocate a new page to replace the stolen page (see the pagewriter code, which does this too).

- relay_add_pages():

The same as relay_add_page(), but adds a set of pages to relay and guarantees that they'll stay together and remain in the same order they were added.

- relay_close():

Releases unread pages to the tracer(s) and frees the channel.

- relay_flush():

Wakes up readers.

- relay_reset():

Releases unread pages to the tracer(s) and resets the channel state.

That's basically the entire kernel API; the userspace API is of course just read(), splice(), and sendfile().

Tom

linux/relay.h new file +170 -0
@@ -0,0 +1,170 @@
+/*
+ * linux/include/linux/relay.h
+ *
+ * Copyright (C) 2002, 2003 - Tom Zanussi ([email protected]), IBM Corp
+ * Copyright (C) 1999, 2000, 2001, 2002 - Karim Yaghmour ([email protected])
+ * Copyright (C) 2008 - Tom Zanussi ([email protected])
+ *
+ * CONFIG_RELAY definitions and declarations
+ */
+
+#ifndef _LINUX_RELAY_H
+#define _LINUX_RELAY_H
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/kref.h>
+#include <linux/pagevec.h>
+
+/*
+ * relay channel flags
+ */
+#define RCHAN_GLOBAL_BUFFER		0x00000001	/* not using per-cpu */
+
+/*
+ * For page lists
+ */
+struct relay_page {
+	struct page *page;
+	struct list_head list;
+	struct relay_page_callbacks *cb;
+	void *private_data;
+};
+
+/*
+ * Per-cpu relay channel buffer
+ */
+struct rchan_buf {
+	struct rchan *chan;		/* associated channel */
+	wait_queue_head_t read_wait;	/* reader wait queue */
+	struct timer_list timer; 	/* reader wake-up timer */
+	struct dentry *dentry;		/* channel file dentry */
+	struct kref kref;		/* channel buffer refcount */
+	struct list_head pages;		/* current set of unconsumed pages */
+	size_t nr_pages;		/* number of unconsumed pages */
+	spinlock_t lock;		/* protect pages list */
+	size_t consumed_offset;		/* bytes consumed in cur page */
+	unsigned int finalized;		/* buffer has been finalized */
+	unsigned int cpu;		/* this buf's cpu */
+} ____cacheline_aligned;
+
+/*
+ * Relay channel data structure
+ */
+struct rchan
+{
+	size_t n_pages_wakeup;		/* wake up readers after filling n */
+	struct rchan_callbacks *cb;	/* client callbacks */
+	struct kref kref;		/* channel refcount */
+	void *private_data;		/* for user-defined data */
+	struct rchan_buf *buf[NR_CPUS]; /* per-cpu channel buffers */
+	struct list_head list;		/* for channel list */
+	struct dentry *parent;		/* parent dentry passed to open */
+	char base_filename[NAME_MAX];	/* saved base filename */
+	unsigned long flags;		/* relay flags for this channel */
+};
+
+/*
+ * Relay channel client callbacks
+ */
+struct rchan_callbacks
+{
+	/*
+	 * create_buf_file - create file to represent a relay channel buffer
+	 * @filename: the name of the file to create
+	 * @parent: the parent of the file to create
+	 * @mode: the mode of the file to create
+	 * @buf: the channel buffer
+	 *
+	 * Called during relay_open(), once for each per-cpu buffer,
+	 * to allow the client to create a file to be used to
+	 * represent the corresponding channel buffer.  If the file is
+	 * created outside of relay, the parent must also exist in
+	 * that filesystem.
+	 *
+	 * The callback should return the dentry of the file created
+	 * to represent the relay buffer.
+	 *
+	 * See Documentation/filesystems/relayfs.txt for more info.
+	 */
+	struct dentry *(*create_buf_file)(const char *filename,
+					  struct dentry *parent,
+					  int mode,
+					  struct rchan_buf *buf);
+
+	/*
+	 * remove_buf_file - remove file representing a relay channel buffer
+	 * @dentry: the dentry of the file to remove
+	 *
+	 * Called during relay_close(), once for each per-cpu buffer,
+	 * to allow the client to remove a file used to represent a
+	 * channel buffer.
+	 *
+	 * The callback should return 0 if successful, negative if not.
+	 */
+	int (*remove_buf_file)(struct dentry *dentry);
+};
+
+/*
+ * Relay page callbacks
+ */
+struct relay_page_callbacks
+{
+	/*
+	 * page_released - notification that a page is ready for re-use
+	 * @page: the released page
+	 * @private_data: user-defined data associated with the page
+	 *
+	 * This callback is a notification that a given page has been
+	 * read by userspace and can be re-used.  Always called in
+	 * user context.
+	 */
+	void (*page_released) (struct page *page, void *private_data);
+
+	/*
+	 * page_released - notification that a page has been stolen
+	 * @page: the stolen page
+	 * @private_data: user-defined data associated with the page
+	 *
+	 * This callback is a notification that a given page has been
+	 * stolen by userspace.  The owner may wish to replace it;
+	 * this gives it the opportunity to do so.  Always called in
+	 * user context.
+	 */
+	void (*page_stolen) (struct page *page, void *private_data);
+};
+
+/*
+ * CONFIG_RELAY kernel API, kernel/relay.c
+ */
+
+extern struct rchan *relay_open(const char *base_filename,
+				struct dentry *parent,
+				size_t n_pages_wakeup,
+				struct rchan_callbacks *cb,
+				void *private_data,
+				unsigned long rchan_flags);
+extern void relay_add_page(struct rchan *chan,
+			   struct page *page,
+			   struct relay_page_callbacks *cb,
+			   void *private_data);
+extern void relay_add_pages(struct rchan *chan,
+			    struct pagevec *pages,
+			    struct relay_page_callbacks *cb,
+			    void *private_data);
+extern void relay_flush(struct rchan *chan);
+extern void relay_close(struct rchan *chan);
+extern void relay_reset(struct rchan *chan);
+
+/*
+ * exported relay file operations, kernel/relay.c
+ */
+extern const struct file_operations relay_file_operations;
+
+#endif /* _LINUX_RELAY_H */
+
relay.c new file +969 -0

969 lines not on this page · raw message

#4 lore raw

RFC PATCH 1/1 relay revamp v5

In reply to Tom Zanussi

The full relay patch.

Basically it includes the changes from the previous 11 that I posted and in addition completely separates the reading part of relay from the writing part. With the new changes, relay really does become just what its name says and and nothing more - it accepts pages from tracers, and relays the data to userspace via read(2) or splice(2) (and therefore sendfile(2)). It doesn't allocate any buffer space and provides no write functions - those are expected to be supplied by some other component such as the unified ring-buffer or any other tracer that might want relay pages of trace data to userspace.

Includes original relay write functions and buffers (the no-vmap page-based versions of the previous patchset), which have been split out into a new file called relay_pagewriter.c and provide one means of writing into pages and feeding them into relay. blktrace and kvmtrace have been 'ported' over to using pagewriter instead of relay directly.

block/blktrace.c +18 -50
index eb9651c..8ba7094 100644
@@ -35,7 +35,7 @@ static void trace_note(struct blk_trace *bt, pid_t pid, int action,
 {
 	struct blk_io_trace *t;
 
-	t = relay_reserve(bt->rchan, sizeof(*t) + len);
+	t = kmalloc(sizeof(*t) + len, GFP_KERNEL);
 	if (t) {
 		const int cpu = smp_processor_id();
 
@@ -47,6 +47,8 @@ static void trace_note(struct blk_trace *bt, pid_t pid, int action,
 		t->cpu = cpu;
 		t->pdu_len = len;
 		memcpy((void *) t + sizeof(*t), data, len);
+		pagewriter_write(bt->pagewriter, t, sizeof(*t) + len);
+		kfree(t);
 	}
 }
 
@@ -166,7 +168,7 @@ void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes,
 	if (unlikely(tsk->btrace_seq != blktrace_seq))
 		trace_note_tsk(bt, tsk);
 
-	t = relay_reserve(bt->rchan, sizeof(*t) + pdu_len);
+	t = kmalloc(sizeof(*t) + pdu_len, GFP_KERNEL);
 	if (t) {
 		cpu = smp_processor_id();
 		sequence = per_cpu_ptr(bt->sequence, cpu);
@@ -185,6 +187,8 @@ void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes,
 
 		if (pdu_len)
 			memcpy((void *) t + sizeof(*t), pdu_data, pdu_len);
+		pagewriter_write(bt->pagewriter, t, sizeof(*t) + pdu_len);
+		kfree(t);
 	}
 
 	local_irq_restore(flags);
@@ -243,7 +247,7 @@ err:
 
 static void blk_trace_cleanup(struct blk_trace *bt)
 {
-	relay_close(bt->rchan);
+	pagewriter_close(bt->pagewriter);
 	debugfs_remove(bt->msg_file);
 	debugfs_remove(bt->dropped_file);
 	blk_remove_tree(bt->dir);
@@ -281,7 +285,8 @@ static ssize_t blk_dropped_read(struct file *filp, char __user *buffer,
 	struct blk_trace *bt = filp->private_data;
 	char buf[16];
 
-	snprintf(buf, sizeof(buf), "%u\n", atomic_read(&bt->dropped));
+	snprintf(buf, sizeof(buf), "%u\n",
+		 atomic_read(&bt->pagewriter->dropped));
 
 	return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
 }
@@ -331,45 +336,6 @@ static const struct file_operations blk_msg_fops = {
 };
 
 /*
- * Keep track of how many times we encountered a full subbuffer, to aid
- * the user space app in telling how many lost events there were.
- */
-static int blk_subbuf_start_callback(struct rchan_buf *buf, void *subbuf,
-				     void *prev_subbuf, size_t prev_padding)
-{
-	struct blk_trace *bt;
-
-	if (!relay_buf_full(buf))
-		return 1;
-
-	bt = buf->chan->private_data;
-	atomic_inc(&bt->dropped);
-	return 0;
-}
-
-static int blk_remove_buf_file_callback(struct dentry *dentry)
-{
-	debugfs_remove(dentry);
-	return 0;
-}
-
-static struct dentry *blk_create_buf_file_callback(const char *filename,
-						   struct dentry *parent,
-						   int mode,
-						   struct rchan_buf *buf,
-						   int *is_global)
-{
-	return debugfs_create_file(filename, mode, parent, buf,
-					&relay_file_operations);
-}
-
-static struct rchan_callbacks blk_relay_callbacks = {
-	.subbuf_start		= blk_subbuf_start_callback,
-	.create_buf_file	= blk_create_buf_file_callback,
-	.remove_buf_file	= blk_remove_buf_file_callback,
-};
-
-/*
  * Setup everything required to start tracing
  */
 int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
@@ -378,6 +344,7 @@ int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
 	struct blk_trace *old_bt, *bt = NULL;
 	struct dentry *dir = NULL;
 	int ret, i;
+	int n_pages, n_pages_wakeup;
 
 	if (!buts->buf_size || !buts->buf_nr)
 		return -EINVAL;
@@ -412,7 +379,6 @@ int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
 
 	bt->dir = dir;
 	bt->dev = dev;
-	atomic_set(&bt->dropped, 0);
 
 	ret = -EIO;
 	bt->dropped_file = debugfs_create_file("dropped", 0444, dir, bt, &blk_dropped_fops);
@@ -423,9 +389,11 @@ int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
 	if (!bt->msg_file)
 		goto err;
 
-	bt->rchan = relay_open("trace", dir, buts->buf_size,
-				buts->buf_nr, &blk_relay_callbacks, bt);
-	if (!bt->rchan)
+	n_pages = (buts->buf_size * buts->buf_nr) / PAGE_SIZE;
+	n_pages_wakeup = buts->buf_size / PAGE_SIZE;
+	bt->pagewriter = pagewriter_open("trace", dir, n_pages, n_pages_wakeup,
+					 NULL, bt, 0UL);
+	if (!bt->pagewriter)
 		goto err;
 
 	bt->act_mask = buts->act_mask;
@@ -458,8 +426,8 @@ err:
 			debugfs_remove(bt->dropped_file);
 		free_percpu(bt->sequence);
 		free_percpu(bt->msg_data);
-		if (bt->rchan)
-			relay_close(bt->rchan);
+		if (bt->pagewriter)
+			pagewriter_close(bt->pagewriter);
 		kfree(bt);
 	}
 	return ret;
@@ -512,7 +480,7 @@ int blk_trace_startstop(struct request_queue *q, int start)
 	} else {
 		if (bt->trace_state == Blktrace_running) {
 			bt->trace_state = Blktrace_stopped;
-			relay_flush(bt->rchan);
+			pagewriter_flush(bt->pagewriter);
 			ret = 0;
 		}
 	}
include/linux/blktrace_api.h +2 -3
index d084b8d..59461f2 100644
@@ -2,7 +2,7 @@
 #define BLKTRACE_H
 
 #include <linux/blkdev.h>
-#include <linux/relay.h>
+#include <linux/relay_pagewriter.h>
 
 /*
  * Trace categories
@@ -119,7 +119,7 @@ enum {
 
 struct blk_trace {
 	int trace_state;
-	struct rchan *rchan;
+	struct pagewriter *pagewriter;
 	unsigned long *sequence;
 	unsigned char *msg_data;
 	u16 act_mask;
@@ -130,7 +130,6 @@ struct blk_trace {
 	struct dentry *dir;
 	struct dentry *dropped_file;
 	struct dentry *msg_file;
-	atomic_t dropped;
 };
 
 /*
include/linux/relay.h +64 -185

322 lines not on this page · raw message

include/linux/relay_pagewriter.h new file +217 -0

217 lines not on this page · raw message

kernel/Makefile +1 -1
index 4e1d7df..42f867e 100644
@@ -78,7 +78,7 @@ obj-$(CONFIG_PREEMPT_RCU) += rcupreempt.o
 ifeq ($(CONFIG_PREEMPT_RCU),y)
 obj-$(CONFIG_RCU_TRACE) += rcupreempt_trace.o
 endif
-obj-$(CONFIG_RELAY) += relay.o
+obj-$(CONFIG_RELAY) += relay.o relay_pagewriter.o
 obj-$(CONFIG_SYSCTL) += utsname_sysctl.o
 obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
 obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o
kernel/relay.c +517 -910

1,759 lines not on this page · raw message

kernel/relay_pagewriter.c new file +616 -0

531 lines not on this page · raw message

virt/kvm/kvm_trace.c +31 -53

84 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Part of the diff is not shown here. View the raw message.

#5 lore raw

Re: [RFC PATCH 1/1] relay revamp v5

Quoted 25 lines · Tom Zanussi

The full relay patch.

Basically it includes the changes from the previous 11 that I posted and in addition completely separates the reading part of relay from the writing part. With the new changes, relay really does become just what its name says and and nothing more - it accepts pages from tracers, and relays the data to userspace via read(2) or splice(2) (and therefore sendfile(2)). It doesn't allocate any buffer space and provides no write functions - those are expected to be supplied by some other component such as the unified ring-buffer or any other tracer that might want relay pages of trace data to userspace.

Includes original relay write functions and buffers (the no-vmap page-based versions of the previous patchset), which have been split out into a new file called relay_pagewriter.c and provide one means of writing into pages and feeding them into relay. blktrace and kvmtrace have been 'ported' over to using pagewriter instead of relay directly.

block/blktrace.c +0 -0
index eb9651c..8ba7094 100644

block/blktrace.c static void trace_note(struct blk_trace *bt, pid_t pid, int action, · line 41

Quoted 8 lines
block/blktrace.c +1 -1
@@ -35,7 +35,7 @@ static void trace_note(struct blk_trace *bt, pid_t pid, int action,
 {
 	struct blk_io_trace *t;
 
-	t = relay_reserve(bt->rchan, sizeof(*t) + len);
+	t = kmalloc(sizeof(*t) + len, GFP_KERNEL);
 	if (t) {
 		const int cpu = smp_processor_id();
 

Ugh, that's no good - it's both way too expensive, and also requires an atomic allocation.

block/blktrace.c void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, · line 174

Quoted 9 lines
block/blktrace.c +1 -1
@@ -166,7 +168,7 @@ void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes,
 	if (unlikely(tsk->btrace_seq != blktrace_seq))
 		trace_note_tsk(bt, tsk);
 
-	t = relay_reserve(bt->rchan, sizeof(*t) + pdu_len);
+	t = kmalloc(sizeof(*t) + pdu_len, GFP_KERNEL);
 	if (t) {
 		cpu = smp_processor_id();
 		sequence = per_cpu_ptr(bt->sequence, cpu);

Ditto - I don't think this approach is viable at all, sorry!

Signature · 1 line
Jens Axboe
#6 lore raw

Re: [RFC PATCH 1/1] relay revamp v5

Quoted 29 lines · Jens Axboe
Quoted 28 lines · Tom Zanussi

The full relay patch.

Basically it includes the changes from the previous 11 that I posted and in addition completely separates the reading part of relay from the writing part. With the new changes, relay really does become just what its name says and and nothing more - it accepts pages from tracers, and relays the data to userspace via read(2) or splice(2) (and therefore sendfile(2)). It doesn't allocate any buffer space and provides no write functions - those are expected to be supplied by some other component such as the unified ring-buffer or any other tracer that might want relay pages of trace data to userspace.

Includes original relay write functions and buffers (the no-vmap page-based versions of the previous patchset), which have been split out into a new file called relay_pagewriter.c and provide one means of writing into pages and feeding them into relay. blktrace and kvmtrace have been 'ported' over to using pagewriter instead of relay directly.

block/blktrace.c +0 -0
index eb9651c..8ba7094 100644
@@ -35,7 +35,7 @@ static void trace_note(struct blk_trace *bt, pid_t pid, int action,
 {
 	struct blk_io_trace *t;
 
Quoted 8 lines

block/blktrace.c static void trace_note(struct blk_trace *bt, pid_t pid, int action, · line 41

Quoted 5 lines
block/blktrace.c +1 -1
-	t = relay_reserve(bt->rchan, sizeof(*t) + len);
+	t = kmalloc(sizeof(*t) + len, GFP_KERNEL);
 	if (t) {
 		const int cpu = smp_processor_id();
 

Ugh, that's no good - it's both way too expensive, and also requires an atomic allocation.

This is was only to keep things working until I could add reserve() back.

Quoted 5 lines
Quoted 5 lines
block/blktrace.c +0 -0
@@ -166,7 +168,7 @@ void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes,
 	if (unlikely(tsk->btrace_seq != blktrace_seq))
 		trace_note_tsk(bt, tsk);
 
Quoted 8 lines

block/blktrace.c void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, · line 174

Quoted 5 lines
block/blktrace.c +1 -1
-	t = relay_reserve(bt->rchan, sizeof(*t) + pdu_len);
+	t = kmalloc(sizeof(*t) + pdu_len, GFP_KERNEL);
 	if (t) {
 		cpu = smp_processor_id();
 		sequence = per_cpu_ptr(bt->sequence, cpu);

Ditto - I don't think this approach is viable at all, sorry!

The patch below adds reserve() back and changes blktrace back to using it. Adding it back also meant adding padding back into the equation, but now there's a way to write a padding 'event' as part of the event stream rather than as metadata. For blktrace, I added a blktrace_notify 'padding message', which I'm sure isn't really what you'd want, but it seems to do the trick for now, and didn't even require any changes in blkparse - it happily skips over the padding as intended.

Tom

Add pagewrite_reserve().

Also added is a callback name write_padding() which can be used to write padding events at the end of the page if an event won't fit in the remaining space.

block/blktrace.c +20 -7
index 8ba7094..f5b745d 100644
@@ -35,7 +35,7 @@ static void trace_note(struct blk_trace *bt, pid_t pid, int action,
 {
 	struct blk_io_trace *t;
 
-	t = kmalloc(sizeof(*t) + len, GFP_KERNEL);
+	t = pagewriter_reserve(bt->pagewriter, sizeof(*t) + len, sizeof(*t));
 	if (t) {
 		const int cpu = smp_processor_id();
 
@@ -47,8 +47,6 @@ static void trace_note(struct blk_trace *bt, pid_t pid, int action,
 		t->cpu = cpu;
 		t->pdu_len = len;
 		memcpy((void *) t + sizeof(*t), data, len);
-		pagewriter_write(bt->pagewriter, t, sizeof(*t) + len);
-		kfree(t);
 	}
 }
 
@@ -168,7 +166,8 @@ void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes,
 	if (unlikely(tsk->btrace_seq != blktrace_seq))
 		trace_note_tsk(bt, tsk);
 
-	t = kmalloc(sizeof(*t) + pdu_len, GFP_KERNEL);
+	t = pagewriter_reserve(bt->pagewriter, sizeof(*t) + pdu_len,
+			       sizeof(*t));
 	if (t) {
 		cpu = smp_processor_id();
 		sequence = per_cpu_ptr(bt->sequence, cpu);
@@ -187,8 +186,6 @@ void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes,
 
 		if (pdu_len)
 			memcpy((void *) t + sizeof(*t), pdu_data, pdu_len);
-		pagewriter_write(bt->pagewriter, t, sizeof(*t) + pdu_len);
-		kfree(t);
 	}
 
 	local_irq_restore(flags);
@@ -335,6 +332,21 @@ static const struct file_operations blk_msg_fops = {
 	.write =	blk_msg_write,
 };
 
+static void blk_write_padding_callback(struct pagewriter_buf *buf,
+				       size_t length,
+				       void *reserved)
+{
+	struct blk_io_trace *t = reserved;
+
+	t->magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION;
+	t->action = BLK_TN_PADDING;
+	t->pdu_len = length - sizeof(*t);
+}
+
+static struct pagewriter_callbacks blk_pagewriter_callbacks = {
+	.write_padding           = blk_write_padding_callback,
+};
+
 /*
  * Setup everything required to start tracing
  */
@@ -392,7 +404,8 @@ int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
 	n_pages = (buts->buf_size * buts->buf_nr) / PAGE_SIZE;
 	n_pages_wakeup = buts->buf_size / PAGE_SIZE;
 	bt->pagewriter = pagewriter_open("trace", dir, n_pages, n_pages_wakeup,
-					 NULL, bt, 0UL);
+					 &blk_pagewriter_callbacks, bt,
+					 PAGEWRITER_PAD_WRITES);
 	if (!bt->pagewriter)
 		goto err;
 
include/linux/blktrace_api.h +2 -0
index 59461f2..c9857f1 100644
@@ -56,6 +56,7 @@ enum blktrace_notify {
 	__BLK_TN_PROCESS = 0,		/* establish pid/name mapping */
 	__BLK_TN_TIMESTAMP,		/* include system clock */
 	__BLK_TN_MESSAGE,		/* Character string message */
+	__BLK_TN_PADDING,		/* Padding message */
 };
 
 
@@ -81,6 +82,7 @@ enum blktrace_notify {
 #define BLK_TN_PROCESS		(__BLK_TN_PROCESS | BLK_TC_ACT(BLK_TC_NOTIFY))
 #define BLK_TN_TIMESTAMP	(__BLK_TN_TIMESTAMP | BLK_TC_ACT(BLK_TC_NOTIFY))
 #define BLK_TN_MESSAGE		(__BLK_TN_MESSAGE | BLK_TC_ACT(BLK_TC_NOTIFY))
+#define BLK_TN_PADDING		(__BLK_TN_PADDING | BLK_TC_ACT(BLK_TC_NOTIFY))
 
 #define BLK_IO_TRACE_MAGIC	0x65617400
 #define BLK_IO_TRACE_VERSION	0x07
include/linux/relay_pagewriter.h +69 -0

93 lines not on this page · raw message

kernel/relay_pagewriter.c +20 -5
index 4b79274..7eb23e9 100644
@@ -54,7 +54,7 @@ static void __pagewriter_reset(struct pagewriter_buf *buf, unsigned int init);
  *	@n_pages_wakeup: wakeup readers after this many pages, 0 means never
  *	@cb: client callback functions
  *	@private_data: user-defined data
- *	@rchan_flags: relay flags, passed on to relay
+ *	@flags: channel flags, top half for pagewriter, bottom half for relay
  *
  *	Returns pagewriter pointer if successful, %NULL otherwise.
  *
@@ -67,7 +67,7 @@ struct pagewriter *pagewriter_open(const char *base_filename,
 				   size_t n_pages_wakeup,
 				   struct pagewriter_callbacks *cb,
 				   void *private_data,
-				   unsigned long rchan_flags)
+				   unsigned long flags)
 {
 	unsigned int i;
 	struct pagewriter *pagewriter;
@@ -77,7 +77,7 @@ struct pagewriter *pagewriter_open(const char *base_filename,
 		return NULL;
 
 	rchan = relay_open(base_filename, parent, n_pages_wakeup, NULL,
-			   private_data, rchan_flags);
+			   private_data, flags);
 	if (!rchan)
 		return NULL;
 
@@ -88,6 +88,7 @@ struct pagewriter *pagewriter_open(const char *base_filename,
 	}
 
 	pagewriter->rchan = rchan;
+	pagewriter->flags = flags;
 	pagewriter->n_pages = n_pages;
 	atomic_set(&pagewriter->dropped, 0);
 
@@ -414,10 +415,20 @@ static void new_page_default_callback(struct pagewriter_buf *buf,
 {
 }
 
+/*
+ * write_padding() default callback.
+ */
+void pagewriter_write_padding_default_callback(struct pagewriter_buf *buf,
+					       size_t length,
+					       void *reserved)
+{
+}
+
 /* pagewriter default callbacks */
 static struct pagewriter_callbacks default_pagewriter_callbacks = {
 	.new_page = new_page_default_callback,
 	.switch_page = pagewriter_switch_page_default_callback,
+	.write_padding = pagewriter_write_padding_default_callback,
 };
 
 static void setup_callbacks(struct pagewriter *pagewriter,
@@ -432,6 +443,9 @@ static void setup_callbacks(struct pagewriter *pagewriter,
 		cb->new_page = new_page_default_callback;
 	if (!cb->switch_page)
 		cb->switch_page = pagewriter_switch_page_default_callback;
+	if (!cb->write_padding)
+		cb->write_padding = pagewriter_write_padding_default_callback;
+
 	pagewriter->cb = cb;
 }
 
@@ -502,7 +516,7 @@ size_t pagewriter_switch_page_default_callback(struct pagewriter_buf *buf,
 					       size_t length,
 					       void **reserved)
 {
-	size_t remainder;
+	size_t remainder = length;
 	struct relay_page *new_page;
 
 	if (unlikely(pagewriter_event_toobig(buf, length)))
@@ -517,7 +531,8 @@ size_t pagewriter_switch_page_default_callback(struct pagewriter_buf *buf,
 		return 0;
 	}
 
-	remainder = length - (PAGE_SIZE - buf->offset);
+	if (buf->pagewriter->flags & PAGEWRITER_PAD_WRITES)
+		remainder = length - (PAGE_SIZE - buf->offset);
 
 	relay_add_page(buf->pagewriter->rchan, buf->page->page,
 		       &pagewriter_relay_page_callbacks, (void *)buf);
#7 lore raw
In reply to Mathieu Desnoyers
Quoted 22 lines · Mathieu Desnoyers
Quoted 7 lines · * Tom Zanussi ([email protected]) wrote:

Remove padding-related code from relay_read()/relay_splice_read() et al.

Because we no longer write padding, we no longer have to read it or account for it anywhere else, greatly simplifying the related code.

Hi Tom,

This question might sound a bit dumb, but I'll ask anyway : why do you implement a splice_read rather than a splice_write in relay ?

splice_read allows reading information from a file or from a socket to a pipe, while splice_write does the opposite.

So if you implement a relay splice_read, you probably consider the relay buffers to be a "file", so you really have to send the information to a pipe, and then you have to use this pipe to send the data elsewhere.

Quoted 5 lines

My first reaction when looking at the splice implementation is that what we would really want is a splice_write which would take the data from a pipe (actually, we would have to write an actor which would make the relay buffer behave like a pipe) and write it either to disk or to a socket.

I don't understand where you are going with this... With the ->splice_read(), we can move relay data into a pipe and pass that to a user application (or send it off using sendfile() or splice to a socket). So it's a way to get the data to user space, instead of using read().

With a ->splice_write(), you could support adding data to relayfs from userspace. Why? You want the ->splice_write() on the output end, for your socket or file or wherever you want to send to relay data TO.

So as long as your model is that the kernel produces data and the user app consumes them, you need the ->splice_read() and not a ->splice_write().

Quoted 1 line

Is there something I am misunderstanding here ?

I think so :-)

Signature · 1 line
Jens Axboe