1/* 2 * lib/route/qdisc/plug.c PLUG Qdisc 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation version 2.1 7 * of the License. 8 * 9 * Copyright (c) 2012 Shriram Rajagopalan <rshriram@cs.ubc.ca> 10 */ 11 12/** 13 * @ingroup qdisc 14 * @defgroup qdisc_plug Plug/Unplug Traffic (PLUG) 15 * @brief 16 * 17 * Queue traffic until an explicit release command. 18 * 19 * There are two ways to use this qdisc: 20 * 1. A simple "instantaneous" plug/unplug operation, by issuing an alternating 21 * sequence of TCQ_PLUG_BUFFER & TCQ_PLUG_RELEASE_INDEFINITE commands. 22 * 23 * 2. For network output buffering (a.k.a output commit) functionality. 24 * Output commit property is commonly used by applications using checkpoint 25 * based fault-tolerance to ensure that the checkpoint from which a system 26 * is being restored is consistent w.r.t outside world. 27 * 28 * Consider for e.g. Remus - a Virtual Machine checkpointing system, 29 * wherein a VM is checkpointed, say every 50ms. The checkpoint is replicated 30 * asynchronously to the backup host, while the VM continues executing the 31 * next epoch speculatively. 32 * 33 * The following is a typical sequence of output buffer operations: 34 * 1.At epoch i, start_buffer(i) 35 * 2. At end of epoch i (i.e. after 50ms): 36 * 2.1 Stop VM and take checkpoint(i). 37 * 2.2 start_buffer(i+1) and Resume VM 38 * 3. While speculatively executing epoch(i+1), asynchronously replicate 39 * checkpoint(i) to backup host. 40 * 4. When checkpoint_ack(i) is received from backup, release_buffer(i) 41 * Thus, this Qdisc would receive the following sequence of commands: 42 * TCQ_PLUG_BUFFER (epoch i) 43 * .. TCQ_PLUG_BUFFER (epoch i+1) 44 * ....TCQ_PLUG_RELEASE_ONE (epoch i) 45 * ......TCQ_PLUG_BUFFER (epoch i+2) 46 * ........ 47 * 48 * 49 * State of the queue, when used for network output buffering: 50 * 51 * plug(i+1) plug(i) head 52 * ------------------+--------------------+----------------> 53 * | | 54 * | | 55 * pkts_current_epoch| pkts_last_epoch |pkts_to_release 56 * ----------------->|<--------+--------->|+---------------> 57 * v v 58 * 59 * 60 * @{ 61 */ 62 63#include <netlink-private/netlink.h> 64#include <netlink-private/tc.h> 65#include <netlink/netlink.h> 66#include <netlink/utils.h> 67#include <netlink-private/route/tc-api.h> 68#include <netlink/route/qdisc/plug.h> 69 70static int plug_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) 71{ 72 struct rtnl_plug *plug = data; 73 struct tc_plug_qopt opts; 74 75 if (!plug) 76 return -NLE_INVAL; 77 78 opts.action = plug->action; 79 opts.limit = plug->limit; 80 81 return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD); 82} 83 84/** 85 * @name Attribute Modification 86 * @{ 87 */ 88 89/** 90 * Insert a plug into the qdisc and buffer any incoming 91 * network traffic. 92 * @arg qdisc PLUG qdisc to be modified. 93 */ 94int rtnl_qdisc_plug_buffer(struct rtnl_qdisc *qdisc) 95{ 96 struct rtnl_plug *plug; 97 98 if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) 99 return -NLE_NOMEM; 100 101 plug->action = TCQ_PLUG_BUFFER; 102 return 0; 103} 104 105/** 106 * Unplug the qdisc, releasing packets from queue head 107 * to the last complete buffer, while new traffic 108 * continues to be buffered. 109 * @arg qdisc PLUG qdisc to be modified. 110 */ 111int rtnl_qdisc_plug_release_one(struct rtnl_qdisc *qdisc) 112{ 113 struct rtnl_plug *plug; 114 115 if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) 116 return -NLE_NOMEM; 117 118 plug->action = TCQ_PLUG_RELEASE_ONE; 119 return 0; 120} 121 122/** 123 * Indefinitely unplug the qdisc, releasing all packets. 124 * Network traffic will not be buffered until the next 125 * buffer command is issued. 126 * @arg qdisc PLUG qdisc to be modified. 127 */ 128int rtnl_qdisc_plug_release_indefinite(struct rtnl_qdisc *qdisc) 129{ 130 struct rtnl_plug *plug; 131 132 if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) 133 return -NLE_NOMEM; 134 135 plug->action = TCQ_PLUG_RELEASE_INDEFINITE; 136 return 0; 137} 138 139/** 140 * Set limit of PLUG qdisc. 141 * @arg qdisc PLUG qdisc to be modified. 142 * @arg limit New limit. 143 * @return 0 on success or a negative error code. 144 */ 145int rtnl_qdisc_plug_set_limit(struct rtnl_qdisc *qdisc, int limit) 146{ 147 struct rtnl_plug *plug; 148 149 if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) 150 return -NLE_NOMEM; 151 152 plug->action = TCQ_PLUG_LIMIT; 153 plug->limit = limit; 154 155 return 0; 156} 157 158/** @} */ 159 160static struct rtnl_tc_ops plug_ops = { 161 .to_kind = "plug", 162 .to_type = RTNL_TC_TYPE_QDISC, 163 .to_size = sizeof(struct rtnl_plug), 164 .to_msg_fill = plug_msg_fill, 165}; 166 167static void __init plug_init(void) 168{ 169 rtnl_tc_register(&plug_ops); 170} 171 172static void __exit plug_exit(void) 173{ 174 rtnl_tc_unregister(&plug_ops); 175} 176 177/** @} */ 178