{
    "content": [
        {
            "type": "text",
            "text": "# tc-hfsc(7) (man)\n\n**Summary:** tc-hfcs - Hierarchical Fair Service Curve\n\n## See Also\n\n- tc(8)\n- tc-stab(8)\n\n## Section Outline\n\n- **NAME** (2 lines) — 1 subsections\n  - HISTORY & INTRODUCTION (36 lines)\n- **ABBREVIATIONS** (7 lines)\n- **BASICS OF HFSC** (81 lines)\n- **REALTIME CRITERION** (48 lines)\n- **LINKSHARING CRITERION** (74 lines)\n- **UPPERLIMIT CRITERION** (36 lines) — 1 subsections\n  - SEPARATE LS / RT SCs (4 lines)\n- **CORNER CASES** (53 lines)\n- **LINUX AND TIMER RESOLUTION** (74 lines) — 1 subsections\n  - CAVEAT: RANDOM ONLINE EXAMPLES (17 lines)\n- **LAYER2 ADAPTATION** (2 lines)\n- **SEE ALSO** (4 lines)\n- **AUTHOR** (5 lines)\n\n## Full Content\n\n### NAME\n\ntc-hfcs - Hierarchical Fair Service Curve\n\n#### HISTORY & INTRODUCTION\n\nHFSC  (Hierarchical  Fair  Service  Curve)  is a network packet scheduling algorithm that was\nfirst presented at SIGCOMM'97. Developed as a part of ALTQ (ALTernative Queuing)  on  NetBSD,\nfound its way quickly to other BSD systems, and then a few years ago became part of the linux\nkernel. Still, it's not the most popular scheduling algorithm - especially if compared to HTB\n-  and  it's  not well documented for the enduser. This introduction aims to explain how HFSC\nworks without using too much math (although some math it will be inevitable).\n\nIn short HFSC aims to:\n\n1)  guarantee precise bandwidth and delay allocation for all leaf classes (realtime  cri‐\nterion)\n\n2)  allocate  excess bandwidth fairly as specified by class hierarchy (linkshare & upper‐\nlimit criterion)\n\n3)  minimize any discrepancy between the service curve and the actual amount  of  service\nprovided during linksharing\n\nThe main \"selling\" point of HFSC is feature (1), which is achieved by using nonlinear service\ncurves (more about what it actually is later). This is particularly useful in VoIP or  games,\nwhere  not  only a guarantee of consistent bandwidth is important, but also limiting the ini‐\ntial delay of a data stream. Note that it matters only for leaf  classes  (where  the  actual\nqueues are) - thus class hierarchy is ignored in the realtime case.\n\nFeature  (2)  is well, obvious - any algorithm featuring class hierarchy (such as HTB or CBQ)\nstrives to achieve that. HFSC does that well, although you might end with unusual situations,\nif you define service curves carelessly - see section CORNER CASES for examples.\n\nFeature (3) is mentioned due to the nature of the problem. There may be situations where it's\neither not possible to guarantee service of all curves at the same time, and/or it's impossi‐\nble  to do so fairly. Both will be explained later. Note that this is mainly related to inte‐\nrior (aka aggregate) classes, as the leafs are already handled by (1). Still, it's  perfectly\npossible to create a leaf class without realtime service, and in such a case the caveats will\nnaturally extend to leaf classes as well.\n\n### ABBREVIATIONS\n\nFor the remaining part of the document, we'll use following shortcuts:\n\nRT - realtime\nLS - linkshare\nUL - upperlimit\nSC - service curve\n\n### BASICS OF HFSC\n\nTo understand how HFSC works, we must first introduce a service curve.  Overall, it's a  non‐\ndecreasing  function  of some time unit, returning the amount of service (an allowed or allo‐\ncated amount of bandwidth) at some specific point in time. The purpose of it should  be  sub‐\nconsciously obvious: if a class was allowed to transfer not less than the amount specified by\nits service curve, then the service curve is not violated.\n\nStill, we need more elaborate criterion than just the above (although  in  the  most  generic\ncase it can be reduced to it). The criterion has to take two things into account:\n\n•   idling periods\n\n•   the  ability  to \"look back\", so if during current active period the service curve is\nviolated, maybe it isn't if we count excess bandwidth received during earlier  active\nperiod(s)\n\nLet's define the criterion as follows:\n\n(1) For each t1, there must exist t0 in set B, so S(t1-t0) <= w(t0,t1)\n\nHere  'w' denotes the amount of service received during some time period between t0 and t1. B\nis a set of all times, where a session becomes active after idling period (further denoted as\n'becoming backlogged'). For a clearer picture, imagine two situations:\n\na)  our session was active during two periods, with a small time gap between them\n\nb)  as in (a), but with a larger gap\n\nConsider  (a):  if  the service received during both periods meets (1), then all is well. But\nwhat if it doesn't do so during the 2nd period? If the amount of service received during  the\n1st  period  is  larger  than the service curve, then it might compensate for smaller service\nduring the 2nd period and the gap - if the gap is small enough.\n\nIf the gap is larger (b) - then it's less likely to happen (unless the excess bandwidth allo‐\ncated during the 1st part was really large). Still, the larger the gap - the less interesting\nis what happened in the past (e.g. 10 minutes ago) - what matters is the current traffic that\njust started.\n\nFrom HFSC's perspective, more interesting is answering the following question: when should we\nstart transferring packets, so a service curve of a class is not violated. Or rephrasing  it:\nHow  much  X()  amount of service should a session receive by time t, so the service curve is\nnot violated. Function X() defined as below is the basic building block of HFSC, used in: el‐\nigible,  deadline,  virtual-time and fit-time curves. Of course, X() is based on equation (1)\nand is defined recursively:\n\n\n•   At the 1st backlogged period beginning function X is initialized to  generic  service\ncurve assigned to a class\n\n•   At any subsequent backlogged period, X() is:\nmin(X() from previous period ; w(t0)+S(t-t0) for t>=t0),\n... where t0 denotes the beginning of the current backlogged period.\n\nHFSC  uses  either linear, or two-piece linear service curves. In case of linear or two-piece\nlinear convex functions (first slope < second slope), min() in X's definition reduces to  the\n2nd  argument. But in case of two-piece concave functions, the 1st argument might quickly be‐\ncome lesser for some t>=t0. Note, that for some backlogged period, X() is defined  only  from\nthat  period's  beginning. We also define X^(-1)(w) as smallest t>=t0, for which X(t) = w. We\nhave to define it this way, as X() is usually not an injection.\n\nThe above generic X() can be one of the following:\n\nE() In realtime criterion, selects packets eligible for sending. If  none  are  eligible,\nHFSC will use linkshare criterion. Eligible time 'et' is calculated with reference to\npackets' heads ( et = E^(-1)(w) ). It's based on RT service curve, but in case  of  a\nconvex curve, uses its 2nd slope only.\n\nD() In  realtime criterion, selects the most suitable packet from the ones chosen by E().\nDeadline time 'dt' corresponds to packets' tails (dt  =  D^(-1)(w+l),  where  'l'  is\npacket's length). Based on RT service curve.\n\nV() In  linkshare criterion, arbitrates which packet to send next. Note that V() is func‐\ntion of a virtual time - see LINKSHARE CRITERION section for  details.  Virtual  time\n'vt' corresponds to packets' heads (vt = V^(-1)(w)). Based on LS service curve.\n\nF() An extension to linkshare criterion, used to limit at which speed linkshare criterion\nis  allowed  to  dequeue.  Fit-time  'ft'  corresponds  to  packets'  heads  as  well\n(ft = F^(-1)(w)). Based on UL service curve.\n\nBe  sure  to  make  clean  distinction between session's RT, LS and UL service curves and the\nabove \"utility\" functions.\n\n### REALTIME CRITERION\n\nRT criterion ignores class hierarchy and guarantees precise bandwidth and  delay  allocation.\nWe  say  that  a packet is eligible for sending, when the current real time is later than the\neligible time of the packet. From all eligible packets, the one most suited  for  sending  is\nthe one with the shortest deadline time. This sounds simple, but consider the following exam‐\nple:\n\nInterface 10Mbit, two classes, both with two-piece linear service curves:\n\n•   1st class - 2Mbit for 100ms, then 7Mbit (convex - 1st slope < 2nd slope)\n\n•   2nd class - 7Mbit for 100ms, then 2Mbit (concave - 1st slope > 2nd slope)\n\nAssume for a moment, that we only use D() for both finding eligible packets, and choosing the\nmost  fitting  one, thus eligible time would be computed as D^(-1)(w) and deadline time would\nbe computed as D^(-1)(w+l). If the 2nd class starts sending packets 1 second  after  the  1st\nclass,  it's  of  course  impossible to guarantee 14Mbit, as the interface capability is only\n10Mbit.  The only workaround in this scenario is to allow the 1st class to send  the  packets\nearlier  that would normally be allowed. That's where separate E() comes to help. Putting all\nthe math aside (see HFSC paper for details), E() for RT concave service curve  is  just  like\nD(), but for the RT convex service curve - it's constructed using only RT service curve's 2nd\nslope (in our example\n7Mbit).\n\nThe effect of such E() - packets will be sent earlier, and at the same time D() will  be  up‐\ndated  -  so  the  current deadline time calculated from it will be later. Thus, when the 2nd\nclass starts sending packets later, both the 1st and the 2nd class will be eligible, but  the\n2nd  session's deadline time will be smaller and its packets will be sent first. When the 1st\nclass becomes idle at some later point, the 2nd class will be able to \"buffer\" up  again  for\nlater active period of the 1st class.\n\nA  short remark - in a situation, where the total amount of bandwidth available on the inter‐\nface is larger than the allocated total realtime parts (imagine  a  10  Mbit  interface,  but\n1Mbit/2Mbit  and 2Mbit/1Mbit classes), the sole speed of the interface could suffice to guar‐\nantee the times.\n\nImportant part of RT criterion is that apart from updating its D() and E(), also V() used  by\nLS  criterion is updated. Generally the RT criterion is secondary to LS one, and used only if\nthere's a risk of violating precise realtime  requirements.  Still,  the  \"participation\"  in\nbandwidth  distributed  by  LS criterion is there, so V() has to be updated along the way. LS\ncriterion can than properly compensate for non-ideal fair sharing  situation,  caused  by  RT\nscheduling.  If you use UL service curve its F() will be updated as well (UL service curve is\nan extension to LS one - see UPPERLIMIT CRITERION section).\n\nAnyway - careless specification of LS and RT service curves can lead to potentially undesired\nsituations  (see  CORNER CASES for examples). This wasn't the case in HFSC paper where LS and\nRT service curves couldn't be specified separately.\n\n### LINKSHARING CRITERION\n\nLS criterion's task is to distribute bandwidth according to specified class  hierarchy.  Con‐\ntrary  to  RT criterion, there're no comparisons between current real time and virtual time -\nthe decision is based solely on direct comparison of virtual times of all active subclasses -\nthe one with the smallest vt wins and gets scheduled. One immediate conclusion from this fact\nis that absolute values don't matter - only ratios between them (so for example, two children\nclasses  with  simple  linear 1Mbit service curves will get the same treatment from LS crite‐\nrion's perspective, as if they were 5Mbit). The other conclusion is, that in perfectly  fluid\nsystem with linear curves, all virtual times across whole class hierarchy would be equal.\n\nWhy is VC defined in term of virtual time (and what is it)?\n\nImagine an example: class A with two children - A1 and A2, both with let's say 10Mbit SCs. If\nA2 is idle, A1 receives all the bandwidth of A (and update its V() in the process).  When  A2\nbecomes active, A1's virtual time is already far later than A2's one. Considering the type of\ndecision made by LS criterion, A1 would become idle for a long time. We can  workaround  this\nsituation by adjusting virtual time of the class becoming active - we do that by getting such\ntime \"up to date\". HFSC uses a mean of the smallest and the biggest virtual time of currently\nactive  children  fit  for  sending. As it's not real time anymore (excluding trivial case of\nsituation where all classes become active at the same time,  and  never  become  idle),  it's\ncalled virtual time.\n\nSuch  approach has its price though. The problem is analogous to what was presented in previ‐\nous section and is caused by non-linearity of service curves:\n\n1)  either it's impossible to guarantee service curves and satisfy  fairness  during  certain\ntime periods:\n\nRecall the example from RT section, slightly modified (with 3Mbit slopes instead of 2Mbit\nones):\n\n\n•   1st class - 3Mbit for 100ms, then 7Mbit (convex - 1st slope < 2nd slope)\n\n•   2nd class - 7Mbit for 100ms, then 3Mbit (concave - 1st slope > 2nd slope)\n\n\nThey sum up nicely to 10Mbit - the interface's capacity. But if we wanted to only use  LS\nfor  guarantees  and fairness - it simply won't work. In LS context, only V() is used for\nmaking decision which class to schedule. If the 2nd class becomes active when the 1st one\nis in its second slope, the fairness will be preserved - ratio will be 1:1 (7Mbit:7Mbit),\nbut LS itself is of course unable to guarantee the absolute values  themselves  -  as  it\nwould have to go beyond of what the interface is capable of.\n\n\n2)  and/or  it's  impossible  to  guarantee  service  curves  of all classes at the same time\n[fairly or not]:\n\n\nThis is similar to the above case, but a bit more subtle. We will consider two  subtrees,\narbitrated by their common (root here) parent:\n\nR (root) - 10Mbit\n\nA  - 7Mbit, then 3Mbit\nA1 - 5Mbit, then 2Mbit\nA2 - 2Mbit, then 1Mbit\n\nB  - 3Mbit, then 7Mbit\n\nR  arbitrates between left subtree (A) and right (B). Assume that A2 and B are constantly\nbacklogged, and at some later point A1 becomes backlogged (when all other classes are  in\ntheir 2nd linear part).\n\nWhat  happens  now?  B (choice made by R) will always get 7 Mbit as R is only (obviously)\nconcerned with the ratio between its direct children. Thus A subtree gets 3Mbit, but  its\nchildren  would  want  (at  the point when A1 became backlogged) 5Mbit + 1Mbit. That's of\ncourse impossible, as they can only get 3Mbit due to interface limitation.\n\nIn the left subtree - we have the same situation as previously (fair split between A1 and\nA2,  but  violated guarantees), but in the whole tree - there's no fairness (B got 7Mbit,\nbut A1 and A2 have to fit together in 3Mbit) and there's no guarantees  for  all  classes\n(only  B  got what it wanted). Even if we violated fairness in the A subtree and set A2's\nservice curve to 0, A1 would still not get the required bandwidth.\n\n### UPPERLIMIT CRITERION\n\nUL criterion is an extensions to LS one, that permits sending packets only  if  current  real\ntime is later than fit-time ('ft'). So the modified LS criterion becomes: choose the smallest\nvirtual time from all active children, such that fit-time < current  real  time  also  holds.\nFit-time is calculated from F(), which is based on UL service curve. As you can see, its role\nis kinda similar to E() used in RT criterion. Also, for obvious reasons - you  can't  specify\nUL service curve without LS one.\n\nThe  main  purpose of the UL service curve is to limit HFSC to bandwidth available on the up‐\nstream router (think adsl home modem/router,  and  linux  server  as  NAT/firewall/etc.  with\n100Mbit+  connection  to  mentioned  modem/router).   Typically, it's used to create a single\nclass directly under root, setting a linear UL service curve to  available  bandwidth  -  and\nthen creating your class structure from that class downwards. Of course, you're free to add a\nUL service curve (linear or not) to any class with LS criterion.\n\nAn important part about the UL service curve is that whenever at some point in time  a  class\ndoesn't  qualify  for  linksharing due to its fit-time, the next time it does qualify it will\nupdate its virtual time to the smallest virtual time of all active children fit for linkshar‐\ning.  This  way,  one  of the main things the LS criterion tries to achieve - equality of all\nvirtual times across whole hierarchy - is preserved (in perfectly fluid system with only lin‐\near curves, all virtual times would be equal).\n\nWithout  that,  'vt' would lag behind other virtual times, and could cause problems. Consider\nan interface with a capacity of 10Mbit, and the following leaf classes (just in  case  you're\nskipping this text quickly - this example shows behavior that ddooeessnn''tt hhaappppeenn):\n\nA - ls 5.0Mbit\nB - ls 2.5Mbit\nC - ls 2.5Mbit, ul 2.5Mbit\n\nIf B was idle, while A and C were constantly backlogged, A and C would normally (as far as LS\ncriterion is concerned) divide bandwidth in 2:1 ratio. But due to UL service curve in  place,\nC would get at most 2.5Mbit, and A would get the remaining 7.5Mbit. The longer the backlogged\nperiod, the more the virtual times of A and C would drift apart. If B  became  backlogged  at\nsome later point in time, its virtual time would be set to (A's vt + C's vt)/2, thus blocking\nA from sending any traffic until B's virtual time catches up with A.\n\n#### SEPARATE LS / RT SCs\n\nAnother difference from the original HFSC paper is that RT and LS SCs can be specified  sepa‐\nrately.  Moreover,  leaf classes are allowed to have only either RT SC or LS SC. For interior\nclasses, only LS SCs make sense: any RT SC will be ignored.\n\n### CORNER CASES\n\nSeparate service curves for LS and RT criteria can lead  to  certain  traps  that  come  from\n\"fighting\"  between  ideal  linksharing  and  enforced  realtime guarantees. Those situations\ndidn't exist in original HFSC paper, where specifying separate LS / RT service curves was not\ndiscussed.\n\nConsider an interface with a 10Mbit capacity, with the following leaf classes:\n\nA - ls 5.0Mbit, rt 8Mbit\nB - ls 2.5Mbit\nC - ls 2.5Mbit\n\nImagine  A  and  C are constantly backlogged. As B is idle, A and C would divide bandwidth in\n2:1 ratio, considering LS service curve (so in theory - 6.66 and  3.33).  Alas  RT  criterion\ntakes priority, so A will get 8Mbit and LS will be able to compensate class C for only 2 Mbit\n- this will cause discrepancy between virtual times of A and C.\n\nAssume this situation lasts for a long time with no idle periods, and suddenly B becomes  ac‐\ntive.  B's  virtual  time  will be updated to (A's vt + C's vt)/2, effectively landing in the\nmiddle between A's and C's virtual time. The effect - B, having no  RT  guarantees,  will  be\npunished and will not be allowed to transfer until C's virtual time catches up.\n\nIf  the  interface had a higher capacity, for example 100Mbit, this example would behave per‐\nfectly fine though.\n\nLet's look a bit closer at the above example - it \"cleverly\" invalidates  one  of  the  basic\nthings  LS criterion tries to achieve - equality of all virtual times across class hierarchy.\nLeaf classes without RT service curves are literally left to  their  own  fate  (governed  by\nmessed up virtual times).\n\nAlso,  it doesn't make much sense. Class A will always be guaranteed up to 8Mbit, and this is\nmore than any absolute bandwidth that could happen from its LS criterion  (excluding  trivial\ncase of only A being active). If the bandwidth taken by A is smaller than absolute value from\nLS criterion, the unused part will be automatically assigned to other active  classes  (as  A\nhas idling periods in such case). The only \"advantage\" is, that even in case of low bandwidth\non average, bursts would be handled at the speed defined by RT  criterion.  Still,  if  extra\nspeed is needed (e.g. due to latency), non linear service curves should be used in such case.\n\nIn the other words: the LS criterion is meaningless in the above example.\n\nYou  can quickly \"workaround\" it by making sure each leaf class has RT service curve assigned\n(thus guaranteeing all of them will get some bandwidth), but it  doesn't  make  it  any  more\nvalid.\n\nKeep  in mind - if you use nonlinear curves and irregularities explained above happen only in\nthe first segment, then there's little wrong with \"overusing\" RT curve a bit:\n\nA - ls 5.0Mbit, rt 9Mbit/30ms, then 1Mbit\nB - ls 2.5Mbit\nC - ls 2.5Mbit\n\nHere, the vt of A will \"spike\" in the initial period, but then A will  never  get  more  than\n1Mbit until B & C catch up. Then everything will be back to normal.\n\n### LINUX AND TIMER RESOLUTION\n\nIn  certain  situations,  the  scheduler  can throttle itself and setup so called watchdog to\nwakeup dequeue function at some time later. In case of HFSC it happens when  for  example  no\npacket  is  eligible for scheduling, and UL service curve is used to limit the speed at which\nLS criterion is allowed to dequeue packets. It's called throttling, and accuracy of it is de‐\npendent on how the kernel is compiled.\n\nThere're  3 important options in modern kernels, as far as timers' resolution goes: 'tickless\nsystem', 'high resolution timer support' and 'timer frequency'.\n\nIf you have 'tickless system' enabled, then the timer interrupt will  trigger  as  slowly  as\npossible,  but  each time a scheduler throttles itself (or any other part of the kernel needs\nbetter accuracy), the rate will be increased as needed /  possible.  The  ceiling  is  either\n'timer  frequency' if 'high resolution timer support' is not available or not compiled in, or\nit's hardware dependent and can go far beyond the highest 'timer  frequency'  setting  avail‐\nable.\n\nIf  'tickless  system'  is  not  enabled, the timer will trigger at a fixed rate specified by\n'timer frequency' - regardless if high resolution timers are or aren't available.\n\nThis is important to keep those settings in mind, as in scenario like:  no  tickless,  no  HR\ntimers,  frequency  set to 100hz - throttling accuracy would be at 10ms. It doesn't automati‐\ncally mean you would be limited to ~0.8Mbit/s (assuming packets at ~1KB) - as  long  as  your\nqueues  are prepared to cover for timer inaccuracy. Of course, in case of e.g. locally gener‐\nated UDP traffic - appropriate socket size is needed as well. Short example to make  it  more\nunderstandable (assume hardcore anti-schedule settings - HZ=100, no HR timers, no tickless):\n\ntc qdisc add dev eth0 root handle 1:0 hfsc default 1\ntc class add dev eth0 parent 1:0 classid 1:1 hfsc rt m2 10Mbit\n\nAssuming packet of ~1KB size and HZ=100, that averages to ~0.8Mbit - anything beyond it (e.g.\nthe above example with specified rate over 10x larger) will require appropriate  queuing  and\ncause bursts every ~10 ms. As you can imagine, any HFSC's RT guarantees will be seriously in‐\nvalidated by that.  Aforementioned example is mainly important if you deal with old  hardware\n-  as  is  particularly popular for home server chores. Even then, you can easily set HZ=1000\nand have very accurate scheduling for typical adsl speeds.\n\nAnything modern (apic or even hpet msi based timers + 'tickless system') will provide  enough\naccuracy  for superb 1Gbit scheduling. For example, on one of my cheap dual-core AMD boards I\nhave the following settings:\n\ntc qdisc add dev eth0 parent root handle 1:0 hfsc default 1\ntc class add dev eth0 parent 1:0 classid 1:1 hfsc rt m2 300mbit\n\nAnd a simple:\n\nnc -u dst.host.com 54321 </dev/zero\nnc -l -p 54321 >/dev/null\n\n...will yield the following effects over a period of ~10  seconds  (taken  from  /proc/inter‐\nrupts):\n\n319: 42124229   0  HPETMSI-edge  hpet2 (before)\n319: 42436214   0  HPETMSI-edge  hpet2 (after 10s.)\n\nThat's  roughly  31000/s.  Now compare it with HZ=1000 setting. The obvious drawback of it is\nthat cpu load can be rather high with servicing that many timer interrupts. The example  with\n300Mbit  RT  service curve on 1Gbit link is particularly ugly, as it requires a lot of throt‐\ntling with minuscule delays.\n\nAlso note that it's just an example showing the capabilities of current hardware.  The  above\nexample  (essentially  a 300Mbit TBF emulator) is pointless on an internal interface to begin\nwith: you will pretty much always want a regular LS service curve there, and in such  a  sce‐\nnario HFSC simply doesn't throttle at all.\n\n300Mbit RT service curve (selected columns from mpstat -P ALL 1):\n\n10:56:43 PM  CPU  %sys     %irq   %soft   %idle\n10:56:44 PM  all  20.10    6.53   34.67   37.19\n10:56:44 PM    0  35.00    0.00   63.00    0.00\n10:56:44 PM    1   4.95   12.87    6.93   73.27\n\nSo, in the rare case you need those speeds with only a RT service curve, or with a UL service\ncurve: remember the drawbacks.\n\n#### CAVEAT: RANDOM ONLINE EXAMPLES\n\nFor reasons unknown (though well guessed), many examples you can google love  to  overuse  UL\ncriterion  and  stuff  it  in every node possible. This makes no sense and works against what\nHFSC tries to do (and does pretty damn well). Use UL where it makes sense: on  the  uppermost\nnode  to match upstream router's uplink capacity. Or in special cases, such as testing (limit\ncertain subtree to some speed), or customers that must never get more than certain speed.  In\nthe  last case you can usually achieve the same by just using a RT criterion without LS+UL on\nleaf nodes.\n\nAs for the router case - remember it's good to differentiate between \"traffic to router\" (re‐\nmote console, web config, etc.) and \"outgoing traffic\", so for example:\n\ntc qdisc add dev eth0 root handle 1:0 hfsc default 0x8002\ntc class add dev eth0 parent 1:0 classid 1:999 hfsc rt m2 50Mbit\ntc class add dev eth0 parent 1:0 classid 1:1 hfsc ls m2 2Mbit ul m2 2Mbit\n\n... so \"internet\" tree under 1:1 and \"router itself\" as 1:999\n\n### LAYER2 ADAPTATION\n\nPlease refer to tc-stab(8)\n\n### SEE ALSO\n\ntc(8), tc-hfsc(8), tc-stab(8)\n\nPlease direct bugreports and patches to: <netdev@vger.kernel.org>\n\n### AUTHOR\n\nManpage created by Michal Soltys (soltys@ziu.info)\n\n\n\niproute2                                   31 October 2011                                TC-HFSC(7)\n\n"
        }
    ],
    "structuredContent": {
        "command": "tc-hfsc",
        "section": "7",
        "mode": "man",
        "summary": "tc-hfcs - Hierarchical Fair Service Curve",
        "synopsis": null,
        "tldr_summary": null,
        "tldr_examples": [],
        "tldr_source": null,
        "flags": [],
        "examples": [],
        "see_also": [
            {
                "name": "tc",
                "section": "8",
                "url": "https://www.chedong.com/phpMan.php/man/tc/8/json"
            },
            {
                "name": "tc-stab",
                "section": "8",
                "url": "https://www.chedong.com/phpMan.php/man/tc-stab/8/json"
            }
        ],
        "section_outline": [
            {
                "name": "NAME",
                "lines": 2,
                "subsections": [
                    {
                        "name": "HISTORY & INTRODUCTION",
                        "lines": 36
                    }
                ]
            },
            {
                "name": "ABBREVIATIONS",
                "lines": 7,
                "subsections": []
            },
            {
                "name": "BASICS OF HFSC",
                "lines": 81,
                "subsections": []
            },
            {
                "name": "REALTIME CRITERION",
                "lines": 48,
                "subsections": []
            },
            {
                "name": "LINKSHARING CRITERION",
                "lines": 74,
                "subsections": []
            },
            {
                "name": "UPPERLIMIT CRITERION",
                "lines": 36,
                "subsections": [
                    {
                        "name": "SEPARATE LS / RT SCs",
                        "lines": 4
                    }
                ]
            },
            {
                "name": "CORNER CASES",
                "lines": 53,
                "subsections": []
            },
            {
                "name": "LINUX AND TIMER RESOLUTION",
                "lines": 74,
                "subsections": [
                    {
                        "name": "CAVEAT: RANDOM ONLINE EXAMPLES",
                        "lines": 17
                    }
                ]
            },
            {
                "name": "LAYER2 ADAPTATION",
                "lines": 2,
                "subsections": []
            },
            {
                "name": "SEE ALSO",
                "lines": 4,
                "subsections": []
            },
            {
                "name": "AUTHOR",
                "lines": 5,
                "subsections": []
            }
        ]
    }
}