• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
plug_msg_fill(struct rtnl_tc * tc,void * data,struct nl_msg * msg)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  */
rtnl_qdisc_plug_buffer(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_plug_release_one(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_plug_release_indefinite(struct rtnl_qdisc * qdisc)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  */
rtnl_qdisc_plug_set_limit(struct rtnl_qdisc * qdisc,int limit)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 
plug_init(void)167 static void __init plug_init(void)
168 {
169 	rtnl_tc_register(&plug_ops);
170 }
171 
plug_exit(void)172 static void __exit plug_exit(void)
173 {
174 	rtnl_tc_unregister(&plug_ops);
175 }
176 
177 /** @} */
178