1.\" $NetBSD: altq.9,v 1.8 2002/05/28 11:41:45 wiz Exp $ 2.\" $OpenBSD: altq.9,v 1.4 2001/07/12 12:41:42 itojun Exp $ 3.\" 4.\" Copyright (C) 2001 5.\" Sony Computer Science Laboratories Inc. All rights reserved. 6.\" 7.\" Redistribution and use in source and binary forms, with or without 8.\" modification, are permitted provided that the following conditions 9.\" are met: 10.\" 1. Redistributions of source code must retain the above copyright 11.\" notice, this list of conditions and the following disclaimer. 12.\" 2. Redistributions in binary form must reproduce the above copyright 13.\" notice, this list of conditions and the following disclaimer in the 14.\" documentation and/or other materials provided with the distribution. 15.\" 16.\" THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND 17.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19.\" ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE 20.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26.\" SUCH DAMAGE. 27.\" 28.Dd July 10, 2001 29.Dt ALTQ 9 30.Os 31.\" 32.Sh NAME 33.Nm ALTQ 34.Nd kernel interfaces for manipulating output queues on network interfaces 35.Sh SYNOPSIS 36.Fd #include \*[Lt]sys/types.h\*[Gt] 37.Fd #include \*[Lt]sys/socket.h\*[Gt] 38.Fd #include \*[Lt]net/if.h\*[Gt] 39.Ft void \"macro 40.Fn IFQ_ENQUEUE "struct ifaltq *ifq" "struct mbuf *m" "int error" 41.Ft void \"macro 42.Fn IFQ_DEQUEUE "struct ifaltq *ifq" "struct mbuf *m" 43.Ft void \"macro 44.Fn IFQ_POLL "struct ifaltq *ifq" "struct mbuf *m" 45.Ft void \"macro 46.Fn IFQ_PURGE "struct ifaltq *ifq" 47.Ft void \"macro 48.Fn IFQ_CLASSIFY "struct ifaltq *ifq" "struct mbuf *m" "int af" "struct altq_pktattr *pktattr" 49.Ft void \"macro 50.Fn IFQ_IS_EMPTY "struct ifaltq *ifq" 51.Ft void \"macro 52.Fn IFQ_SET_MAXLEN "struct ifaltq *ifq" "int len" 53.Ft void \"macro 54.Fn IFQ_INC_LEN "struct ifaltq *ifq" 55.Ft void \"macro 56.Fn IFQ_DEC_LEN "struct ifaltq *ifq" 57.Ft void \"macro 58.Fn IFQ_INC_DROPS "struct ifaltq *ifq" 59.Ft void \"macro 60.Fn IFQ_SET_READY "struct ifaltq *ifq" 61.Sh DESCRIPTION 62The 63.Nm 64system is a framework to manage queueing disciplines on network 65interfaces. 66.Nm 67introduces new macros to manipulate output queues. 68The output queue macros are used to abstract queue operations and not to 69touch the internal fields of the output queue structure. 70The macros are independent from the 71.Nm 72implementation, and compatible with the traditional 73.Dv ifqueue 74macros for ease of transition. 75.Pp 76.Fn IFQ_ENQUEUE 77enqueues a packet 78.Fa m 79to the queue 80.Fa ifq . 81The underlying queueing discipline may discard the packet. 82.Fa error 83is set to 0 on success, or 84.Dv ENOBUFS 85if the packet is discarded. 86.Fa m 87will be freed by the device driver on success or by the queueing discipline on 88failure so that the caller should not touch 89.Fa m 90after calling 91.Fn IFQ_ENQUEUE . 92.Pp 93.Fn IFQ_DEQUEUE 94dequeues a packet from the queue. 95The dequeued packet is returned in 96.Fa m , 97or 98.Fa m 99is set to 100.Dv NULL 101if no packet is dequeued. 102The caller must always check 103.Fa m 104since a non-empty queue could return 105.Dv NULL 106under rate-limiting. 107.Pp 108.Fn IFQ_POLL 109returns the next packet without removing it from the queue. 110It is guaranteed by the underlying queueing discipline that 111.Fn IFQ_DEQUEUE 112immediately after 113.Fn IFQ_POLL 114returns the same packet. 115.Pp 116.Fn IFQ_PURGE 117discards all the packets in the queue. 118The purge operation is needed since a non-work conserving queue cannot be 119emptied by a dequeue loop. 120.Pp 121.Fn IFQ_CLASSIFY 122classifies a packet to a scheduling class, and returns the result in 123.Fa pktattr . 124.Pp 125.Fn IFQ_IS_EMPTY 126can be used to check if the queue is empty. 127Note that 128.Fn IFQ_DEQUEUE 129could still return 130.Dv NULL 131if the queueing discipline is non-work conserving. 132.Pp 133.Fn IFQ_SET_MAXLEN 134sets the queue length limit to the default FIFO queue. 135.Pp 136.Fn IFQ_INC_LEN 137and 138.Fn IFQ_DEC_LEN 139increment or decrement the current queue length in packets. 140.Pp 141.Fn IFQ_INC_DROPS 142increments the drop counter and is equal to 143.Fn IF_DROP . 144It is defined for naming consistency. 145.Pp 146.Fn IFQ_SET_READY 147sets a flag to indicate this driver is converted to use the new macros. 148.Nm 149can be enabled only on interfaces with this flag. 150.Sh COMPATIBILITY 151.Ss ifaltq structure 152In order to keep compatibility with the existing code, the new 153output queue structure 154.Dv ifaltq 155has the same fields. 156The traditional 157.Fn IF_XXX 158macros and the code directly referencing the fields within 159.Dv if_snd 160still work with 161.Dv ifaltq . 162(Once we finish conversions of all the drivers, we no longer need 163these fields.) 164.Bd -literal 165 ##old-style## ##new-style## 166 | 167 struct ifqueue { | struct ifaltq { 168 struct mbuf *ifq_head; | struct mbuf *ifq_head; 169 struct mbuf *ifq_tail; | struct mbuf *ifq_tail; 170 int ifq_len; | int ifq_len; 171 int ifq_maxlen; | int ifq_maxlen; 172 int ifq_drops; | int ifq_drops; 173 }; | /* altq related fields */ 174 | ...... 175 | }; 176 | 177.Ed 178The new structure replaces 179.Dv struct ifqueue 180in 181.Dv struct ifnet . 182.Bd -literal 183 ##old-style## ##new-style## 184 | 185 struct ifnet { | struct ifnet { 186 .... | .... 187 | 188 struct ifqueue if_snd; | struct ifaltq if_snd; 189 | 190 .... | .... 191 }; | }; 192 | 193.Ed 194The (simplified) new 195.Fn IFQ_XXX 196macros looks like: 197.Bd -literal 198 #ifdef ALTQ 199 #define IFQ_DEQUEUE(ifq, m) \e 200 if (ALTQ_IS_ENABLED((ifq)) \e 201 ALTQ_DEQUEUE((ifq), (m)); \e 202 else \e 203 IF_DEQUEUE((ifq), (m)); 204 #else 205 #define IFQ_DEQUEUE(ifq, m) IF_DEQUEUE((ifq), (m)); 206 #endif 207.Ed 208.Ss Enqueue operation 209The semantics of the enqueue operation is changed. 210In the new style, 211enqueue and packet drop are combined since they cannot be easily 212separated in many queueing disciplines. 213The new enqueue operation corresponds to the following macro that is 214written with the old macros. 215.Bd -literal 216#define IFQ_ENQUEUE(ifq, m, error) \e 217do { \e 218 if (IF_QFULL((ifq))) { \e 219 m_freem((m)); \e 220 (error) = ENOBUFS; \e 221 IF_DROP(ifq); \e 222 } else { \e 223 IF_ENQUEUE((ifq), (m)); \e 224 (error) = 0; \e 225 } \e 226} while (0) 227.Ed 228.Pp 229.Fn IFQ_ENQUEUE 230does the followings: 231.Bl -hyphen -compact 232.It 233queue a packet 234.It 235drop (and free) a packet if the enqueue operation fails 236.El 237If the enqueue operation fails, 238.Fa error 239is set to 240.Dv ENOBUFS . 241.Fa mbuf 242is freed by the queueing discipline. 243The caller should not touch mbuf after calling 244.Fn IFQ_ENQUEUE 245so that the caller may need to copy 246.Fa m_pkthdr.len 247or 248.Fa m_flags 249field beforehand for statistics. 250The caller should not use 251.Fn senderr 252since mbuf was already freed. 253.Pp 254The new style 255.Fn if_output 256looks as follows: 257.Bd -literal 258 ##old-style## ##new-style## 259 | 260 int | int 261 ether_output(ifp, m0, dst, rt0) | ether_output(ifp, m0, dst, rt0) 262 { | { 263 ...... | ...... 264 | 265 | mflags = m-\*[Gt]m_flags; 266 | len = m-\*[Gt]m_pkthdr.len; 267 s = splimp(); | s = splimp(); 268 if (IF_QFULL(\*[Am]ifp-\*[Gt]if_snd)) { | IFQ_ENQUEUE(\*[Am]ifp-\*[Gt]if_snd, m, 269 | error); 270 IF_DROP(\*[Am]ifp-\*[Gt]if_snd); | if (error != 0) { 271 splx(s); | splx(s); 272 senderr(ENOBUFS); | return (error); 273 } | } 274 IF_ENQUEUE(\*[Am]ifp-\*[Gt]if_snd, m); | 275 ifp-\*[Gt]if_obytes += | ifp-\*[Gt]if_obytes += len; 276 m-\*[Gt]m_pkthdr.len; | 277 if (m-\*[Gt]m_flags \*[Am] M_MCAST) | if (mflags \*[Am] M_MCAST) 278 ifp-\*[Gt]if_omcasts++; | ifp-\*[Gt]if_omcasts++; 279 | 280 if ((ifp-\*[Gt]if_flags \*[Am] IFF_OACTIVE) | if ((ifp-\*[Gt]if_flags \*[Am] IFF_OACTIVE) 281 == 0) | == 0) 282 (*ifp-\*[Gt]if_start)(ifp); | (*ifp-\*[Gt]if_start)(ifp); 283 splx(s); | splx(s); 284 return (error); | return (error); 285 | 286 bad: | bad: 287 if (m) | if (m) 288 m_freem(m); | m_freem(m); 289 return (error); | return (error); 290 } | } 291 | 292.Ed 293.Ss Classifier 294The classifier mechanism is currently implemented in 295.Fn if_output . 296.Dv struct altq_pktattr 297is used to store the classifier result, and it is passed to the enqueue 298function. 299(We will change the method to tag the classifier result to mbuf in the future.) 300.Bd -literal 301int 302ether_output(ifp, m0, dst, rt0) 303{ 304 ...... 305 struct altq_pktattr pktattr; 306 307 ...... 308 309 /* classify the packet before prepending link-headers */ 310 IFQ_CLASSIFY(\*[Am]ifp-\*[Gt]if_snd, m, dst-\*[Gt]sa_family, \*[Am]pktattr); 311 312 /* prepend link-level headers */ 313 ...... 314 315 IFQ_ENQUEUE(\*[Am]ifp-\*[Gt]if_snd, m, \*[Am]pktattr, error); 316 317 ...... 318} 319.Ed 320.Sh HOW TO CONVERT THE EXISTING DRIVERS 321First, make sure the corresponding 322.Fn if_output 323is already converted to the new style. 324.Pp 325Look for 326.Fa if_snd 327in the driver. 328Probably, you need to make changes to the lines that include 329.Fa if_snd . 330.Ss Empty check operation 331If the code checks 332.Fa ifq_head 333to see whether the queue is empty or not, use 334.Fn IFQ_IS_EMPTY . 335.Bd -literal 336 ##old-style## ##new-style## 337 | 338 if (ifp-\*[Gt]if_snd.ifq_head != NULL) | if (IFQ_IS_EMPTY(\*[Am]ifp-\*[Gt]if_snd) == 0) 339 | 340.Ed 341Note that 342.Fn IFQ_POLL 343can be used for the same purpose, but 344.Fn IFQ_POLL 345could be costly for a complex scheduling algorithm since 346.Fn IFQ_POLL 347needs to run the scheduling algorithm to select the next packet. 348On the other hand, 349.Fn IFQ_IS_EMPTY 350checks only if there is any packet stored in the queue. 351Another difference is that even when 352.Fn IFQ_IS_EMPTY 353is 354.Dv FALSE , 355.Fn IFQ_DEQUEUE 356could still return 357.Dv NULL 358if the queue is under rate-limiting. 359.Ss Dequeue operation 360Replace 361.Fn IF_DEQUEUE 362by 363.Fn IFQ_DEQUEUE . 364Always check whether the dequeued mbuf is 365.Dv NULL 366or not. 367Note that even when 368.Fn IFQ_IS_EMPTY 369is 370.Dv FALSE , 371.Fn IFQ_DEQUEUE 372could return 373.Dv NULL 374due to rate-limiting. 375.Bd -literal 376 ##old-style## ##new-style## 377 | 378 IF_DEQUEUE(\*[Am]ifp-\*[Gt]if_snd, m); | IFQ_DEQUEUE(\*[Am]ifp-\*[Gt]if_snd, m); 379 | if (m == NULL) 380 | return; 381 | 382.Ed 383A driver is supposed to call 384.Fn if_start 385from transmission complete interrupts in order to trigger the next dequeue. 386.Ss Poll-and-dequeue operation 387If the code polls the packet at the head of the queue and actually uses 388the packet before dequeueing it, use 389.Fn IFQ_POLL 390and 391.Fn IFQ_DEQUEUE . 392.Bd -literal 393 ##old-style## ##new-style## 394 | 395 m = ifp-\*[Gt]if_snd.ifq_head; | IFQ_POLL(\*[Am]ifp-\*[Gt]if_snd, m); 396 if (m != NULL) { | if (m != NULL) { 397 | 398 /* use m to get resources */ | /* use m to get resources */ 399 if (something goes wrong) | if (something goes wrong) 400 return; | return; 401 | 402 IF_DEQUEUE(\*[Am]ifp-\*[Gt]if_snd, m); | IFQ_DEQUEUE(\*[Am]ifp-\*[Gt]if_snd, m); 403 | 404 /* kick the hardware */ | /* kick the hardware */ 405 } | } 406 | 407.Ed 408It is guaranteed that 409.Fn IFQ_DEQUEUE 410immediately after 411.Fn IFQ_POLL 412returns the same packet. 413Note that they need to be guarded by 414.Fn splimp 415if called from outside of 416.Fn if_start . 417.Ss Eliminating IF_PREPEND 418If the code uses 419.Fn IF_PREPEND , 420you have to eliminate it since the prepend operation is not possible for many 421queueing disciplines. 422A common use of 423.Fn IF_PREPEND 424is to cancel the previous dequeue operation. 425You have to convert the logic into poll-and-dequeue. 426.Bd -literal 427 ##old-style## ##new-style## 428 | 429 IF_DEQUEUE(\*[Am]ifp-\*[Gt]if_snd, m); | IFQ_POLL(\*[Am]ifp-\*[Gt]if_snd, m); 430 if (m != NULL) { | if (m != NULL) { 431 | 432 if (something_goes_wrong) { | if (something_goes_wrong) { 433 IF_PREPEND(\*[Am]ifp-\*[Gt]if_snd, m); | 434 return; | return; 435 } | } 436 | 437 | /* at this point, the driver 438 | * is committed to send this 439 | * packet. 440 | */ 441 | IFQ_DEQUEUE(\*[Am]ifp-\*[Gt]if_snd, m); 442 | 443 /* kick the hardware */ | /* kick the hardware */ 444 } | } 445 | 446.Ed 447.Ss Purge operation 448Use 449.Fn IFQ_PURGE 450to empty the queue. 451Note that a non-work conserving queue cannot be emptied by a dequeue loop. 452.Bd -literal 453 ##old-style## ##new-style## 454 | 455 while (ifp-\*[Gt]if_snd.ifq_head != NULL) {| IFQ_PURGE(\*[Am]ifp-\*[Gt]if_snd); 456 IF_DEQUEUE(\*[Am]ifp-\*[Gt]if_snd, m); | 457 m_freem(m); | 458 } | 459 | 460.Ed 461.Ss Attach routine 462Use 463.Fn IFQ_SET_MAXLEN 464to set 465.Fa ifq_maxlen 466to 467.Fa len . 468Add 469.Fn IFQ_SET_READY 470to show this driver is converted to the new style. 471(This is used to distinguish new-style drivers.) 472.Bd -literal 473 ##old-style## ##new-style## 474 | 475 ifp-\*[Gt]if_snd.ifq_maxlen = qsize; | IFQ_SET_MAXLEN(\*[Am]ifp-\*[Gt]if_snd, qsize); 476 | IFQ_SET_READY(\*[Am]ifp-\*[Gt]if_snd); 477 if_attach(ifp); | if_attach(ifp); 478 | 479.Ed 480.Ss Other issues 481The new macros for statistics: 482.Bd -literal 483 ##old-style## ##new-style## 484 | 485 IF_DROP(\*[Am]ifp-\*[Gt]if_snd); | IFQ_INC_DROPS(\*[Am]ifp-\*[Gt]if_snd); 486 | 487 ifp-\*[Gt]if_snd.ifq_len++; | IFQ_INC_LEN(\*[Am]ifp-\*[Gt]if_snd); 488 | 489 ifp-\*[Gt]if_snd.ifq_len--; | IFQ_DEC_LEN(\*[Am]ifp-\*[Gt]if_snd); 490 | 491.Ed 492Some drivers instruct the hardware to invoke transmission complete 493interrupts only when it thinks necessary. 494Rate-limiting breaks its assumption. 495.Ss How to convert drivers using multiple ifqueues 496Some (pseudo) devices (such as slip) have another 497.Dv ifqueue 498to prioritize packets. 499It is possible to eliminate the second queue 500since 501.Nm 502provides more flexible mechanisms but the following shows 503how to keep the original behavior. 504.Bd -literal 505struct sl_softc { 506 struct ifnet sc_if; /* network-visible interface */ 507 ... 508 struct ifqueue sc_fastq; /* interactive output queue */ 509 ... 510}; 511.Ed 512The driver doesn't compile in the new model since it has the following 513line 514.Po 515.Fa if_snd 516is no longer a type of 517.Dv struct ifqueue 518.Pc . 519.Bd -literal 520 struct ifqueue *ifq = \*[Am]ifp-\*[Gt]if_snd; 521.Ed 522A simple way is to use the original 523.Fn IF_XXX 524macros for 525.Fa sc_fastq 526and use the new 527.Fn IFQ_XXX 528macros for 529.Fa if_snd . 530The enqueue operation looks like: 531.Bd -literal 532 ##old-style## ##new-style## 533 | 534 struct ifqueue *ifq = \*[Am]ifp-\*[Gt]if_snd; | struct ifqueue *ifq = NULL; 535 | 536 if (ip-\*[Gt]ip_tos \*[Am] IPTOS_LOWDELAY) | if ((ip-\*[Gt]ip_tos \*[Am] IPTOS_LOWDELAY) \*[Am]\*[Am] 537 ifq = \*[Am]sc-\*[Gt]sc_fastq; | !ALTQ_IS_ENABLED(\*[Am]sc-\*[Gt]sc_if.if_snd)) { 538 | ifq = \*[Am]sc-\*[Gt]sc_fastq; 539 if (IF_QFULL(ifq)) { | if (IF_QFULL(ifq)) { 540 IF_DROP(ifq); | IF_DROP(ifq); 541 m_freem(m); | m_freem(m); 542 splx(s); | error = ENOBUFS; 543 sc-\*[Gt]sc_if.if_oerrors++; | } else { 544 return (ENOBUFS); | IF_ENQUEUE(ifq, m); 545 } | error = 0; 546 IF_ENQUEUE(ifq, m); | } 547 | } else 548 | IFQ_ENQUEUE(\*[Am]sc-\*[Gt]sc_if.if_snd, 549 | m, error); 550 | 551 | if (error) { 552 | splx(s); 553 | sc-\*[Gt]sc_if.if_oerrors++; 554 | return (error); 555 | } 556 if ((sc-\*[Gt]sc_oqlen = | if ((sc-\*[Gt]sc_oqlen = 557 sc-\*[Gt]sc_ttyp-\*[Gt]t_outq.c_cc) == 0) | sc-\*[Gt]sc_ttyp-\*[Gt]t_outq.c_cc) == 0) 558 slstart(sc-\*[Gt]sc_ttyp); | slstart(sc-\*[Gt]sc_ttyp); 559 splx(s); | splx(s); 560 | 561.Ed 562The dequeue operations looks like: 563.Bd -literal 564 ##old-style## ##new-style## 565 | 566 s = splimp(); | s = splimp(); 567 IF_DEQUEUE(\*[Am]sc-\*[Gt]sc_fastq, m); | IF_DEQUEUE(\*[Am]sc-\*[Gt]sc_fastq, m); 568 if (m == NULL) | if (m == NULL) 569 IF_DEQUEUE(\*[Am]sc-\*[Gt]sc_if.if_snd, m); | IFQ_DEQUEUE(\*[Am]sc-\*[Gt]sc_if.if_snd, m); 570 splx(s); | splx(s); 571 | 572.Ed 573.Sh QUEUEING DISCIPLINES 574Queueing disciplines need to maintain 575.Fa ifq_len 576.Po 577used by 578.Fn IFQ_IS_EMPTY 579.Pc . 580Queueing disciplines also need to guarantee the same mbuf is returned if 581.Fn IFQ_DEQUEUE 582is called immediately after 583.Fn IFQ_POLL . 584.Sh SEE ALSO 585.Xr altq.conf 5 , 586.Xr altqd 8 , 587.Xr tbrconfig 8 588.Sh HISTORY 589The 590.Nm 591system first appeared in March 1997. 592