hostdev: Introduce virDomainHostdevSubsysSCSIiSCSI
[libvirt.git] / src / util / virnetdevbandwidth.c
1 /*
2  * Copyright (C) 2009-2013 Red Hat, Inc.
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; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library.  If not, see
16  * <http://www.gnu.org/licenses/>.
17  *
18  * Authors:
19  *     Michal Privoznik <mprivozn@redhat.com>
20  *     Daniel P. Berrange <berrange@redhat.com>
21  */
22
23 #include <config.h>
24
25 #include "virnetdevbandwidth.h"
26 #include "vircommand.h"
27 #include "viralloc.h"
28 #include "virerror.h"
29 #include "virstring.h"
30
31 #define VIR_FROM_THIS VIR_FROM_NONE
32
33 void
34 virNetDevBandwidthFree(virNetDevBandwidthPtr def)
35 {
36     if (!def)
37         return;
38
39     VIR_FREE(def->in);
40     VIR_FREE(def->out);
41     VIR_FREE(def);
42 }
43
44
45 /**
46  * virNetDevBandwidthSet:
47  * @ifname: on which interface
48  * @bandwidth: rates to set (may be NULL)
49  * @hierarchical_class: whether to create hierarchical class
50  *
51  * This function enables QoS on specified interface
52  * and set given traffic limits for both, incoming
53  * and outgoing traffic. Any previous setting get
54  * overwritten. If @hierarchical_class is TRUE, create
55  * hierarchical class. It is used to guarantee minimal
56  * throughput ('floor' attribute in NIC).
57  *
58  * Return 0 on success, -1 otherwise.
59  */
60 int
61 virNetDevBandwidthSet(const char *ifname,
62                       virNetDevBandwidthPtr bandwidth,
63                       bool hierarchical_class)
64 {
65     int ret = -1;
66     virCommandPtr cmd = NULL;
67     char *average = NULL;
68     char *peak = NULL;
69     char *burst = NULL;
70
71     if (!bandwidth) {
72         /* nothing to be enabled */
73         ret = 0;
74         goto cleanup;
75     }
76
77     virNetDevBandwidthClear(ifname);
78
79     if (bandwidth->in && bandwidth->in->average) {
80         if (virAsprintf(&average, "%llukbps", bandwidth->in->average) < 0)
81             goto cleanup;
82         if (bandwidth->in->peak &&
83             (virAsprintf(&peak, "%llukbps", bandwidth->in->peak) < 0))
84             goto cleanup;
85         if (bandwidth->in->burst &&
86             (virAsprintf(&burst, "%llukb", bandwidth->in->burst) < 0))
87             goto cleanup;
88
89         cmd = virCommandNew(TC);
90         virCommandAddArgList(cmd, "qdisc", "add", "dev", ifname, "root",
91                              "handle", "1:", "htb", "default",
92                              hierarchical_class ? "2" : "1", NULL);
93         if (virCommandRun(cmd, NULL) < 0)
94             goto cleanup;
95
96         /* If we are creating a hierarchical class, all non guaranteed traffic
97          * goes to the 1:2 class which will adjust 'rate' dynamically as NICs
98          * with guaranteed throughput are plugged and unplugged. Class 1:1
99          * exists so we don't exceed the maximum limit for the network. For each
100          * NIC with guaranteed throughput a separate classid will be created.
101          * NB '1:' is just a shorter notation of '1:0'.
102          *
103          * To get a picture how this works:
104          *
105          * +-----+     +---------+     +-----------+      +-----------+     +-----+
106          * |     |     |  qdisc  |     | class 1:1 |      | class 1:2 |     |     |
107          * | NIC |     | def 1:2 |     |   rate    |      |   rate    |     | sfq |
108          * |     | --> |         | --> |   peak    | -+-> |   peak    | --> |     |
109          * +-----+     +---------+     +-----------+  |   +-----------+     +-----+
110          *                                            |
111          *                                            |   +-----------+     +-----+
112          *                                            |   | class 1:3 |     |     |
113          *                                            |   |   rate    |     | sfq |
114          *                                            +-> |   peak    | --> |     |
115          *                                            |   +-----------+     +-----+
116          *                                           ...
117          *                                            |   +-----------+     +-----+
118          *                                            |   | class 1:n |     |     |
119          *                                            |   |   rate    |     | sfq |
120          *                                            +-> |   peak    | --> |     |
121          *                                                +-----------+     +-----+
122          *
123          * After the routing decision, when is it clear a packet is to be sent
124          * via a particular NIC, it is sent to the root qdisc (queueing
125          * discipline). In this case HTB (Hierarchical Token Bucket). It has
126          * only one direct child class (with id 1:1) which shapes the overall
127          * rate that is sent through the NIC.  This class has at least one child
128          * (1:2) which is meant for all non-privileged (non guaranteed) traffic
129          * from all domains. Then, for each interface with guaranteed
130          * throughput, a separate class (1:n) is created. Imagine a class is a
131          * box. Whenever a packet ends up in a class it is stored in this box
132          * until the kernel sends it, then it is removed from box. Packets are
133          * placed into boxes based on rules (filters) - e.g. depending on
134          * destination IP/MAC address. If there is no rule to be applied, the
135          * root qdisc has a default where such packets go (1:2 in this case).
136          * Packets come in over and over again and boxes get filled more and
137          * more. Imagine that kernel sends packets just once a second. So it
138          * starts to traverse through this tree. It starts with the root qdisc
139          * and through 1:1 it gets to 1:2. It sends packets up to 1:2's 'rate'.
140          * Then it moves to 1:3 and again sends packets up to 1:3's 'rate'.  The
141          * whole process is repeated until 1:n is processed. So now we have
142          * ensured each class its guaranteed bandwidth. If the sum of sent data
143          * doesn't exceed the 'rate' in 1:1 class, we can go further and send
144          * more packets. The rest of available bandwidth is distributed to the
145          * 1:2,1:3...1:n classes by ratio of their 'rate'. As soon as the root
146          * 'rate' limit is reached or there are no more packets to send, we stop
147          * sending and wait another second. Each class has an SFQ qdisc which
148          * shuffles packets in boxes stochastically, so one sender cannot
149          * starve others.
150          *
151          * Therefore, whenever we want to plug in a new guaranteed interface, we
152          * need to create a new class and adjust the 'rate' of the 1:2 class.
153          * When unplugging we do the exact opposite - remove the associated
154          * class, and adjust the 'rate'.
155          *
156          * This description is rather long, but it is still a good idea to read
157          * it before you dig into the code.
158          */
159         if (hierarchical_class) {
160             virCommandFree(cmd);
161             cmd = virCommandNew(TC);
162             virCommandAddArgList(cmd, "class", "add", "dev", ifname, "parent",
163                                  "1:", "classid", "1:1", "htb", "rate", average,
164                                  "ceil", peak ? peak : average, NULL);
165             if (virCommandRun(cmd, NULL) < 0)
166                 goto cleanup;
167         }
168         virCommandFree(cmd);
169         cmd = virCommandNew(TC);
170         virCommandAddArgList(cmd, "class", "add", "dev", ifname, "parent",
171                              hierarchical_class ? "1:1" : "1:", "classid",
172                              hierarchical_class ? "1:2" : "1:1", "htb",
173                              "rate", average, NULL);
174
175         if (peak)
176             virCommandAddArgList(cmd, "ceil", peak, NULL);
177         if (burst)
178             virCommandAddArgList(cmd, "burst", burst, NULL);
179
180         if (virCommandRun(cmd, NULL) < 0)
181             goto cleanup;
182
183         virCommandFree(cmd);
184         cmd = virCommandNew(TC);
185         virCommandAddArgList(cmd, "qdisc", "add", "dev", ifname, "parent",
186                              hierarchical_class ? "1:2" : "1:1",
187                              "handle", "2:", "sfq", "perturb",
188                              "10", NULL);
189
190         if (virCommandRun(cmd, NULL) < 0)
191             goto cleanup;
192
193         virCommandFree(cmd);
194         cmd = virCommandNew(TC);
195         virCommandAddArgList(cmd, "filter", "add", "dev", ifname, "parent",
196                              "1:0", "protocol", "ip", "handle", "1", "fw",
197                              "flowid", "1", NULL);
198
199         if (virCommandRun(cmd, NULL) < 0)
200             goto cleanup;
201
202         VIR_FREE(average);
203         VIR_FREE(peak);
204         VIR_FREE(burst);
205     }
206
207     if (bandwidth->out) {
208         if (virAsprintf(&average, "%llukbps", bandwidth->out->average) < 0)
209             goto cleanup;
210         if (virAsprintf(&burst, "%llukb", bandwidth->out->burst ?
211                         bandwidth->out->burst : bandwidth->out->average) < 0)
212             goto cleanup;
213
214         virCommandFree(cmd);
215         cmd = virCommandNew(TC);
216             virCommandAddArgList(cmd, "qdisc", "add", "dev", ifname,
217                                  "ingress", NULL);
218
219         if (virCommandRun(cmd, NULL) < 0)
220             goto cleanup;
221
222         virCommandFree(cmd);
223         cmd = virCommandNew(TC);
224         virCommandAddArgList(cmd, "filter", "add", "dev", ifname, "parent",
225                              "ffff:", "protocol", "ip", "u32", "match", "ip",
226                              "src", "0.0.0.0/0", "police", "rate", average,
227                              "burst", burst, "mtu", "64kb", "drop", "flowid",
228                              ":1", NULL);
229
230         if (virCommandRun(cmd, NULL) < 0)
231             goto cleanup;
232     }
233
234     ret = 0;
235
236 cleanup:
237     virCommandFree(cmd);
238     VIR_FREE(average);
239     VIR_FREE(peak);
240     VIR_FREE(burst);
241     return ret;
242 }
243
244 /**
245  * virNetDevBandwidthClear:
246  * @ifname: on which interface
247  *
248  * This function tries to disable QoS on specified interface
249  * by deleting root and ingress qdisc. However, this may fail
250  * if we try to remove the default one.
251  *
252  * Return 0 on success, -1 otherwise.
253  */
254 int
255 virNetDevBandwidthClear(const char *ifname)
256 {
257     int ret = 0;
258     int dummy; /* for ignoring the exit status */
259     virCommandPtr cmd = NULL;
260
261     cmd = virCommandNew(TC);
262     virCommandAddArgList(cmd, "qdisc", "del", "dev", ifname, "root", NULL);
263
264     if (virCommandRun(cmd, &dummy) < 0)
265         ret = -1;
266
267     virCommandFree(cmd);
268
269     cmd = virCommandNew(TC);
270     virCommandAddArgList(cmd, "qdisc",  "del", "dev", ifname, "ingress", NULL);
271
272     if (virCommandRun(cmd, &dummy) < 0)
273         ret = -1;
274
275     virCommandFree(cmd);
276
277     return ret;
278 }
279
280 /*
281  * virNetDevBandwidthCopy:
282  * @dest: destination
283  * @src:  source (may be NULL)
284  *
285  * Returns -1 on OOM error (which gets reported),
286  * 0 otherwise.
287  */
288 int
289 virNetDevBandwidthCopy(virNetDevBandwidthPtr *dest,
290                        const virNetDevBandwidth *src)
291 {
292     int ret = -1;
293
294     *dest = NULL;
295     if (!src) {
296         /* nothing to be copied */
297         return 0;
298     }
299
300     if (VIR_ALLOC(*dest) < 0)
301         goto cleanup;
302
303     if (src->in) {
304         if (VIR_ALLOC((*dest)->in) < 0)
305             goto cleanup;
306         memcpy((*dest)->in, src->in, sizeof(*src->in));
307     }
308
309     if (src->out) {
310         if (VIR_ALLOC((*dest)->out) < 0) {
311             VIR_FREE((*dest)->in);
312             goto cleanup;
313         }
314         memcpy((*dest)->out, src->out, sizeof(*src->out));
315     }
316
317     ret = 0;
318
319 cleanup:
320     if (ret < 0) {
321         virNetDevBandwidthFree(*dest);
322         *dest = NULL;
323     }
324     return ret;
325 }
326
327 bool
328 virNetDevBandwidthEqual(virNetDevBandwidthPtr a,
329                         virNetDevBandwidthPtr b)
330 {
331     if (!a && !b)
332         return true;
333
334     if (!a || !b)
335         return false;
336
337     /* in */
338     if (a->in) {
339         if (!b->in)
340             return false;
341
342         if (a->in->average != b->in->average ||
343             a->in->peak != b->in->peak ||
344             a->in->burst != b->in->burst)
345             return false;
346     } else if (b->in) {
347         return false;
348     }
349
350     /*out*/
351     if (a->out) {
352         if (!b->out)
353             return false;
354
355         if (a->out->average != b->out->average ||
356             a->out->peak != b->out->peak ||
357             a->out->burst != b->out->burst)
358             return false;
359     } else if (b->out) {
360         return false;
361     }
362
363     return true;
364 }
365
366 /*
367  * virNetDevBandwidthPlug:
368  * @brname: name of the bridge
369  * @net_bandwidth: QoS settings on @brname
370  * @ifmac: MAC of interface
371  * @bandwidth: QoS settings for interface
372  * @id: unique ID (MUST be greater than 2)
373  *
374  * Set bridge part of interface QoS settings, e.g. guaranteed
375  * bandwidth.  @id is an unique ID (among @brname) from which
376  * other identifiers for class, qdisc and filter are derived.
377  * However, two classes were already set up (by
378  * virNetDevBandwidthSet). That's why this @id MUST be greater
379  * than 2. You may want to keep passed @id, as it is used later
380  * by virNetDevBandwidthUnplug.
381  *
382  * Returns:
383  * 0 if QoS set successfully
384  * -1 otherwise.
385  */
386 int
387 virNetDevBandwidthPlug(const char *brname,
388                        virNetDevBandwidthPtr net_bandwidth,
389                        const virMacAddr *ifmac_ptr,
390                        virNetDevBandwidthPtr bandwidth,
391                        unsigned int id)
392 {
393     int ret = -1;
394     virCommandPtr cmd = NULL;
395     char *class_id = NULL;
396     char *qdisc_id = NULL;
397     char *filter_id = NULL;
398     char *floor = NULL;
399     char *ceil = NULL;
400     unsigned char ifmac[VIR_MAC_BUFLEN];
401     char ifmacStr[VIR_MAC_STRING_BUFLEN];
402     char *mac[2] = {NULL, NULL};
403
404     if (id <= 2) {
405         virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid class ID %d"), id);
406         return -1;
407     }
408
409     virMacAddrGetRaw(ifmac_ptr, ifmac);
410     virMacAddrFormat(ifmac_ptr, ifmacStr);
411
412     if (!net_bandwidth || !net_bandwidth->in) {
413         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
414                        _("Bridge '%s' has no QoS set, therefore "
415                          "unable to set 'floor' on '%s'"),
416                        brname, ifmacStr);
417         return -1;
418     }
419
420     if (virAsprintf(&class_id, "1:%x", id) < 0 ||
421         virAsprintf(&qdisc_id, "%x:", id) < 0 ||
422         virAsprintf(&filter_id, "%u", id) < 0 ||
423         virAsprintf(&mac[0], "0x%02x%02x%02x%02x", ifmac[2],
424                     ifmac[3], ifmac[4], ifmac[5]) < 0 ||
425         virAsprintf(&mac[1], "0x%02x%02x", ifmac[0], ifmac[1]) < 0 ||
426         virAsprintf(&floor, "%llukbps", bandwidth->in->floor) < 0 ||
427         virAsprintf(&ceil, "%llukbps", net_bandwidth->in->peak ?
428                     net_bandwidth->in->peak :
429                     net_bandwidth->in->average) < 0)
430         goto cleanup;
431
432     cmd = virCommandNew(TC);
433     virCommandAddArgList(cmd, "class", "add", "dev", brname, "parent", "1:1",
434                          "classid", class_id, "htb", "rate", floor,
435                          "ceil", ceil, NULL);
436
437     if (virCommandRun(cmd, NULL) < 0)
438         goto cleanup;
439
440     virCommandFree(cmd);
441     cmd = virCommandNew(TC);
442     virCommandAddArgList(cmd, "qdisc", "add", "dev", brname, "parent",
443                          class_id, "handle", qdisc_id, "sfq", "perturb",
444                          "10", NULL);
445
446     if (virCommandRun(cmd, NULL) < 0)
447         goto cleanup;
448
449     virCommandFree(cmd);
450     cmd = virCommandNew(TC);
451     /* Okay, this not nice. But since libvirt does not know anything about
452      * interface IP address(es), and tc fw filter simply refuse to use ebtables
453      * marks, we need to use u32 selector to match MAC address.
454      * If libvirt will ever know something, remove this FIXME
455      */
456     virCommandAddArgList(cmd, "filter", "add", "dev", brname, "protocol", "ip",
457                          "prio", filter_id, "u32",
458                          "match", "u16", "0x0800", "0xffff", "at", "-2",
459                          "match", "u32", mac[0], "0xffffffff", "at", "-12",
460                          "match", "u16", mac[1], "0xffff", "at", "-14",
461                          "flowid", class_id, NULL);
462
463     if (virCommandRun(cmd, NULL) < 0)
464         goto cleanup;
465
466     ret = 0;
467
468 cleanup:
469     VIR_FREE(mac[1]);
470     VIR_FREE(mac[0]);
471     VIR_FREE(ceil);
472     VIR_FREE(floor);
473     VIR_FREE(filter_id);
474     VIR_FREE(qdisc_id);
475     VIR_FREE(class_id);
476     virCommandFree(cmd);
477     return ret;
478 }
479
480 /*
481  * virNetDevBandwidthUnplug:
482  * @brname: from which bridge are we unplugging
483  * @id: unique identifier (MUST be greater than 2)
484  *
485  * Remove QoS settings from bridge.
486  *
487  * Returns 0 on success, -1 otherwise.
488  */
489 int
490 virNetDevBandwidthUnplug(const char *brname,
491                          unsigned int id)
492 {
493     int ret = -1;
494     int cmd_ret = 0;
495     virCommandPtr cmd = NULL;
496     char *class_id = NULL;
497     char *qdisc_id = NULL;
498     char *filter_id = NULL;
499
500     if (id <= 2) {
501         virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid class ID %d"), id);
502         return -1;
503     }
504
505     if (virAsprintf(&class_id, "1:%x", id) < 0 ||
506         virAsprintf(&qdisc_id, "%x:", id) < 0 ||
507         virAsprintf(&filter_id, "%u", id) < 0)
508         goto cleanup;
509
510     cmd = virCommandNew(TC);
511     virCommandAddArgList(cmd, "qdisc", "del", "dev", brname,
512                          "handle", qdisc_id, NULL);
513
514     /* Don't threat tc errors as fatal, but
515      * try to remove as much as possible */
516     if (virCommandRun(cmd, &cmd_ret) < 0)
517         goto cleanup;
518
519     virCommandFree(cmd);
520     cmd = virCommandNew(TC);
521     virCommandAddArgList(cmd, "filter", "del", "dev", brname,
522                          "prio", filter_id, NULL);
523
524     if (virCommandRun(cmd, &cmd_ret) < 0)
525         goto cleanup;
526
527     virCommandFree(cmd);
528     cmd = virCommandNew(TC);
529     virCommandAddArgList(cmd, "class", "del", "dev", brname,
530                          "classid", class_id, NULL);
531
532     if (virCommandRun(cmd, &cmd_ret) < 0)
533         goto cleanup;
534
535     ret = 0;
536
537 cleanup:
538     VIR_FREE(filter_id);
539     VIR_FREE(qdisc_id);
540     VIR_FREE(class_id);
541     virCommandFree(cmd);
542     return ret;
543 }
544
545 /**
546  * virNetDevBandwidthUpdateRate:
547  * @ifname: interface name
548  * @classid: ID of class to update
549  * @new_rate: new rate
550  *
551  * This function updates the 'rate' attribute of HTB class.
552  * It can be used whenever a new interface is plugged to a
553  * bridge to adjust average throughput of non guaranteed
554  * NICs.
555  *
556  * Returns 0 on success, -1 otherwise.
557  */
558 int
559 virNetDevBandwidthUpdateRate(const char *ifname,
560                              const char *class_id,
561                              virNetDevBandwidthPtr bandwidth,
562                              unsigned long long new_rate)
563 {
564     int ret = -1;
565     virCommandPtr cmd = NULL;
566     char *rate = NULL;
567     char *ceil = NULL;
568
569     if (virAsprintf(&rate, "%llukbps", new_rate) < 0 ||
570         virAsprintf(&ceil, "%llukbps", bandwidth->in->peak ?
571                     bandwidth->in->peak :
572                     bandwidth->in->average) < 0)
573         goto cleanup;
574
575     cmd = virCommandNew(TC);
576     virCommandAddArgList(cmd, "class", "change", "dev", ifname,
577                          "classid", class_id, "htb", "rate", rate,
578                          "ceil", ceil, NULL);
579
580     if (virCommandRun(cmd, NULL) < 0)
581         goto cleanup;
582
583     ret = 0;
584
585 cleanup:
586     virCommandFree(cmd);
587     VIR_FREE(rate);
588     VIR_FREE(ceil);
589     return ret;
590 }