/* * Simple per-CPU NMI-safe bump allocation mechanism, backed by the NMI-safe * try_alloc_pages()/free_pages_nolock() primitives. We allocate a page and * stash it in a local per-CPU variable, and bump allocate from the page * whenever items need to be printed to a stream. Each page holds a global * atomic refcount in its first 4 bytes, and then records of variable length * that describe the printed messages. Once the global refcount has dropped to * zero, it is a signal to free the page back to the kernel's page allocator, * given all the individual records in it have been consumed. * * It is possible the same page is used to serve allocations across different * programs, which may be consumed at different times individually, hence * maintaining a reference count per-page is critical for correct lifetime * tracking. * * The bpf_stream_page code will be replaced to use kmalloc_nolock() once it * lands.
*/ struct bpf_stream_page {
refcount_t ref;
u32 consumed; char buf[];
};
/* Available room to add data to a refcounted page. */ #define BPF_STREAM_PAGE_SZ (PAGE_SIZE - offsetofend(struct bpf_stream_page, consumed))
staticint bpf_stream_page_check_room(struct bpf_stream_page *stream_page, int len)
{ int min = offsetof(struct bpf_stream_elem, str[0]); int consumed = stream_page->consumed; int total = BPF_STREAM_PAGE_SZ; int rem = max(0, total - consumed - min);
/* Let's give room of at least 8 bytes. */
WARN_ON_ONCE(rem % 8 != 0);
rem = rem < 8 ? 0 : rem; return min(len, rem);
}
BUILD_BUG_ON(max_len > BPF_STREAM_PAGE_SZ); /* * Length denotes the amount of data to be written as part of stream element, * thus includes '\0' byte. We're capped by how much bpf_bprintf_buffers can * accomodate, therefore deny allocations that won't fit into them.
*/ if (len < 0 || len > max_len) return NULL;
/* * Allocate a bpf_prog_stream_elem and push it to the bpf_prog_stream * log, elements will be popped at once and reversed to print the log.
*/
elem = bpf_stream_elem_alloc(len); if (!elem) return -ENOMEM;
staticbool bpf_stream_consume_elem(struct bpf_stream_elem *elem, int *len)
{ int rem = elem->total_len - elem->consumed_len; int used = min(rem, *len);
elem->consumed_len += used;
*len -= used;
return elem->consumed_len == elem->total_len;
}
staticint bpf_stream_read(struct bpf_stream *stream, void __user *buf, int len)
{ int rem_len = len, cons_len, ret = 0; struct bpf_stream_elem *elem = NULL; struct llist_node *node;
mutex_lock(&stream->lock);
while (rem_len) { int pos = len - rem_len; bool cont;
node = bpf_stream_backlog_peek(stream); if (!node) {
bpf_stream_backlog_fill(stream);
node = bpf_stream_backlog_peek(stream);
} if (!node) break;
elem = container_of(node, typeof(*elem), node);
ret = copy_to_user(buf + pos, elem->str + cons_len,
elem->consumed_len - cons_len); /* Restore in case of error. */ if (ret) {
ret = -EFAULT;
elem->consumed_len = cons_len; break;
}
if (cont) continue;
bpf_stream_backlog_pop(stream);
bpf_stream_release_capacity(stream, elem);
bpf_stream_free_elem(elem);
}
mutex_unlock(&stream->lock); return ret ? ret : len - rem_len;
}
int bpf_prog_stream_read(struct bpf_prog *prog, enum bpf_stream_id stream_id, void __user *buf, int len)
{ struct bpf_stream *stream;
ret = bpf_bprintf_prepare(fmt__str, fmt_size, args, num_args, &data); if (ret < 0) return ret;
ret = bstr_printf(data.buf, MAX_BPRINTF_BUF, fmt__str, data.bin_args); /* Exclude NULL byte during push. */
ret = bpf_stream_push_str(stream, data.buf, ret);
bpf_bprintf_cleanup(&data);
return ret;
}
__bpf_kfunc_end_defs();
/* Added kfunc to common_btf_ids */
void bpf_prog_stream_init(struct bpf_prog *prog)
{ int i;
for (i = 0; i < ARRAY_SIZE(prog->aux->stream); i++) {
atomic_set(&prog->aux->stream[i].capacity, 0);
init_llist_head(&prog->aux->stream[i].log);
mutex_init(&prog->aux->stream[i].lock);
prog->aux->stream[i].backlog_head = NULL;
prog->aux->stream[i].backlog_tail = NULL;
}
}
void bpf_prog_stream_free(struct bpf_prog *prog)
{ struct llist_node *list; int i;
for (i = 0; i < ARRAY_SIZE(prog->aux->stream); i++) {
list = llist_del_all(&prog->aux->stream[i].log);
bpf_stream_free_list(list);
bpf_stream_free_list(prog->aux->stream[i].backlog_head);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.