@@ -9,18 +9,28 @@
#include "notif.h"
#include "rsrc.h"
+static const struct ubuf_info_ops io_ubuf_ops;
+
static void io_notif_tw_complete(struct io_kiocb *notif, struct io_tw_state *ts)
{
struct io_notif_data *nd = io_notif_to_data(notif);
- if (unlikely(nd->zc_report) && (nd->zc_copied || !nd->zc_used))
- notif->cqe.res |= IORING_NOTIF_USAGE_ZC_COPIED;
+ do {
+ notif = cmd_to_io_kiocb(nd);
- if (nd->account_pages && notif->ctx->user) {
- __io_unaccount_mem(notif->ctx->user, nd->account_pages);
- nd->account_pages = 0;
- }
- io_req_task_complete(notif, ts);
+ lockdep_assert(refcount_read(&nd->uarg.refcnt) == 0);
+
+ if (unlikely(nd->zc_report) && (nd->zc_copied || !nd->zc_used))
+ notif->cqe.res |= IORING_NOTIF_USAGE_ZC_COPIED;
+
+ if (nd->account_pages && notif->ctx->user) {
+ __io_unaccount_mem(notif->ctx->user, nd->account_pages);
+ nd->account_pages = 0;
+ }
+
+ nd = nd->next;
+ io_req_task_complete(notif, ts);
+ } while (nd);
}
void io_tx_ubuf_complete(struct sk_buff *skb, struct ubuf_info *uarg,
@@ -39,12 +49,56 @@ void io_tx_ubuf_complete(struct sk_buff *skb, struct ubuf_info *uarg,
if (!refcount_dec_and_test(&uarg->refcnt))
return;
+ if (nd->head != nd) {
+ io_tx_ubuf_complete(skb, &nd->head->uarg, success);
+ return;
+ }
notif->io_task_work.func = io_notif_tw_complete;
__io_req_task_work_add(notif, IOU_F_TWQ_LAZY_WAKE);
}
+static int io_link_skb(struct sk_buff *skb, struct ubuf_info *uarg)
+{
+ struct io_notif_data *nd, *prev_nd;
+ struct io_kiocb *prev_notif, *notif;
+ struct ubuf_info *prev_uarg = skb_zcopy(skb);
+
+ nd = container_of(uarg, struct io_notif_data, uarg);
+ notif = cmd_to_io_kiocb(nd);
+
+ if (!prev_uarg) {
+ net_zcopy_get(&nd->uarg);
+ skb_zcopy_init(skb, &nd->uarg);
+ return 0;
+ }
+ /* handle it separately as we can't link a notif to itself */
+ if (unlikely(prev_uarg == &nd->uarg))
+ return 0;
+ /* we can't join two links together, just request a fresh skb */
+ if (unlikely(nd->head != nd || nd->next))
+ return -EEXIST;
+ /* don't mix zc providers */
+ if (unlikely(prev_uarg->ops != &io_ubuf_ops))
+ return -EEXIST;
+
+ prev_nd = container_of(prev_uarg, struct io_notif_data, uarg);
+ prev_notif = cmd_to_io_kiocb(nd);
+
+ /* make sure all noifications can be finished in the same task_work */
+ if (unlikely(notif->ctx != prev_notif->ctx ||
+ notif->task != prev_notif->task))
+ return -EEXIST;
+
+ nd->head = prev_nd->head;
+ nd->next = prev_nd->next;
+ prev_nd->next = nd;
+ net_zcopy_get(&nd->head->uarg);
+ return 0;
+}
+
static const struct ubuf_info_ops io_ubuf_ops = {
.complete = io_tx_ubuf_complete,
+ .link_skb = io_link_skb,
};
struct io_kiocb *io_alloc_notif(struct io_ring_ctx *ctx)
@@ -65,6 +119,9 @@ struct io_kiocb *io_alloc_notif(struct io_ring_ctx *ctx)
nd = io_notif_to_data(notif);
nd->zc_report = false;
nd->account_pages = 0;
+ nd->next = NULL;
+ nd->head = nd;
+
nd->uarg.flags = IO_NOTIF_UBUF_FLAGS;
nd->uarg.ops = &io_ubuf_ops;
refcount_set(&nd->uarg.refcnt, 1);
@@ -14,6 +14,9 @@ struct io_notif_data {
struct file *file;
struct ubuf_info uarg;
+ struct io_notif_data *next;
+ struct io_notif_data *head;
+
unsigned account_pages;
bool zc_report;
bool zc_used;