Z implementčního hlediska to znamená zhruba toto. Po vytvoření fronty (queuing discipline) je ukazatel ve struktuře zařízení (interface) (include/netdevice.h). IP vrstva po přídání nezbytných hlaviček zavolá funkci dev_queue_xmit z net/core/dev.c. Příslušná Část kódu vypadá zhruba takhle:
/* Grab device queue */ spin_lock_bh(&dev->queue_lock); q = dev->qdisc; if (q->enqueue) { int ret = q->enqueue(skb, q); qdisc_run(dev); spin_unlock_bh(&dev->queue_lock); return ret == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : ret; } . . . . if (dev->hard_start_xmit(skb, dev) == 0) {Tahle funkce dokazuje, že dříve než je packet poslán na výstupní interface je uložen do fronty. Linuxová podpora pro QoS se skládá z následujících věcí:
Fronty jsou identifikovány dvojící <major number:minor number>, kde minor number je 0. Tento identifikátor se používá pro propojení tříd s frontami. Fronty musí implementovat následující metody:
static struct sk_buff * pfifo_dequeue(struct Qdisc* sch) { return __skb_dequeue(&sch->q); }
gred_drop(struct Qdisc* sch) { . . . skb = __skb_dequeue_tail(&sch->q); if (skb) { sch->stats.backlog -= skb->len; sch->stats.drops++; q= t->tab[(skb->tc_index&0xf)]; if (q) { q->backlog -= skb->len; q->other++; if (!q->backlog && !t->eqp) PSCHED_GET_TIME(q->qidlestart); } else { D2PRINTK("gred_dequeue: skb has bad tcindex %x\n",skb->tc_index&0xf); } kfree_skb(skb); return 1; } . . .Jak je vidět, funkce pouze zavolá dequeue a pak uvolní po packetu pamět.
static void prio_reset(struct Qdisc* sch) { int prio; struct prio_sched_data *q = (struct prio_sched_data *)sch->data; for (prio=0; priobands; prio++) qdisc_reset(q->queues[prio]); sch->q.qlen = 0; }
Jsou dva způsoby, jak je třída identifikována. Jeden identifikátor zadává uživatel, druhý je interní. Ne všechny fronty podporují třídy. S podporou jsou například CBQ, DS_MARK, CSZ, p-FIFO.
Třídy mají tyto funkce:
static int prio_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, struct Qdisc **old) { struct prio_sched_data *q = (struct prio_sched_data *)sch->data; unsigned long band = arg - 1; if (band >= q->bands) return -EINVAL; if (new == NULL) new = &noop_qdisc; sch_tree_lock(sch); *old = q->queues[band]; q->queues[band] = new; qdisc_reset(*old); sch_tree_unlock(sch); return 0; }Funkce má jako parametr novou frontu.
static __inline__ struct cbq_class * cbq_class_lookup(struct cbq_sched_data *q, u32 classid) { struct cbq_class *cl; for (cl = q->classes[cbq_hash(classid)]; cl; cl = cl->next) if (cl->classid == classid) return cl; return NULL; } static unsigned long cbq_get(struct Qdisc *sch, u32 classid) { struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data; struct cbq_class *cl = cbq_class_lookup(q, classid); if (cl) { cl->refcnt++; return (unsigned long)cl; } return 0; }
cbq_put(struct Qdisc *sch, unsigned long arg) { struct cbq_class *cl = (struct cbq_class*)arg; if (--cl->refcnt == 0) { #ifdef CONFIG_NET_CLS_POLICE struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data; spin_lock_bh(&sch->dev->queue_lock); if (q->rx_class == cl) q->rx_class = NULL; spin_unlock_bh(&sch->dev->queue_lock); #endif cbq_destroy_class(cl); } }
static void csz_walk(struct Qdisc *sch, struct qdisc_walker *arg) { struct csz_sched_data *q = (struct csz_sched_data *)sch->data; int prio = 0; if (arg->stop) return; for (prio = 0; prio < CSZ_FLOWS; prio++) { if (arg->count < arg->skip) { arg->count++; continue; } if (prio < CSZ_GUARANTEED && q->flow[prio].L_tab == NULL) { arg->count++; continue; } if (arg->fn(sch, prio+1, arg) < 0) { arg->stop = 1; break; } arg->count++; } }
struct tcf_proto { /* Fast access part */ struct tcf_proto *next; void *root; int (*classify)(struct sk_buff*, struct tcf_proto*, struct tcf_result *); u32 protocol; /* All the rest */ u32 prio; u32 classid; struct Qdisc *q; void *data; struct tcf_proto_ops *ops;Elementy filteru jsou identifikovány 32-bitovým identifikátorem. Id. 0 je sám filter. Stejně jako třídy, i filtery mají interní ID, které lze získat pomocí funkce get. Strukturu filteru ukazuje následující obrázek. Filter má tyto funkce
Filtery se mohou rozdělit na generické a specifické. U generických je
pouze jedna instance na frontu, u specifických jich může být více.
Usage: tc [ OPTIONS ] OBJECT { COMMAND | help } where OBJECT := { qdisc | class | filter } OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] }
Usage: tc qdisc [ add | del | replace | change | get ] dev STRING [ handle QHANDLE ] [ root | ingress | parent CLASSID ] [ estimator INTERVAL TIME_CONSTANT ] [ [ QDISC_KIND ] [ help | OPTIONS ] ] tc qdisc show [ dev STRING ] [ingress] Where: QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. } OPTIONS := ... try tc qdisc addhelp
Usage: tc class [ add | del | change | get ] dev STRING [ classid CLASSID ] [ root | parent CLASSID ] [ [ QDISC_KIND ] [ help | OPTIONS ] ] tc class show [ dev STRING ] [ root | parent CLASSID ] Where: QDISC_KIND := { prio | cbq | etc. } OPTIONS := ... try tc class addQDISC_KIND je nějaká fronta podporující třídy.help
Usage: tc filter [ add | del | change | get ] dev STRING [ pref PRIO ] [ protocol PROTO ] [ estimator INTERVAL TIME_CONSTANT ] [ root | classid CLASSID ] [ handle FILTERID ] [ [ FILTER_TYPE ] [ help | OPTIONS ] ] tc filter show [ dev STRING ] [ root | parent CLASSID ] Where: FILTER_TYPE := { rsvp | u32 | fw | route | etc. } FILTERID := ... format depends on classifier, see there OPTIONS := ... try tc filter addhelp
tc qdisc [ add | del | replace | change | get ] dev STRING\ cbq bandwidth BPS [ avpkt BYTES] [ mpu BYTES ] [ cell BYTES ] [ ewma LOG ]Význam jednotlivých položek je následující:
tc qdisc [ add | del | replace | change ] cbq bandwidth BPS rate BPS maxburst PKTS \ [ avpkt BYTES ] [ minburst PKTS ] [ bounded ] [ isolated ] [ allot BYTES ] \ [ mpu BYTES ] [ weight RATE ] [ prio NUMBER ] [ cell BYTES ] [ ewma LOG ] \ [ estimator INTERVAL TINE_CONSTANT ] [ split CLASSID ] [ defmap MASK/CHANGE ]Kde políčka zanmenají následující:
tc filter [ add | del | change | get ] dev STRING [ parent PARENTID ] [ protocol PROTO ] [ PRIORITY ] route Whete: PROTO = { ip | icmp | etc. }
# Pripojeni fronty na eth0. Sirka pasma je 10Mbps tc qdisc add dev eth0 root handle 1: cbq bandwidth 10Mbit allot 1514 cell 8 avpkt 1000 mpu 64 # Pridame tridu fronty. Koren ma plnych 10Mbps tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 10Mbit rate 10Mbit allot 1514 cell 8 weight 1Mbit prio 8 maxburst 20 avpkt 1000 # Tok na B tc class add dev eth0 parent 1:1 classid 1:2 cbq bandwidth 10Mbit rate 4Mbit allot 1514 cell 8 weight 400Kbit prio 4 maxburst 20 avpkt 1000 bounded # Tok na C tc cladd add dev eth0 dev parent 1:1 classid 1:3 cbq bandwidth 10Mbit rate 6Mbit allot 1514 cell 8 weight 600Kbit prio 4 maxburst 20 avpkt 1000 bounded # Pripojeni dalsiho CBQ na toky na B tc qdisc add dev eth0 parent 1:2 handle 2: cbq bandwidth 4Mbit alot 1514 cell 8 avpkt 1000 mpu 64 # Pripojeni dalsiho CBQ na toky na C tc qdisc add dev eth0 parent 1:3 handle 3: cbq bandwidth 6Mbit alot 1514 cell 8 avpkt 1000 mpu 64 # Nastaveni trid pro aplikace na B tc class add dev eth0 parent 2:0 classid 2:1 cbq bandwidth 4Mbit rate 4Mbit allot 1514 cell 8 weight 400Kbit prio 4 maxburst 20 avpkt 1000 bounded tc class add dev eth0 parent 2:1 classid 2:2 cbq bandwidth 4Mbit rate 3Mbit allot 1514 cell 8 weight 300Kbit prio 1 maxburst 20 avpkt 1000 bounded tc class add dev eth0 parent 2:1 classid 2:3 cbq bandwidth 4Mbit rate 1Mbit allot 1514 cell 8 weight 100Kbit prio 2 maxburst 20 avpkt 1000 bounded # Nastaveni trid pro aplikace na C tc class add dev eth0 parent 3:0 classid 3:1 cbq bandwidth 6Mbit rate 6Mbit allot 1514 cell 8 weight 600Kbit prio 7 maxburst 20 avpkt 1000 bounded tc class add dev eth0 parent 3:1 classid 3:2 cbq bandwidth 6Mbit rate 4Mbit allot 1514 cell 8 weight 400Kbit prio 1 maxburst 20 avpkt 1000 bounded tc class add dev eth0 parent 3:1 classid 3:3 cbq bandwidth 6Mbit rate 2Mbit allot 1514 cell 8 weight 200Kbit prio 2 maxburst 20 avpkt 1000 bounded # Instalace router classifier na eth0 tc filter add dev eth0 parent 1:0 protocol ip prio 100 route # Nastavuji routy pro B a C a pravidla pro jejich toky ip route 192.168.1.11 via 192.168.1.1 flow 1:2 ip route 192.168.1.21 via 192.168.1.1 flow 1:3 # Instaluji u32 classifier pro B tc filter add dev eth0 parent 2:0 prio 4 protocol ip u32 # Vytvareni hash tabulky classifeiru pro packety na B tc filter add dev eth0 parent 2:0 prio 4 handle 1: u32 divisor 256 tc filter add dev eth0 parent 2:0 prio 4 handle 2: u32 divisor 256 # Konfigiruji 6. slot tabulky, aby vybral packet urceny pro port 1111 (0x0457) a posla je do tridy 2:2 tc filter add dev eth0 parent 2:0 prio 4 u32 ht 1:6: match ip dst 192.168.1.11 match tcp dst 0x0457 0xffff flowid 2:2 # Ted to same pro port 1112 tc filter add dev eth0 parent 2:0 prio 4 u32 ht 2:6: match ip dst 192.168.1.11 match tcp dst 0x0458 0xffff flowid 2:3 # Projdi hash table a pokud neni fragmentovana, pouzij protokol jako hashovaci klic. tc filter add dev eth0 parent 2:0 prio 4 handle ::1 u32 ht 800:: match ip nofrag offset mask 0x0f00 shift 6 hashkey mask 0x00ff0000 at 8 link 1: tc filter add dev eth0 parent 2:0 prio 4 handle ::1 u32 ht 800:: match ip nofrag offset mask 0x0f00 shift 6 hashkey mask 0x00ff0000 at 8 link 2: # Instaluji u32 classifier pro B tc filter add dev eth0 parent 3:0 prio 7 protocol ip u32 # Vytvareni hash tabulky classifeiru pro packety na B tc filter add dev eth0 parent 3:0 prio 7 handle 1: u32 divisor 256 tc filter add dev eth0 parent 3:0 prio 7 handle 2: u32 divisor 256 # Konfigiruji 6. slot tabulky, aby vybral packet urceny pro port 1113 (0x0459) a posla je do tridy 2:2 tc filter add dev eth0 parent 3:0 prio 7 u32 ht 1:6: match ip dst 192.168.1.21 match tcp dst 0x0459 0xffff flowid 3:2 # Ted to same pro port 1114 tc filter add dev eth0 parent 3:0 prio 7 u32 ht 2:6: match ip dst 192.168.1.21 match tcp dst 0x045a 0xffff flowid 3:3 # Projdi hash table a pokud neni fragmentovana, pouzij protokol jako hashovaci klic. tc filter add dev eth0 parent 3:0 prio 7 handle ::1 u32 ht 800:: match ip nofrag offset mask 0x0f00 shift 6 hashkey mask 0x00ff0000 at 8 link 1: tc filter add dev eth0 parent 3:0 prio 7 handle ::1 u32 ht 800:: match ip nofrag offset mask 0x0f00 shift 6 hashkey mask 0x00ff0000 at 8 link 2: