summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael Zalamena <rzalamena@cvs.openbsd.org>2016-11-10 17:32:41 +0000
committerRafael Zalamena <rzalamena@cvs.openbsd.org>2016-11-10 17:32:41 +0000
commita24f4ad6bf784bec793703e7c6a3521ff21bc13f (patch)
tree5ddf09582fe6da939b9872b4a92ecab35bb4df14
parent8eb0fd931da31d1e8f5f462270564567d730a6ec (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.c31
-rw-r--r--sys/net/if_switch.h4
-rw-r--r--sys/net/switchctl.c105
-rw-r--r--sys/net/switchofp.c17
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),