Skip to content

Commit 2344835

Browse files
tweejAlexei Starovoitov
authored andcommitted
bpf: Fix truncated dmabuf iterator reads
If there is a large number (hundreds) of dmabufs allocated, the text output generated from dmabuf_iter_seq_show can exceed common user buffer sizes (e.g. PAGE_SIZE) necessitating multiple start/stop cycles to iterate through all dmabufs. However the dmabuf iterator currently returns NULL in dmabuf_iter_seq_start for all non-zero pos values, which results in the truncation of the output before all dmabufs are handled. After dma_buf_iter_begin / dma_buf_iter_next, the refcount of the buffer is elevated so that the BPF iterator program can run without holding any locks. When a stop occurs, instead of immediately dropping the reference on the buffer, stash a pointer to the buffer in seq->priv until either start is called or the iterator is released. This also enables the resumption of iteration without first walking through the list of dmabufs based on the pos value. Fixes: 76ea955 ("bpf: Add dmabuf iterator") Signed-off-by: T.J. Mercier <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 297c3fb commit 2344835

File tree

1 file changed

+49
-7
lines changed

1 file changed

+49
-7
lines changed

kernel/bpf/dmabuf_iter.c

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,33 @@
66
#include <linux/kernel.h>
77
#include <linux/seq_file.h>
88

9+
struct dmabuf_iter_priv {
10+
/*
11+
* If this pointer is non-NULL, the buffer's refcount is elevated to
12+
* prevent destruction between stop/start. If reading is not resumed and
13+
* start is never called again, then dmabuf_iter_seq_fini drops the
14+
* reference when the iterator is released.
15+
*/
16+
struct dma_buf *dmabuf;
17+
};
18+
919
static void *dmabuf_iter_seq_start(struct seq_file *seq, loff_t *pos)
1020
{
11-
if (*pos)
12-
return NULL;
21+
struct dmabuf_iter_priv *p = seq->private;
22+
23+
if (*pos) {
24+
struct dma_buf *dmabuf = p->dmabuf;
25+
26+
if (!dmabuf)
27+
return NULL;
28+
29+
/*
30+
* Always resume from where we stopped, regardless of the value
31+
* of pos.
32+
*/
33+
p->dmabuf = NULL;
34+
return dmabuf;
35+
}
1336

1437
return dma_buf_iter_begin();
1538
}
@@ -54,8 +77,11 @@ static void dmabuf_iter_seq_stop(struct seq_file *seq, void *v)
5477
{
5578
struct dma_buf *dmabuf = v;
5679

57-
if (dmabuf)
58-
dma_buf_put(dmabuf);
80+
if (dmabuf) {
81+
struct dmabuf_iter_priv *p = seq->private;
82+
83+
p->dmabuf = dmabuf;
84+
}
5985
}
6086

6187
static const struct seq_operations dmabuf_iter_seq_ops = {
@@ -71,11 +97,27 @@ static void bpf_iter_dmabuf_show_fdinfo(const struct bpf_iter_aux_info *aux,
7197
seq_puts(seq, "dmabuf iter\n");
7298
}
7399

100+
static int dmabuf_iter_seq_init(void *priv, struct bpf_iter_aux_info *aux)
101+
{
102+
struct dmabuf_iter_priv *p = (struct dmabuf_iter_priv *)priv;
103+
104+
p->dmabuf = NULL;
105+
return 0;
106+
}
107+
108+
static void dmabuf_iter_seq_fini(void *priv)
109+
{
110+
struct dmabuf_iter_priv *p = (struct dmabuf_iter_priv *)priv;
111+
112+
if (p->dmabuf)
113+
dma_buf_put(p->dmabuf);
114+
}
115+
74116
static const struct bpf_iter_seq_info dmabuf_iter_seq_info = {
75117
.seq_ops = &dmabuf_iter_seq_ops,
76-
.init_seq_private = NULL,
77-
.fini_seq_private = NULL,
78-
.seq_priv_size = 0,
118+
.init_seq_private = dmabuf_iter_seq_init,
119+
.fini_seq_private = dmabuf_iter_seq_fini,
120+
.seq_priv_size = sizeof(struct dmabuf_iter_priv),
79121
};
80122

81123
static struct bpf_iter_reg bpf_dmabuf_reg_info = {

0 commit comments

Comments
 (0)