ifq.c revision 1.16
1/* $OpenBSD: ifq.c,v 1.16 2017/12/14 00:45:16 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 cond c = COND_INITIALIZER(); 137 struct task t = TASK_INITIALIZER(ifq_barrier_task, &c); 138 139 /* this should only be called from converted drivers */ 140 KASSERT(ISSET(ifq->ifq_if->if_xflags, IFXF_MPSAFE)); 141 142 if (ifq->ifq_serializer == NULL) 143 return; 144 145 ifq_serialize(ifq, &t); 146 147 cond_wait(&c, "ifqbar"); 148} 149 150void 151ifq_barrier_task(void *p) 152{ 153 unsigned int *notdone = p; 154 155 *notdone = 0; 156 wakeup_one(notdone); 157} 158 159/* 160 * ifqueue mbuf queue API 161 */ 162 163void 164ifq_init(struct ifqueue *ifq, struct ifnet *ifp, unsigned int idx) 165{ 166 ifq->ifq_if = ifp; 167 ifq->ifq_softc = NULL; 168 169 mtx_init(&ifq->ifq_mtx, IPL_NET); 170 ifq->ifq_qdrops = 0; 171 172 /* default to priq */ 173 ifq->ifq_ops = &priq_ops; 174 ifq->ifq_q = priq_ops.ifqop_alloc(idx, NULL); 175 176 ml_init(&ifq->ifq_free); 177 ifq->ifq_len = 0; 178 179 ifq->ifq_packets = 0; 180 ifq->ifq_bytes = 0; 181 ifq->ifq_qdrops = 0; 182 ifq->ifq_errors = 0; 183 ifq->ifq_mcasts = 0; 184 185 mtx_init(&ifq->ifq_task_mtx, IPL_NET); 186 TAILQ_INIT(&ifq->ifq_task_list); 187 ifq->ifq_serializer = NULL; 188 189 task_set(&ifq->ifq_start, ifq_start_task, ifq); 190 task_set(&ifq->ifq_restart, ifq_restart_task, ifq); 191 192 if (ifq->ifq_maxlen == 0) 193 ifq_set_maxlen(ifq, IFQ_MAXLEN); 194 195 ifq->ifq_idx = idx; 196} 197 198void 199ifq_attach(struct ifqueue *ifq, const struct ifq_ops *newops, void *opsarg) 200{ 201 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 202 struct mbuf_list free_ml = MBUF_LIST_INITIALIZER(); 203 struct mbuf *m; 204 const struct ifq_ops *oldops; 205 void *newq, *oldq; 206 207 newq = newops->ifqop_alloc(ifq->ifq_idx, opsarg); 208 209 mtx_enter(&ifq->ifq_mtx); 210 ifq->ifq_ops->ifqop_purge(ifq, &ml); 211 ifq->ifq_len = 0; 212 213 oldops = ifq->ifq_ops; 214 oldq = ifq->ifq_q; 215 216 ifq->ifq_ops = newops; 217 ifq->ifq_q = newq; 218 219 while ((m = ml_dequeue(&ml)) != NULL) { 220 m = ifq->ifq_ops->ifqop_enq(ifq, m); 221 if (m != NULL) { 222 ifq->ifq_qdrops++; 223 ml_enqueue(&free_ml, m); 224 } else 225 ifq->ifq_len++; 226 } 227 mtx_leave(&ifq->ifq_mtx); 228 229 oldops->ifqop_free(ifq->ifq_idx, oldq); 230 231 ml_purge(&free_ml); 232} 233 234void 235ifq_destroy(struct ifqueue *ifq) 236{ 237 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 238 239 /* don't need to lock because this is the last use of the ifq */ 240 241 ifq->ifq_ops->ifqop_purge(ifq, &ml); 242 ifq->ifq_ops->ifqop_free(ifq->ifq_idx, ifq->ifq_q); 243 244 ml_purge(&ml); 245} 246 247void 248ifq_add_data(struct ifqueue *ifq, struct if_data *data) 249{ 250 mtx_enter(&ifq->ifq_mtx); 251 data->ifi_opackets += ifq->ifq_packets; 252 data->ifi_obytes += ifq->ifq_bytes; 253 data->ifi_oqdrops += ifq->ifq_qdrops; 254 data->ifi_omcasts += ifq->ifq_mcasts; 255 /* ifp->if_data.ifi_oerrors */ 256 mtx_leave(&ifq->ifq_mtx); 257} 258 259int 260ifq_enqueue(struct ifqueue *ifq, struct mbuf *m) 261{ 262 struct mbuf *dm; 263 264 mtx_enter(&ifq->ifq_mtx); 265 dm = ifq->ifq_ops->ifqop_enq(ifq, m); 266 if (dm != m) { 267 ifq->ifq_packets++; 268 ifq->ifq_bytes += m->m_pkthdr.len; 269 if (ISSET(m->m_flags, M_MCAST)) 270 ifq->ifq_mcasts++; 271 } 272 273 if (dm == NULL) 274 ifq->ifq_len++; 275 else 276 ifq->ifq_qdrops++; 277 mtx_leave(&ifq->ifq_mtx); 278 279 if (dm != NULL) 280 m_freem(dm); 281 282 return (dm == m ? ENOBUFS : 0); 283} 284 285static inline void 286ifq_deq_enter(struct ifqueue *ifq) 287{ 288 mtx_enter(&ifq->ifq_mtx); 289} 290 291static inline void 292ifq_deq_leave(struct ifqueue *ifq) 293{ 294 struct mbuf_list ml; 295 296 ml = ifq->ifq_free; 297 ml_init(&ifq->ifq_free); 298 299 mtx_leave(&ifq->ifq_mtx); 300 301 if (!ml_empty(&ml)) 302 ml_purge(&ml); 303} 304 305struct mbuf * 306ifq_deq_begin(struct ifqueue *ifq) 307{ 308 struct mbuf *m = NULL; 309 void *cookie; 310 311 ifq_deq_enter(ifq); 312 if (ifq->ifq_len == 0 || 313 (m = ifq->ifq_ops->ifqop_deq_begin(ifq, &cookie)) == NULL) { 314 ifq_deq_leave(ifq); 315 return (NULL); 316 } 317 318 m->m_pkthdr.ph_cookie = cookie; 319 320 return (m); 321} 322 323void 324ifq_deq_commit(struct ifqueue *ifq, struct mbuf *m) 325{ 326 void *cookie; 327 328 KASSERT(m != NULL); 329 cookie = m->m_pkthdr.ph_cookie; 330 331 ifq->ifq_ops->ifqop_deq_commit(ifq, m, cookie); 332 ifq->ifq_len--; 333 ifq_deq_leave(ifq); 334} 335 336void 337ifq_deq_rollback(struct ifqueue *ifq, struct mbuf *m) 338{ 339 KASSERT(m != NULL); 340 341 ifq_deq_leave(ifq); 342} 343 344struct mbuf * 345ifq_dequeue(struct ifqueue *ifq) 346{ 347 struct mbuf *m; 348 349 m = ifq_deq_begin(ifq); 350 if (m == NULL) 351 return (NULL); 352 353 ifq_deq_commit(ifq, m); 354 355 return (m); 356} 357 358unsigned int 359ifq_purge(struct ifqueue *ifq) 360{ 361 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 362 unsigned int rv; 363 364 mtx_enter(&ifq->ifq_mtx); 365 ifq->ifq_ops->ifqop_purge(ifq, &ml); 366 rv = ifq->ifq_len; 367 ifq->ifq_len = 0; 368 ifq->ifq_qdrops += rv; 369 mtx_leave(&ifq->ifq_mtx); 370 371 KASSERT(rv == ml_len(&ml)); 372 373 ml_purge(&ml); 374 375 return (rv); 376} 377 378void * 379ifq_q_enter(struct ifqueue *ifq, const struct ifq_ops *ops) 380{ 381 mtx_enter(&ifq->ifq_mtx); 382 if (ifq->ifq_ops == ops) 383 return (ifq->ifq_q); 384 385 mtx_leave(&ifq->ifq_mtx); 386 387 return (NULL); 388} 389 390void 391ifq_q_leave(struct ifqueue *ifq, void *q) 392{ 393 KASSERT(q == ifq->ifq_q); 394 mtx_leave(&ifq->ifq_mtx); 395} 396 397void 398ifq_mfreem(struct ifqueue *ifq, struct mbuf *m) 399{ 400 MUTEX_ASSERT_LOCKED(&ifq->ifq_mtx); 401 402 ifq->ifq_len--; 403 ifq->ifq_qdrops++; 404 ml_enqueue(&ifq->ifq_free, m); 405} 406 407void 408ifq_mfreeml(struct ifqueue *ifq, struct mbuf_list *ml) 409{ 410 MUTEX_ASSERT_LOCKED(&ifq->ifq_mtx); 411 412 ifq->ifq_len -= ml_len(ml); 413 ifq->ifq_qdrops += ml_len(ml); 414 ml_enlist(&ifq->ifq_free, ml); 415} 416 417/* 418 * priq implementation 419 */ 420 421unsigned int 422priq_idx(unsigned int nqueues, const struct mbuf *m) 423{ 424 unsigned int flow = 0; 425 426 if (ISSET(m->m_pkthdr.ph_flowid, M_FLOWID_VALID)) 427 flow = m->m_pkthdr.ph_flowid & M_FLOWID_MASK; 428 429 return (flow % nqueues); 430} 431 432void * 433priq_alloc(unsigned int idx, void *null) 434{ 435 struct priq *pq; 436 int i; 437 438 pq = malloc(sizeof(struct priq), M_DEVBUF, M_WAITOK); 439 for (i = 0; i < IFQ_NQUEUES; i++) 440 ml_init(&pq->pq_lists[i]); 441 return (pq); 442} 443 444void 445priq_free(unsigned int idx, void *pq) 446{ 447 free(pq, M_DEVBUF, sizeof(struct priq)); 448} 449 450struct mbuf * 451priq_enq(struct ifqueue *ifq, struct mbuf *m) 452{ 453 struct priq *pq; 454 struct mbuf_list *pl; 455 struct mbuf *n = NULL; 456 unsigned int prio; 457 458 pq = ifq->ifq_q; 459 KASSERT(m->m_pkthdr.pf.prio <= IFQ_MAXPRIO); 460 461 /* Find a lower priority queue to drop from */ 462 if (ifq_len(ifq) >= ifq->ifq_maxlen) { 463 for (prio = 0; prio < m->m_pkthdr.pf.prio; prio++) { 464 pl = &pq->pq_lists[prio]; 465 if (ml_len(pl) > 0) { 466 n = ml_dequeue(pl); 467 goto enqueue; 468 } 469 } 470 /* 471 * There's no lower priority queue that we can 472 * drop from so don't enqueue this one. 473 */ 474 return (m); 475 } 476 477 enqueue: 478 pl = &pq->pq_lists[m->m_pkthdr.pf.prio]; 479 ml_enqueue(pl, m); 480 481 return (n); 482} 483 484struct mbuf * 485priq_deq_begin(struct ifqueue *ifq, void **cookiep) 486{ 487 struct priq *pq = ifq->ifq_q; 488 struct mbuf_list *pl; 489 unsigned int prio = nitems(pq->pq_lists); 490 struct mbuf *m; 491 492 do { 493 pl = &pq->pq_lists[--prio]; 494 m = MBUF_LIST_FIRST(pl); 495 if (m != NULL) { 496 *cookiep = pl; 497 return (m); 498 } 499 } while (prio > 0); 500 501 return (NULL); 502} 503 504void 505priq_deq_commit(struct ifqueue *ifq, struct mbuf *m, void *cookie) 506{ 507 struct mbuf_list *pl = cookie; 508 509 KASSERT(MBUF_LIST_FIRST(pl) == m); 510 511 ml_dequeue(pl); 512} 513 514void 515priq_purge(struct ifqueue *ifq, struct mbuf_list *ml) 516{ 517 struct priq *pq = ifq->ifq_q; 518 struct mbuf_list *pl; 519 unsigned int prio = nitems(pq->pq_lists); 520 521 do { 522 pl = &pq->pq_lists[--prio]; 523 ml_enlist(ml, pl); 524 } while (prio > 0); 525} 526