diff options
author | Rafael Zalamena <rzalamena@cvs.openbsd.org> | 2016-11-10 17:32:41 +0000 |
---|---|---|
committer | Rafael Zalamena <rzalamena@cvs.openbsd.org> | 2016-11-10 17:32:41 +0000 |
commit | a24f4ad6bf784bec793703e7c6a3521ff21bc13f (patch) | |
tree | 5ddf09582fe6da939b9872b4a92ecab35bb4df14 | |
parent | 8eb0fd931da31d1e8f5f462270564567d730a6ec (diff) |
Add support for partial writes in switchwrite so we can use multiple
write() to write one packet. With this we also get support for writing
multiple ofp packets with a single write.
ok mikeb@
-rw-r--r-- | sys/net/if_switch.c | 31 | ||||
-rw-r--r-- | sys/net/if_switch.h | 4 | ||||
-rw-r--r-- | sys/net/switchctl.c | 105 | ||||
-rw-r--r-- | sys/net/switchofp.c | 17 |
4 files changed, 122 insertions, 35 deletions
diff --git a/sys/net/if_switch.c b/sys/net/if_switch.c index 28f1f156e09..28aeed4a5e9 100644 --- a/sys/net/if_switch.c +++ b/sys/net/if_switch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_switch.c,v 1.11 2016/10/28 09:04:03 rzalamena Exp $ */ +/* $OpenBSD: if_switch.c,v 1.12 2016/11/10 17:32:40 rzalamena Exp $ */ /* * Copyright (c) 2016 Kazuya GODA <goda@openbsd.org> @@ -1527,3 +1527,32 @@ switch_flow_classifier_dump(struct switch_softc *sc, addlog("\n"); } + +int +ofp_split_mbuf(struct mbuf *m, struct mbuf **mtail) +{ + struct ofp_header *oh; + uint16_t ohlen; + + *mtail = NULL; + + /* We need more data. */ + if (m->m_pkthdr.len < sizeof(*oh)) + return (-1); + + oh = mtod(m, struct ofp_header *); + ohlen = ntohs(oh->oh_length); + + /* Nothing to split. */ + if (m->m_pkthdr.len == ohlen) + return (0); + else if (m->m_pkthdr.len < ohlen) + return (-1); + + *mtail = m_split(m, ohlen, M_NOWAIT); + /* No memory, try again later. */ + if (*mtail == NULL) + return (-1); + + return (0); +} diff --git a/sys/net/if_switch.h b/sys/net/if_switch.h index 2256dc960c5..da2115476ef 100644 --- a/sys/net/if_switch.h +++ b/sys/net/if_switch.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_switch.h,v 1.6 2016/11/08 19:11:57 rzalamena Exp $ */ +/* $OpenBSD: if_switch.h,v 1.7 2016/11/10 17:32:40 rzalamena Exp $ */ /* * Copyright (c) 2016 Kazuya GODA <goda@openbsd.org> @@ -182,6 +182,7 @@ TAILQ_HEAD(switch_fwdp_queue, switch_port); struct switch_dev { struct mbuf *swdev_lastm; + struct mbuf *swdev_inputm; struct mbuf_queue swdev_outq; struct selinfo swdev_rsel; struct mutex swdev_rsel_mtx; @@ -220,6 +221,7 @@ void switch_swfcl_free(struct switch_flow_classify *); struct mbuf *switch_flow_classifier(struct mbuf *, uint32_t, struct switch_flow_classify *); +int ofp_split_mbuf(struct mbuf *, struct mbuf **); /* switchctl.c */ void switch_dev_destroy(struct switch_softc *); diff --git a/sys/net/switchctl.c b/sys/net/switchctl.c index 7ac544b86bb..fb65665b44f 100644 --- a/sys/net/switchctl.c +++ b/sys/net/switchctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: switchctl.c,v 1.6 2016/11/09 12:26:55 rzalamena Exp $ */ +/* $OpenBSD: switchctl.c,v 1.7 2016/11/10 17:32:40 rzalamena Exp $ */ /* * Copyright (c) 2016 Kazuya GODA <goda@openbsd.org> @@ -197,39 +197,94 @@ int switchwrite(dev_t dev, struct uio *uio, int ioflag) { struct switch_softc *sc = NULL; - int s, error; - u_int len; - struct mbuf *m; + struct mbuf *m, *n, *mhead, *mtail = NULL; + int s, error, trailing; + size_t len; + + if (uio->uio_resid == 0) + return (0); - if (uio->uio_resid == 0 || uio->uio_resid > MAXMCLBYTES) - return (EMSGSIZE); len = uio->uio_resid; - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) - return (ENOBUFS); - if (len >= MHLEN) { - MCLGETI(m, M_DONTWAIT, NULL, len); - if ((m->m_flags & M_EXT) == 0) { - m_free(m); + sc = switch_dev2sc(dev); + if (sc == NULL) + return (ENXIO); + + if (sc->sc_swdev->swdev_inputm == NULL) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) return (ENOBUFS); + if (len >= MHLEN) { + MCLGETI(m, M_DONTWAIT, NULL, MIN(MAXMCLBYTES, len)); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + return (ENOBUFS); + } } + mhead = m; + + /* M_TRAILINGSPACE() uses this to calculate space. */ + m->m_len = 0; + } else { + /* Recover the mbuf from the last write and get its tail. */ + mhead = sc->sc_swdev->swdev_inputm; + for (m = mhead; m->m_next != NULL; m = m->m_next) + /* NOTHING */; + + sc->sc_swdev->swdev_inputm = NULL; } - error = uiomove(mtod(m, caddr_t), len, uio); - if (error) { - m_freem(m); - return (error); + while (len) { + trailing = ulmin(M_TRAILINGSPACE(m), len); + if ((error = uiomove(mtod(m, caddr_t), trailing, uio)) != 0) + goto save_return; + + len -= trailing; + mhead->m_pkthdr.len += trailing; + m->m_len += trailing; + if (len == 0) + break; + + MGET(n, M_DONTWAIT, MT_DATA); + if (n == NULL) { + error = ENOBUFS; + goto save_return; + } + if (len >= MLEN) { + MCLGETI(n, M_DONTWAIT, NULL, MIN(MAXMCLBYTES, len)); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + error = ENOBUFS; + goto save_return; + } + } + n->m_len = 0; + + m->m_next = n; + m = n; } - m->m_pkthdr.len = m->m_len = len; - sc = switch_dev2sc(dev); - if (sc == NULL) - return (ENXIO); - s = splnet(); - error = sc->sc_swdev->swdev_input(sc, m); - splx(s); + /* Loop until there is no more complete OFP packets. */ + while (ofp_split_mbuf(mhead, &mtail) == 0) { + s = splnet(); + sc->sc_swdev->swdev_input(sc, mhead); + splx(s); + + /* We wrote everything, just quit. */ + if (mtail == NULL) + return (0); + + mhead = mtail; + } + + /* Save the head, because ofp_split_mbuf failed. */ + sc->sc_swdev->swdev_inputm = mhead; + + return (0); + save_return: + /* Save it so user can recover from errors later. */ + sc->sc_swdev->swdev_inputm = mhead; return (error); } @@ -260,6 +315,7 @@ switchclose(dev_t dev, int flags, int mode, struct proc *p) sc = switch_lookup(minor(dev)); if (sc != NULL && sc->sc_swdev != NULL) { m_freem(sc->sc_swdev->swdev_lastm); + m_freem(sc->sc_swdev->swdev_inputm); mq_purge(&sc->sc_swdev->swdev_outq); free(sc->sc_swdev, M_DEVBUF, sizeof(struct switch_dev)); sc->sc_swdev = NULL; @@ -286,6 +342,7 @@ switch_dev_destroy(struct switch_softc *sc) splx(s); m_freem(sc->sc_swdev->swdev_lastm); + m_freem(sc->sc_swdev->swdev_inputm); mq_purge(&sc->sc_swdev->swdev_outq); free(sc->sc_swdev, M_DEVBUF, sizeof(struct switch_dev)); sc->sc_swdev = NULL; diff --git a/sys/net/switchofp.c b/sys/net/switchofp.c index 2dd37f4b6f2..4cbfcb10883 100644 --- a/sys/net/switchofp.c +++ b/sys/net/switchofp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: switchofp.c,v 1.29 2016/11/10 14:10:48 rzalamena Exp $ */ +/* $OpenBSD: switchofp.c,v 1.30 2016/11/10 17:32:40 rzalamena Exp $ */ /* * Copyright (c) 2016 Kazuya GODA <goda@openbsd.org> @@ -4465,15 +4465,14 @@ swofp_input(struct switch_softc *sc, struct mbuf *m) struct ofp_header *oh; ofp_msg_handler handler; - oh = mtod(m, struct ofp_header *); + if (m->m_len < sizeof(*oh) && + (m = m_pullup(m, sizeof(*oh))) == NULL) + return (ENOBUFS); - if (ntohs(oh->oh_length) != m->m_pkthdr.len) { - DPRINTF(sc, "message length is wrong. " - "header length %d, actual length %d\n", - ntohs(oh->oh_length), m->m_pkthdr.len); - m_freem(m); - return (EMSGSIZE); - } + oh = mtod(m, struct ofp_header *); + if (m->m_len < ntohs(oh->oh_length) && + (m = m_pullup(m, ntohs(oh->oh_length))) == NULL) + return (ENOBUFS); VDPRINTF(sc, "received ofp message type=%s xid=%x len=%d\n", swofp_mtype_str(oh->oh_type), ntohl(oh->oh_xid), |