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