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