Home | History | Annotate | Download | only in qdisc
      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 (at) 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 
     70 static 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  */
     94 int 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  */
    111 int 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  */
    128 int 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  */
    145 int 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 
    160 static 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 
    167 static void __init plug_init(void)
    168 {
    169 	rtnl_tc_register(&plug_ops);
    170 }
    171 
    172 static void __exit plug_exit(void)
    173 {
    174 	rtnl_tc_unregister(&plug_ops);
    175 }
    176 
    177 /** @} */
    178