| /* SPDX-License-Identifier: LGPL-2.1-only */ |
| /* |
| * Copyright (c) 2012 Shriram Rajagopalan <[email protected]> |
| */ |
| |
| /** |
| * @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 "nl-default.h" |
| |
| #include <netlink/netlink.h> |
| #include <netlink/utils.h> |
| #include <netlink/route/qdisc/plug.h> |
| |
| #include "tc-api.h" |
| |
| struct rtnl_plug { |
| int action; |
| uint32_t limit; |
| }; |
| |
| 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 _nl_init plug_init(void) |
| { |
| rtnl_tc_register(&plug_ops); |
| } |
| |
| static void _nl_exit plug_exit(void) |
| { |
| rtnl_tc_unregister(&plug_ops); |
| } |
| |
| /** @} */ |