ifq.c revision 1.10
1/* $OpenBSD: ifq.c,v 1.10 2017/05/03 03:14:32 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 mbuf_list ml = MBUF_LIST_INITIALIZER(); 74 struct task work; 75 76 if (ISSET(t->t_flags, TASK_ONQUEUE)) 77 return; 78 79 mtx_enter(&ifq->ifq_task_mtx); 80 if (!ISSET(t->t_flags, TASK_ONQUEUE)) { 81 SET(t->t_flags, TASK_ONQUEUE); 82 TAILQ_INSERT_TAIL(&ifq->ifq_task_list, t, t_entry); 83 } 84 85 if (ifq->ifq_serializer == NULL) { 86 ifq->ifq_serializer = curcpu(); 87 88 while ((t = TAILQ_FIRST(&ifq->ifq_task_list)) != NULL) { 89 TAILQ_REMOVE(&ifq->ifq_task_list, t, t_entry); 90 CLR(t->t_flags, TASK_ONQUEUE); 91 work = *t; /* copy to caller to avoid races */ 92 93 mtx_leave(&ifq->ifq_task_mtx); 94 95 (*work.t_func)(work.t_arg); 96 97 mtx_enter(&ifq->ifq_task_mtx); 98 } 99 100 /* 101 * ifq->ifq_free is only modified by dequeue, which 102 * is only called from within this serialization 103 * context. it is therefore safe to access and modify 104 * here without taking ifq->ifq_mtx. 105 */ 106 ml = ifq->ifq_free; 107 ml_init(&ifq->ifq_free); 108 109 ifq->ifq_serializer = NULL; 110 } 111 mtx_leave(&ifq->ifq_task_mtx); 112 113 ml_purge(&ml); 114} 115 116int 117ifq_is_serialized(struct ifqueue *ifq) 118{ 119 return (ifq->ifq_serializer == curcpu()); 120} 121 122void 123ifq_start_task(void *p) 124{ 125 struct ifqueue *ifq = p; 126 struct ifnet *ifp = ifq->ifq_if; 127 128 if (!ISSET(ifp->if_flags, IFF_RUNNING) || 129 ifq_empty(ifq) || ifq_is_oactive(ifq)) 130 return; 131 132 ifp->if_qstart(ifq); 133} 134 135void 136ifq_restart_task(void *p) 137{ 138 struct ifqueue *ifq = p; 139 struct ifnet *ifp = ifq->ifq_if; 140 141 ifq_clr_oactive(ifq); 142 ifp->if_qstart(ifq); 143} 144 145void 146ifq_barrier(struct ifqueue *ifq) 147{ 148 struct sleep_state sls; 149 unsigned int notdone = 1; 150 struct task t = TASK_INITIALIZER(ifq_barrier_task, ¬done); 151 152 /* this should only be called from converted drivers */ 153 KASSERT(ISSET(ifq->ifq_if->if_xflags, IFXF_MPSAFE)); 154 155 if (ifq->ifq_serializer == NULL) 156 return; 157 158 ifq_serialize(ifq, &t); 159 160 while (notdone) { 161 sleep_setup(&sls, ¬done, PWAIT, "ifqbar"); 162 sleep_finish(&sls, notdone); 163 } 164} 165 166void 167ifq_barrier_task(void *p) 168{ 169 unsigned int *notdone = p; 170 171 *notdone = 0; 172 wakeup_one(notdone); 173} 174 175/* 176 * ifqueue mbuf queue API 177 */ 178 179void 180ifq_init(struct ifqueue *ifq, struct ifnet *ifp, unsigned int idx) 181{ 182 ifq->ifq_if = ifp; 183 ifq->ifq_softc = NULL; 184 185 mtx_init(&ifq->ifq_mtx, IPL_NET); 186 ifq->ifq_qdrops = 0; 187 188 /* default to priq */ 189 ifq->ifq_ops = &priq_ops; 190 ifq->ifq_q = priq_ops.ifqop_alloc(idx, NULL); 191 192 ml_init(&ifq->ifq_free); 193 ifq->ifq_len = 0; 194 195 ifq->ifq_packets = 0; 196 ifq->ifq_bytes = 0; 197 ifq->ifq_qdrops = 0; 198 ifq->ifq_errors = 0; 199 ifq->ifq_mcasts = 0; 200 201 mtx_init(&ifq->ifq_task_mtx, IPL_NET); 202 TAILQ_INIT(&ifq->ifq_task_list); 203 ifq->ifq_serializer = NULL; 204 205 task_set(&ifq->ifq_start, ifq_start_task, ifq); 206 task_set(&ifq->ifq_restart, ifq_restart_task, ifq); 207 208 if (ifq->ifq_maxlen == 0) 209 ifq_set_maxlen(ifq, IFQ_MAXLEN); 210 211 ifq->ifq_idx = idx; 212} 213 214void 215ifq_attach(struct ifqueue *ifq, const struct ifq_ops *newops, void *opsarg) 216{ 217 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 218 struct mbuf_list free_ml = MBUF_LIST_INITIALIZER(); 219 struct mbuf *m; 220 const struct ifq_ops *oldops; 221 void *newq, *oldq; 222 223 newq = newops->ifqop_alloc(ifq->ifq_idx, opsarg); 224 225 mtx_enter(&ifq->ifq_mtx); 226 ifq->ifq_ops->ifqop_purge(ifq, &ml); 227 ifq->ifq_len = 0; 228 229 oldops = ifq->ifq_ops; 230 oldq = ifq->ifq_q; 231 232 ifq->ifq_ops = newops; 233 ifq->ifq_q = newq; 234 235 while ((m = ml_dequeue(&ml)) != NULL) { 236 m = ifq->ifq_ops->ifqop_enq(ifq, m); 237 if (m != NULL) { 238 ifq->ifq_qdrops++; 239 ml_enqueue(&free_ml, m); 240 } else 241 ifq->ifq_len++; 242 } 243 mtx_leave(&ifq->ifq_mtx); 244 245 oldops->ifqop_free(ifq->ifq_idx, oldq); 246 247 ml_purge(&free_ml); 248} 249 250void 251ifq_destroy(struct ifqueue *ifq) 252{ 253 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 254 255 /* don't need to lock because this is the last use of the ifq */ 256 257 ifq->ifq_ops->ifqop_purge(ifq, &ml); 258 ifq->ifq_ops->ifqop_free(ifq->ifq_idx, ifq->ifq_q); 259 260 ml_purge(&ml); 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 289struct mbuf * 290ifq_deq_begin(struct ifqueue *ifq) 291{ 292 struct mbuf *m = NULL; 293 void *cookie; 294 295 mtx_enter(&ifq->ifq_mtx); 296 if (ifq->ifq_len == 0 || 297 (m = ifq->ifq_ops->ifqop_deq_begin(ifq, &cookie)) == NULL) { 298 mtx_leave(&ifq->ifq_mtx); 299 return (NULL); 300 } 301 302 m->m_pkthdr.ph_cookie = cookie; 303 304 return (m); 305} 306 307void 308ifq_deq_commit(struct ifqueue *ifq, struct mbuf *m) 309{ 310 void *cookie; 311 312 KASSERT(m != NULL); 313 cookie = m->m_pkthdr.ph_cookie; 314 315 ifq->ifq_ops->ifqop_deq_commit(ifq, m, cookie); 316 ifq->ifq_len--; 317 mtx_leave(&ifq->ifq_mtx); 318} 319 320void 321ifq_deq_rollback(struct ifqueue *ifq, struct mbuf *m) 322{ 323 KASSERT(m != NULL); 324 325 mtx_leave(&ifq->ifq_mtx); 326} 327 328struct mbuf * 329ifq_dequeue(struct ifqueue *ifq) 330{ 331 struct mbuf *m; 332 333 m = ifq_deq_begin(ifq); 334 if (m == NULL) 335 return (NULL); 336 337 ifq_deq_commit(ifq, m); 338 339 return (m); 340} 341 342unsigned int 343ifq_purge(struct ifqueue *ifq) 344{ 345 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 346 unsigned int rv; 347 348 mtx_enter(&ifq->ifq_mtx); 349 ifq->ifq_ops->ifqop_purge(ifq, &ml); 350 rv = ifq->ifq_len; 351 ifq->ifq_len = 0; 352 ifq->ifq_qdrops += rv; 353 mtx_leave(&ifq->ifq_mtx); 354 355 KASSERT(rv == ml_len(&ml)); 356 357 ml_purge(&ml); 358 359 return (rv); 360} 361 362void * 363ifq_q_enter(struct ifqueue *ifq, const struct ifq_ops *ops) 364{ 365 mtx_enter(&ifq->ifq_mtx); 366 if (ifq->ifq_ops == ops) 367 return (ifq->ifq_q); 368 369 mtx_leave(&ifq->ifq_mtx); 370 371 return (NULL); 372} 373 374void 375ifq_q_leave(struct ifqueue *ifq, void *q) 376{ 377 KASSERT(q == ifq->ifq_q); 378 mtx_leave(&ifq->ifq_mtx); 379} 380 381void 382ifq_mfreem(struct ifqueue *ifq, struct mbuf *m) 383{ 384 IFQ_ASSERT_SERIALIZED(ifq); 385 386 ifq->ifq_qdrops++; 387 ml_enqueue(&ifq->ifq_free, m); 388} 389 390/* 391 * priq implementation 392 */ 393 394unsigned int 395priq_idx(unsigned int nqueues, const struct mbuf *m) 396{ 397 unsigned int flow = 0; 398 399 if (ISSET(m->m_pkthdr.ph_flowid, M_FLOWID_VALID)) 400 flow = m->m_pkthdr.ph_flowid & M_FLOWID_MASK; 401 402 return (flow % nqueues); 403} 404 405void * 406priq_alloc(unsigned int idx, void *null) 407{ 408 struct priq *pq; 409 int i; 410 411 pq = malloc(sizeof(struct priq), M_DEVBUF, M_WAITOK); 412 for (i = 0; i < IFQ_NQUEUES; i++) 413 ml_init(&pq->pq_lists[i]); 414 return (pq); 415} 416 417void 418priq_free(unsigned int idx, void *pq) 419{ 420 free(pq, M_DEVBUF, sizeof(struct priq)); 421} 422 423struct mbuf * 424priq_enq(struct ifqueue *ifq, struct mbuf *m) 425{ 426 struct priq *pq; 427 struct mbuf_list *pl; 428 struct mbuf *n = NULL; 429 unsigned int prio; 430 431 pq = ifq->ifq_q; 432 KASSERT(m->m_pkthdr.pf.prio <= IFQ_MAXPRIO); 433 434 /* Find a lower priority queue to drop from */ 435 if (ifq_len(ifq) >= ifq->ifq_maxlen) { 436 for (prio = 0; prio < m->m_pkthdr.pf.prio; prio++) { 437 pl = &pq->pq_lists[prio]; 438 if (ml_len(pl) > 0) { 439 n = ml_dequeue(pl); 440 goto enqueue; 441 } 442 } 443 /* 444 * There's no lower priority queue that we can 445 * drop from so don't enqueue this one. 446 */ 447 return (m); 448 } 449 450 enqueue: 451 pl = &pq->pq_lists[m->m_pkthdr.pf.prio]; 452 ml_enqueue(pl, m); 453 454 return (n); 455} 456 457struct mbuf * 458priq_deq_begin(struct ifqueue *ifq, void **cookiep) 459{ 460 struct priq *pq = ifq->ifq_q; 461 struct mbuf_list *pl; 462 unsigned int prio = nitems(pq->pq_lists); 463 struct mbuf *m; 464 465 do { 466 pl = &pq->pq_lists[--prio]; 467 m = MBUF_LIST_FIRST(pl); 468 if (m != NULL) { 469 *cookiep = pl; 470 return (m); 471 } 472 } while (prio > 0); 473 474 return (NULL); 475} 476 477void 478priq_deq_commit(struct ifqueue *ifq, struct mbuf *m, void *cookie) 479{ 480 struct mbuf_list *pl = cookie; 481 482 KASSERT(MBUF_LIST_FIRST(pl) == m); 483 484 ml_dequeue(pl); 485} 486 487void 488priq_purge(struct ifqueue *ifq, struct mbuf_list *ml) 489{ 490 struct priq *pq = ifq->ifq_q; 491 struct mbuf_list *pl; 492 unsigned int prio = nitems(pq->pq_lists); 493 494 do { 495 pl = &pq->pq_lists[--prio]; 496 ml_enlist(ml, pl); 497 } while (prio > 0); 498} 499