summaryrefslogtreecommitdiff
path: root/lib/route/qdisc/plug.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/route/qdisc/plug.c')
-rw-r--r--lib/route/qdisc/plug.c177
1 files changed, 177 insertions, 0 deletions
diff --git a/lib/route/qdisc/plug.c b/lib/route/qdisc/plug.c
new file mode 100644
index 0000000..a99b9be
--- /dev/null
+++ b/lib/route/qdisc/plug.c
@@ -0,0 +1,177 @@
+/*
+ * lib/route/qdisc/plug.c PLUG Qdisc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2012 Shriram Rajagopalan <rshriram@cs.ubc.ca>
+ */
+
+/**
+ * @ingroup qdisc
+ * @defgroup qdisc_plug Plug/Unplug Traffic (PLUG)
+ * @brief
+ *
+ * Queue traffic until an explicit release command.
+ *
+ * There are two ways to use this qdisc:
+ * 1. A simple "instantaneous" plug/unplug operation, by issuing an alternating
+ * sequence of TCQ_PLUG_BUFFER & TCQ_PLUG_RELEASE_INDEFINITE commands.
+ *
+ * 2. For network output buffering (a.k.a output commit) functionality.
+ * Output commit property is commonly used by applications using checkpoint
+ * based fault-tolerance to ensure that the checkpoint from which a system
+ * is being restored is consistent w.r.t outside world.
+ *
+ * Consider for e.g. Remus - a Virtual Machine checkpointing system,
+ * wherein a VM is checkpointed, say every 50ms. The checkpoint is replicated
+ * asynchronously to the backup host, while the VM continues executing the
+ * next epoch speculatively.
+ *
+ * The following is a typical sequence of output buffer operations:
+ * 1.At epoch i, start_buffer(i)
+ * 2. At end of epoch i (i.e. after 50ms):
+ * 2.1 Stop VM and take checkpoint(i).
+ * 2.2 start_buffer(i+1) and Resume VM
+ * 3. While speculatively executing epoch(i+1), asynchronously replicate
+ * checkpoint(i) to backup host.
+ * 4. When checkpoint_ack(i) is received from backup, release_buffer(i)
+ * Thus, this Qdisc would receive the following sequence of commands:
+ * TCQ_PLUG_BUFFER (epoch i)
+ * .. TCQ_PLUG_BUFFER (epoch i+1)
+ * ....TCQ_PLUG_RELEASE_ONE (epoch i)
+ * ......TCQ_PLUG_BUFFER (epoch i+2)
+ * ........
+ *
+ *
+ * State of the queue, when used for network output buffering:
+ *
+ * plug(i+1) plug(i) head
+ * ------------------+--------------------+---------------->
+ * | |
+ * | |
+ * pkts_current_epoch| pkts_last_epoch |pkts_to_release
+ * ----------------->|<--------+--------->|+--------------->
+ * v v
+ *
+ *
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/route/tc-api.h>
+#include <netlink/route/qdisc/plug.h>
+
+static int plug_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+ struct rtnl_plug *plug = data;
+ struct tc_plug_qopt opts;
+
+ if (!plug)
+ return -NLE_INVAL;
+
+ opts.action = plug->action;
+ opts.limit = plug->limit;
+
+ return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD);
+}
+
+/**
+ * @name Attribute Modification
+ * @{
+ */
+
+/**
+ * Insert a plug into the qdisc and buffer any incoming
+ * network traffic.
+ * @arg qdisc PLUG qdisc to be modified.
+ */
+int rtnl_qdisc_plug_buffer(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_plug *plug;
+
+ if (!(plug = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ plug->action = TCQ_PLUG_BUFFER;
+ return 0;
+}
+
+/**
+ * Unplug the qdisc, releasing packets from queue head
+ * to the last complete buffer, while new traffic
+ * continues to be buffered.
+ * @arg qdisc PLUG qdisc to be modified.
+ */
+int rtnl_qdisc_plug_release_one(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_plug *plug;
+
+ if (!(plug = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ plug->action = TCQ_PLUG_RELEASE_ONE;
+ return 0;
+}
+
+/**
+ * Indefinitely unplug the qdisc, releasing all packets.
+ * Network traffic will not be buffered until the next
+ * buffer command is issued.
+ * @arg qdisc PLUG qdisc to be modified.
+ */
+int rtnl_qdisc_plug_release_indefinite(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_plug *plug;
+
+ if (!(plug = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ plug->action = TCQ_PLUG_RELEASE_INDEFINITE;
+ return 0;
+}
+
+/**
+ * Set limit of PLUG qdisc.
+ * @arg qdisc PLUG qdisc to be modified.
+ * @arg limit New limit.
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_plug_set_limit(struct rtnl_qdisc *qdisc, int limit)
+{
+ struct rtnl_plug *plug;
+
+ if (!(plug = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ plug->action = TCQ_PLUG_LIMIT;
+ plug->limit = limit;
+
+ return 0;
+}
+
+/** @} */
+
+static struct rtnl_tc_ops plug_ops = {
+ .to_kind = "plug",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_plug),
+ .to_msg_fill = plug_msg_fill,
+};
+
+static void __init plug_init(void)
+{
+ rtnl_tc_register(&plug_ops);
+}
+
+static void __exit plug_exit(void)
+{
+ rtnl_tc_unregister(&plug_ops);
+}
+
+/** @} */