ifq.c revision 1.15
1/* $OpenBSD: ifq.c,v 1.15 2017/11/14 08:44:11 dlg Exp $ */ 2 3/* 4 * Copyright (c) 2015 David Gwynne <dlg@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/param.h> 20#include <sys/systm.h> 21#include <sys/socket.h> 22#include <sys/mbuf.h> 23#include <sys/proc.h> 24 25#include <net/if.h> 26#include <net/if_var.h> 27 28/* 29 * priq glue 30 */ 31unsigned int priq_idx(unsigned int, const struct mbuf *); 32struct mbuf *priq_enq(struct ifqueue *, struct mbuf *); 33struct mbuf *priq_deq_begin(struct ifqueue *, void **); 34void priq_deq_commit(struct ifqueue *, struct mbuf *, void *); 35void priq_purge(struct ifqueue *, struct mbuf_list *); 36 37void *priq_alloc(unsigned int, void *); 38void priq_free(unsigned int, void *); 39 40const struct ifq_ops priq_ops = { 41 priq_idx, 42 priq_enq, 43 priq_deq_begin, 44 priq_deq_commit, 45 priq_purge, 46 priq_alloc, 47 priq_free, 48}; 49 50const struct ifq_ops * const ifq_priq_ops = &priq_ops; 51 52/* 53 * priq internal structures 54 */ 55 56struct priq { 57 struct mbuf_list pq_lists[IFQ_NQUEUES]; 58}; 59 60/* 61 * ifqueue serialiser 62 */ 63 64void ifq_start_task(void *); 65void ifq_restart_task(void *); 66void ifq_barrier_task(void *); 67 68#define TASK_ONQUEUE 0x1 69 70void 71ifq_serialize(struct ifqueue *ifq, struct task *t) 72{ 73 struct task work; 74 75 if (ISSET(t->t_flags, TASK_ONQUEUE)) 76 return; 77 78 mtx_enter(&ifq->ifq_task_mtx); 79 if (!ISSET(t->t_flags, TASK_ONQUEUE)) { 80 SET(t->t_flags, TASK_ONQUEUE); 81 TAILQ_INSERT_TAIL(&ifq->ifq_task_list, t, t_entry); 82 } 83 84 if (ifq->ifq_serializer == NULL) { 85 ifq->ifq_serializer = curcpu(); 86 87 while ((t = TAILQ_FIRST(&ifq->ifq_task_list)) != NULL) { 88 TAILQ_REMOVE(&ifq->ifq_task_list, t, t_entry); 89 CLR(t->t_flags, TASK_ONQUEUE); 90 work = *t; /* copy to caller to avoid races */ 91 92 mtx_leave(&ifq->ifq_task_mtx); 93 94 (*work.t_func)(work.t_arg); 95 96 mtx_enter(&ifq->ifq_task_mtx); 97 } 98 99 ifq->ifq_serializer = NULL; 100 } 101 mtx_leave(&ifq->ifq_task_mtx); 102} 103 104int 105ifq_is_serialized(struct ifqueue *ifq) 106{ 107 return (ifq->ifq_serializer == curcpu()); 108} 109 110void 111ifq_start_task(void *p) 112{ 113 struct ifqueue *ifq = p; 114 struct ifnet *ifp = ifq->ifq_if; 115 116 if (!ISSET(ifp->if_flags, IFF_RUNNING) || 117 ifq_empty(ifq) || ifq_is_oactive(ifq)) 118 return; 119 120 ifp->if_qstart(ifq); 121} 122 123void 124ifq_restart_task(void *p) 125{ 126 struct ifqueue *ifq = p; 127 struct ifnet *ifp = ifq->ifq_if; 128 129 ifq_clr_oactive(ifq); 130 ifp->if_qstart(ifq); 131} 132 133void 134ifq_barrier(struct ifqueue *ifq) 135{ 136 struct sleep_state sls; 137 unsigned int notdone = 1; 138 struct task t = TASK_INITIALIZER(ifq_barrier_task, ¬done); 139 140 /* this should only be called from converted drivers */ 141 KASSERT(ISSET(ifq->ifq_if->if_xflags, IFXF_MPSAFE)); 142 143 if (ifq->ifq_serializer == NULL) 144 return; 145 146 ifq_serialize(ifq, &t); 147 148 while (notdone) { 149 sleep_setup(&sls, ¬done, PWAIT, "ifqbar"); 150 sleep_finish(&sls, notdone); 151 } 152} 153 154void 155ifq_barrier_task(void *p) 156{ 157 unsigned int *notdone = p; 158 159 *notdone = 0; 160 wakeup_one(notdone); 161} 162 163/* 164 * ifqueue mbuf queue API 165 */ 166 167void 168ifq_init(struct ifqueue *ifq, struct ifnet *ifp, unsigned int idx) 169{ 170 ifq->ifq_if = ifp; 171 ifq->ifq_softc = NULL; 172 173 mtx_init(&ifq->ifq_mtx, IPL_NET); 174 ifq->ifq_qdrops = 0; 175 176 /* default to priq */ 177 ifq->ifq_ops = &priq_ops; 178 ifq->ifq_q = priq_ops.ifqop_alloc(idx, NULL); 179 180 ml_init(&ifq->ifq_free); 181 ifq->ifq_len = 0; 182 183 ifq->ifq_packets = 0; 184 ifq->ifq_bytes = 0; 185 ifq->ifq_qdrops = 0; 186 ifq->ifq_errors = 0; 187 ifq->ifq_mcasts = 0; 188 189 mtx_init(&ifq->ifq_task_mtx, IPL_NET); 190 TAILQ_INIT(&ifq->ifq_task_list); 191 ifq->ifq_serializer = NULL; 192 193 task_set(&ifq->ifq_start, ifq_start_task, ifq); 194 task_set(&ifq->ifq_restart, ifq_restart_task, ifq); 195 196 if (ifq->ifq_maxlen == 0) 197 ifq_set_maxlen(ifq, IFQ_MAXLEN); 198 199 ifq->ifq_idx = idx; 200} 201 202void 203ifq_attach(struct ifqueue *ifq, const struct ifq_ops *newops, void *opsarg) 204{ 205 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 206 struct mbuf_list free_ml = MBUF_LIST_INITIALIZER(); 207 struct mbuf *m; 208 const struct ifq_ops *oldops; 209 void *newq, *oldq; 210 211 newq = newops->ifqop_alloc(ifq->ifq_idx, opsarg); 212 213 mtx_enter(&ifq->ifq_mtx); 214 ifq->ifq_ops->ifqop_purge(ifq, &ml); 215 ifq->ifq_len = 0; 216 217 oldops = ifq->ifq_ops; 218 oldq = ifq->ifq_q; 219 220 ifq->ifq_ops = newops; 221 ifq->ifq_q = newq; 222 223 while ((m = ml_dequeue(&ml)) != NULL) { 224 m = ifq->ifq_ops->ifqop_enq(ifq, m); 225 if (m != NULL) { 226 ifq->ifq_qdrops++; 227 ml_enqueue(&free_ml, m); 228 } else 229 ifq->ifq_len++; 230 } 231 mtx_leave(&ifq->ifq_mtx); 232 233 oldops->ifqop_free(ifq->ifq_idx, oldq); 234 235 ml_purge(&free_ml); 236} 237 238void 239ifq_destroy(struct ifqueue *ifq) 240{ 241 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 242 243 /* don't need to lock because this is the last use of the ifq */ 244 245 ifq->ifq_ops->ifqop_purge(ifq, &ml); 246 ifq->ifq_ops->ifqop_free(ifq->ifq_idx, ifq->ifq_q); 247 248 ml_purge(&ml); 249} 250 251void 252ifq_add_data(struct ifqueue *ifq, struct if_data *data) 253{ 254 mtx_enter(&ifq->ifq_mtx); 255 data->ifi_opackets += ifq->ifq_packets; 256 data->ifi_obytes += ifq->ifq_bytes; 257 data->ifi_oqdrops += ifq->ifq_qdrops; 258 data->ifi_omcasts += ifq->ifq_mcasts; 259 /* ifp->if_data.ifi_oerrors */ 260 mtx_leave(&ifq->ifq_mtx); 261} 262 263int 264ifq_enqueue(struct ifqueue *ifq, struct mbuf *m) 265{ 266 struct mbuf *dm; 267 268 mtx_enter(&ifq->ifq_mtx); 269 dm = ifq->ifq_ops->ifqop_enq(ifq, m); 270 if (dm != m) { 271 ifq->ifq_packets++; 272 ifq->ifq_bytes += m->m_pkthdr.len; 273 if (ISSET(m->m_flags, M_MCAST)) 274 ifq->ifq_mcasts++; 275 } 276 277 if (dm == NULL) 278 ifq->ifq_len++; 279 else 280 ifq->ifq_qdrops++; 281 mtx_leave(&ifq->ifq_mtx); 282 283 if (dm != NULL) 284 m_freem(dm); 285 286 return (dm == m ? ENOBUFS : 0); 287} 288 289static inline void 290ifq_deq_enter(struct ifqueue *ifq) 291{ 292 mtx_enter(&ifq->ifq_mtx); 293} 294 295static inline void 296ifq_deq_leave(struct ifqueue *ifq) 297{ 298 struct mbuf_list ml; 299 300 ml = ifq->ifq_free; 301 ml_init(&ifq->ifq_free); 302 303 mtx_leave(&ifq->ifq_mtx); 304 305 if (!ml_empty(&ml)) 306 ml_purge(&ml); 307} 308 309struct mbuf * 310ifq_deq_begin(struct ifqueue *ifq) 311{ 312 struct mbuf *m = NULL; 313 void *cookie; 314 315 ifq_deq_enter(ifq); 316 if (ifq->ifq_len == 0 || 317 (m = ifq->ifq_ops->ifqop_deq_begin(ifq, &cookie)) == NULL) { 318 ifq_deq_leave(ifq); 319 return (NULL); 320 } 321 322 m->m_pkthdr.ph_cookie = cookie; 323 324 return (m); 325} 326 327void 328ifq_deq_commit(struct ifqueue *ifq, struct mbuf *m) 329{ 330 void *cookie; 331 332 KASSERT(m != NULL); 333 cookie = m->m_pkthdr.ph_cookie; 334 335 ifq->ifq_ops->ifqop_deq_commit(ifq, m, cookie); 336 ifq->ifq_len--; 337 ifq_deq_leave(ifq); 338} 339 340void 341ifq_deq_rollback(struct ifqueue *ifq, struct mbuf *m) 342{ 343 KASSERT(m != NULL); 344 345 ifq_deq_leave(ifq); 346} 347 348struct mbuf * 349ifq_dequeue(struct ifqueue *ifq) 350{ 351 struct mbuf *m; 352 353 m = ifq_deq_begin(ifq); 354 if (m == NULL) 355 return (NULL); 356 357 ifq_deq_commit(ifq, m); 358 359 return (m); 360} 361 362unsigned int 363ifq_purge(struct ifqueue *ifq) 364{ 365 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 366 unsigned int rv; 367 368 mtx_enter(&ifq->ifq_mtx); 369 ifq->ifq_ops->ifqop_purge(ifq, &ml); 370 rv = ifq->ifq_len; 371 ifq->ifq_len = 0; 372 ifq->ifq_qdrops += rv; 373 mtx_leave(&ifq->ifq_mtx); 374 375 KASSERT(rv == ml_len(&ml)); 376 377 ml_purge(&ml); 378 379 return (rv); 380} 381 382void * 383ifq_q_enter(struct ifqueue *ifq, const struct ifq_ops *ops) 384{ 385 mtx_enter(&ifq->ifq_mtx); 386 if (ifq->ifq_ops == ops) 387 return (ifq->ifq_q); 388 389 mtx_leave(&ifq->ifq_mtx); 390 391 return (NULL); 392} 393 394void 395ifq_q_leave(struct ifqueue *ifq, void *q) 396{ 397 KASSERT(q == ifq->ifq_q); 398 mtx_leave(&ifq->ifq_mtx); 399} 400 401void 402ifq_mfreem(struct ifqueue *ifq, struct mbuf *m) 403{ 404 MUTEX_ASSERT_LOCKED(&ifq->ifq_mtx); 405 406 ifq->ifq_len--; 407 ifq->ifq_qdrops++; 408 ml_enqueue(&ifq->ifq_free, m); 409} 410 411void 412ifq_mfreeml(struct ifqueue *ifq, struct mbuf_list *ml) 413{ 414 MUTEX_ASSERT_LOCKED(&ifq->ifq_mtx); 415 416 ifq->ifq_len -= ml_len(ml); 417 ifq->ifq_qdrops += ml_len(ml); 418 ml_enlist(&ifq->ifq_free, ml); 419} 420 421/* 422 * priq implementation 423 */ 424 425unsigned int 426priq_idx(unsigned int nqueues, const struct mbuf *m) 427{ 428 unsigned int flow = 0; 429 430 if (ISSET(m->m_pkthdr.ph_flowid, M_FLOWID_VALID)) 431 flow = m->m_pkthdr.ph_flowid & M_FLOWID_MASK; 432 433 return (flow % nqueues); 434} 435 436void * 437priq_alloc(unsigned int idx, void *null) 438{ 439 struct priq *pq; 440 int i; 441 442 pq = malloc(sizeof(struct priq), M_DEVBUF, M_WAITOK); 443 for (i = 0; i < IFQ_NQUEUES; i++) 444 ml_init(&pq->pq_lists[i]); 445 return (pq); 446} 447 448void 449priq_free(unsigned int idx, void *pq) 450{ 451 free(pq, M_DEVBUF, sizeof(struct priq)); 452} 453 454struct mbuf * 455priq_enq(struct ifqueue *ifq, struct mbuf *m) 456{ 457 struct priq *pq; 458 struct mbuf_list *pl; 459 struct mbuf *n = NULL; 460 unsigned int prio; 461 462 pq = ifq->ifq_q; 463 KASSERT(m->m_pkthdr.pf.prio <= IFQ_MAXPRIO); 464 465 /* Find a lower priority queue to drop from */ 466 if (ifq_len(ifq) >= ifq->ifq_maxlen) { 467 for (prio = 0; prio < m->m_pkthdr.pf.prio; prio++) { 468 pl = &pq->pq_lists[prio]; 469 if (ml_len(pl) > 0) { 470 n = ml_dequeue(pl); 471 goto enqueue; 472 } 473 } 474 /* 475 * There's no lower priority queue that we can 476 * drop from so don't enqueue this one. 477 */ 478 return (m); 479 } 480 481 enqueue: 482 pl = &pq->pq_lists[m->m_pkthdr.pf.prio]; 483 ml_enqueue(pl, m); 484 485 return (n); 486} 487 488struct mbuf * 489priq_deq_begin(struct ifqueue *ifq, void **cookiep) 490{ 491 struct priq *pq = ifq->ifq_q; 492 struct mbuf_list *pl; 493 unsigned int prio = nitems(pq->pq_lists); 494 struct mbuf *m; 495 496 do { 497 pl = &pq->pq_lists[--prio]; 498 m = MBUF_LIST_FIRST(pl); 499 if (m != NULL) { 500 *cookiep = pl; 501 return (m); 502 } 503 } while (prio > 0); 504 505 return (NULL); 506} 507 508void 509priq_deq_commit(struct ifqueue *ifq, struct mbuf *m, void *cookie) 510{ 511 struct mbuf_list *pl = cookie; 512 513 KASSERT(MBUF_LIST_FIRST(pl) == m); 514 515 ml_dequeue(pl); 516} 517 518void 519priq_purge(struct ifqueue *ifq, struct mbuf_list *ml) 520{ 521 struct priq *pq = ifq->ifq_q; 522 struct mbuf_list *pl; 523 unsigned int prio = nitems(pq->pq_lists); 524 525 do { 526 pl = &pq->pq_lists[--prio]; 527 ml_enlist(ml, pl); 528 } while (prio > 0); 529} 530