ifq.c revision 1.1
1/* $OpenBSD: ifq.c,v 1.1 2015/12/08 10:06:12 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#include <sys/atomic.h> 25 26#include <net/if.h> 27#include <net/if_var.h> 28 29/* 30 * priq glue 31 */ 32void *priq_alloc(void *); 33void priq_free(void *); 34int priq_enq(struct ifqueue *, struct mbuf *); 35struct mbuf *priq_deq_begin(struct ifqueue *, void **); 36void priq_deq_commit(struct ifqueue *, struct mbuf *, void *); 37void priq_purge(struct ifqueue *, struct mbuf_list *); 38 39const struct ifq_ops priq_ops = { 40 priq_alloc, 41 priq_free, 42 priq_enq, 43 priq_deq_begin, 44 priq_deq_commit, 45 priq_purge, 46}; 47 48const struct ifq_ops * const ifq_priq_ops = &priq_ops; 49 50/* 51 * priq internal structures 52 */ 53 54struct priq_list { 55 struct mbuf *head; 56 struct mbuf *tail; 57}; 58 59struct priq { 60 struct priq_list pq_lists[IFQ_NQUEUES]; 61}; 62 63/* 64 * ifqueue serialiser 65 */ 66 67static inline unsigned int 68ifq_enter(struct ifqueue *ifq) 69{ 70 return (atomic_inc_int_nv(&ifq->ifq_serializer) == 1); 71} 72 73static inline unsigned int 74ifq_leave(struct ifqueue *ifq) 75{ 76 if (atomic_cas_uint(&ifq->ifq_serializer, 1, 0) == 1) 77 return (1); 78 79 ifq->ifq_serializer = 1; 80 81 return (0); 82} 83 84void 85if_start_mpsafe(struct ifnet *ifp) 86{ 87 struct ifqueue *ifq = &ifp->if_snd; 88 89 if (!ifq_enter(ifq)) 90 return; 91 92 do { 93 if (__predict_false(!ISSET(ifp->if_flags, IFF_RUNNING))) { 94 ifq->ifq_serializer = 0; 95 wakeup_one(&ifq->ifq_serializer); 96 return; 97 } 98 99 if (ifq_empty(ifq) || ifq_is_oactive(ifq)) 100 continue; 101 102 ifp->if_start(ifp); 103 104 } while (!ifq_leave(ifq)); 105} 106 107void 108if_start_barrier(struct ifnet *ifp) 109{ 110 struct sleep_state sls; 111 struct ifqueue *ifq = &ifp->if_snd; 112 113 /* this should only be called from converted drivers */ 114 KASSERT(ISSET(ifp->if_xflags, IFXF_MPSAFE)); 115 116 /* drivers should only call this on the way down */ 117 KASSERT(!ISSET(ifp->if_flags, IFF_RUNNING)); 118 119 if (ifq->ifq_serializer == 0) 120 return; 121 122 if_start_mpsafe(ifp); /* spin the wheel to guarantee a wakeup */ 123 do { 124 sleep_setup(&sls, &ifq->ifq_serializer, PWAIT, "ifbar"); 125 sleep_finish(&sls, ifq->ifq_serializer != 0); 126 } while (ifq->ifq_serializer != 0); 127} 128 129/* 130 * ifqueue mbuf queue API 131 */ 132 133void 134ifq_init(struct ifqueue *ifq) 135{ 136 mtx_init(&ifq->ifq_mtx, IPL_NET); 137 ifq->ifq_drops = 0; 138 139 /* default to priq */ 140 ifq->ifq_ops = &priq_ops; 141 ifq->ifq_q = priq_ops.ifqop_alloc(NULL); 142 143 ifq->ifq_serializer = 0; 144 ifq->ifq_len = 0; 145 146 if (ifq->ifq_maxlen == 0) 147 ifq_set_maxlen(ifq, IFQ_MAXLEN); 148} 149 150void 151ifq_attach(struct ifqueue *ifq, const struct ifq_ops *newops, void *opsarg) 152{ 153 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 154 struct mbuf_list free_ml = MBUF_LIST_INITIALIZER(); 155 struct mbuf *m; 156 const struct ifq_ops *oldops; 157 void *newq, *oldq; 158 159 newq = newops->ifqop_alloc(opsarg); 160 161 mtx_enter(&ifq->ifq_mtx); 162 ifq->ifq_ops->ifqop_purge(ifq, &ml); 163 ifq->ifq_len = 0; 164 165 oldops = ifq->ifq_ops; 166 oldq = ifq->ifq_q; 167 168 ifq->ifq_ops = newops; 169 ifq->ifq_q = newq; 170 171 while ((m = ml_dequeue(&ml)) != NULL) { 172 if (ifq->ifq_ops->ifqop_enq(ifq, m) != 0) { 173 ifq->ifq_drops++; 174 ml_enqueue(&free_ml, m); 175 } else 176 ifq->ifq_len++; 177 } 178 mtx_leave(&ifq->ifq_mtx); 179 180 oldops->ifqop_free(oldq); 181 182 ml_purge(&free_ml); 183} 184 185void 186ifq_destroy(struct ifqueue *ifq) 187{ 188 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 189 190 /* don't need to lock because this is the last use of the ifq */ 191 192 ifq->ifq_ops->ifqop_purge(ifq, &ml); 193 ifq->ifq_ops->ifqop_free(ifq->ifq_q); 194 195 ml_purge(&ml); 196} 197 198int 199ifq_enqueue_try(struct ifqueue *ifq, struct mbuf *m) 200{ 201 int rv; 202 203 mtx_enter(&ifq->ifq_mtx); 204 rv = ifq->ifq_ops->ifqop_enq(ifq, m); 205 if (rv == 0) 206 ifq->ifq_len++; 207 else 208 ifq->ifq_drops++; 209 mtx_leave(&ifq->ifq_mtx); 210 211 return (rv); 212} 213 214int 215ifq_enqueue(struct ifqueue *ifq, struct mbuf *m) 216{ 217 int err; 218 219 err = ifq_enqueue_try(ifq, m); 220 if (err != 0) 221 m_freem(m); 222 223 return (err); 224} 225 226struct mbuf * 227ifq_deq_begin(struct ifqueue *ifq) 228{ 229 struct mbuf *m = NULL; 230 void *cookie; 231 232 mtx_enter(&ifq->ifq_mtx); 233 if (ifq->ifq_len == 0 || 234 (m = ifq->ifq_ops->ifqop_deq_begin(ifq, &cookie)) == NULL) { 235 mtx_leave(&ifq->ifq_mtx); 236 return (NULL); 237 } 238 239 m->m_pkthdr.ph_cookie = cookie; 240 241 return (m); 242} 243 244void 245ifq_deq_commit(struct ifqueue *ifq, struct mbuf *m) 246{ 247 void *cookie; 248 249 KASSERT(m != NULL); 250 cookie = m->m_pkthdr.ph_cookie; 251 252 ifq->ifq_ops->ifqop_deq_commit(ifq, m, cookie); 253 ifq->ifq_len--; 254 mtx_leave(&ifq->ifq_mtx); 255} 256 257void 258ifq_deq_rollback(struct ifqueue *ifq, struct mbuf *m) 259{ 260 KASSERT(m != NULL); 261 262 mtx_leave(&ifq->ifq_mtx); 263} 264 265struct mbuf * 266ifq_dequeue(struct ifqueue *ifq) 267{ 268 struct mbuf *m; 269 270 m = ifq_deq_begin(ifq); 271 if (m == NULL) 272 return (NULL); 273 274 ifq_deq_commit(ifq, m); 275 276 return (m); 277} 278 279unsigned int 280ifq_purge(struct ifqueue *ifq) 281{ 282 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 283 unsigned int rv; 284 285 mtx_enter(&ifq->ifq_mtx); 286 ifq->ifq_ops->ifqop_purge(ifq, &ml); 287 rv = ifq->ifq_len; 288 ifq->ifq_len = 0; 289 ifq->ifq_drops += rv; 290 mtx_leave(&ifq->ifq_mtx); 291 292 KASSERT(rv == ml_len(&ml)); 293 294 ml_purge(&ml); 295 296 return (rv); 297} 298 299void * 300ifq_q_enter(struct ifqueue *ifq, const struct ifq_ops *ops) 301{ 302 mtx_enter(&ifq->ifq_mtx); 303 if (ifq->ifq_ops == ops) 304 return (ifq->ifq_q); 305 306 mtx_leave(&ifq->ifq_mtx); 307 308 return (NULL); 309} 310 311void 312ifq_q_leave(struct ifqueue *ifq, void *q) 313{ 314 KASSERT(q == ifq->ifq_q); 315 mtx_leave(&ifq->ifq_mtx); 316} 317 318/* 319 * priq implementation 320 */ 321 322void * 323priq_alloc(void *null) 324{ 325 return (malloc(sizeof(struct priq), M_DEVBUF, M_WAITOK | M_ZERO)); 326} 327 328void 329priq_free(void *pq) 330{ 331 free(pq, M_DEVBUF, sizeof(struct priq)); 332} 333 334int 335priq_enq(struct ifqueue *ifq, struct mbuf *m) 336{ 337 struct priq *pq; 338 struct priq_list *pl; 339 340 if (ifq_len(ifq) >= ifq->ifq_maxlen) 341 return (ENOBUFS); 342 343 pq = ifq->ifq_q; 344 KASSERT(m->m_pkthdr.pf.prio <= IFQ_MAXPRIO); 345 pl = &pq->pq_lists[m->m_pkthdr.pf.prio]; 346 347 m->m_nextpkt = NULL; 348 if (pl->tail == NULL) 349 pl->head = m; 350 else 351 pl->tail->m_nextpkt = m; 352 pl->tail = m; 353 354 return (0); 355} 356 357struct mbuf * 358priq_deq_begin(struct ifqueue *ifq, void **cookiep) 359{ 360 struct priq *pq = ifq->ifq_q; 361 struct priq_list *pl; 362 unsigned int prio = nitems(pq->pq_lists); 363 struct mbuf *m; 364 365 do { 366 pl = &pq->pq_lists[--prio]; 367 m = pl->head; 368 if (m != NULL) { 369 *cookiep = pl; 370 return (m); 371 } 372 } while (prio > 0); 373 374 return (NULL); 375} 376 377void 378priq_deq_commit(struct ifqueue *ifq, struct mbuf *m, void *cookie) 379{ 380 struct priq_list *pl = cookie; 381 382 KASSERT(pl->head == m); 383 384 pl->head = m->m_nextpkt; 385 m->m_nextpkt = NULL; 386 387 if (pl->head == NULL) 388 pl->tail = NULL; 389} 390 391void 392priq_purge(struct ifqueue *ifq, struct mbuf_list *ml) 393{ 394 struct priq *pq = ifq->ifq_q; 395 struct priq_list *pl; 396 unsigned int prio = nitems(pq->pq_lists); 397 struct mbuf *m, *n; 398 399 do { 400 pl = &pq->pq_lists[--prio]; 401 402 for (m = pl->head; m != NULL; m = n) { 403 n = m->m_nextpkt; 404 ml_enqueue(ml, m); 405 } 406 407 pl->head = pl->tail = NULL; 408 } while (prio > 0); 409} 410