Before this patch the kernel chose the lowest available number for
newly created datapath ports.  This patch moves the port number
choosing responsibility to user space, and implements a least
recently used port number queue in an attempt to avoid reuse.

Bug #2140.
---
 lib/dpif-linux.c |   65 +++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 60 insertions(+), 5 deletions(-)

diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
index b5590c4..464ac88 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -52,6 +52,10 @@
 
 VLOG_DEFINE_THIS_MODULE(dpif_linux);
 
+enum { LRU_MAX_PORTS = 1024 };
+enum { LRU_MASK = LRU_MAX_PORTS - 1};
+BUILD_ASSERT_DECL(IS_POW2(LRU_MAX_PORTS));
+
 struct dpif_linux_dp {
     /* Generic Netlink header. */
     uint8_t cmd;
@@ -128,6 +132,11 @@ struct dpif_linux {
     struct sset changed_ports;  /* Ports that have changed. */
     struct rtnetlink_notifier port_notifier;
     bool change_error;
+
+    /* Queue of unused ports. */
+    uint16_t lru_ports[LRU_MAX_PORTS];
+    size_t lru_head;
+    size_t lru_tail;
 };
 
 static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
@@ -158,6 +167,36 @@ dpif_linux_cast(const struct dpif *dpif)
     return CONTAINER_OF(dpif, struct dpif_linux, dpif);
 }
 
+static void
+dpif_linux_push_port(struct dpif_linux *dp, uint16_t port)
+{
+    size_t i;
+
+    if (dp->lru_head - dp->lru_tail >= LRU_MAX_PORTS) {
+        return;
+    }
+
+    /* XXX: Replace this loop with a bitmap indicating which ports are in the
+     * queue. */
+    for (i = dp->lru_tail; i != dp->lru_head; i++) {
+        if (dp->lru_ports[i & LRU_MASK] == port) {
+            return;
+        }
+    }
+
+    dp->lru_ports[dp->lru_head++ & LRU_MASK] = port;
+}
+
+static uint32_t
+dpif_linux_pop_port(struct dpif_linux *dp)
+{
+    if (dp->lru_head == dp->lru_tail) {
+        return UINT32_MAX;
+    }
+
+    return dp->lru_ports[dp->lru_tail++ & LRU_MASK];
+}
+
 static int
 dpif_linux_enumerate(struct sset *all_dps)
 {
@@ -235,6 +274,10 @@ open_dpif(const struct dpif_linux_dp *dp, struct dpif 
**dpifp)
     dpif->change_error = false;
     *dpifp = &dpif->dpif;
 
+    dpif->lru_head = dpif->lru_tail = 0;
+    for (i = 1; i < LRU_MAX_PORTS; i++) {
+        dpif_linux_push_port(dpif, i);
+    }
     return 0;
 
 error_free:
@@ -336,11 +379,17 @@ dpif_linux_port_add(struct dpif *dpif_, struct netdev 
*netdev,
         request.options_len = options->size;
     }
 
-    error = dpif_linux_vport_transact(&request, &reply, &buf);
-    if (!error) {
-        *port_nop = reply.port_no;
+    /* Loop until we find a port that isn't used. */
+    do {
+        request.port_no = dpif_linux_pop_port(dpif);
+        error = dpif_linux_vport_transact(&request, &reply, &buf);
+
+        if (!error) {
+            *port_nop = reply.port_no;
+        }
         ofpbuf_delete(buf);
-    }
+    } while (request.port_no != UINT32_MAX
+             && (error == EBUSY || error == EFBIG));
 
     return error;
 }
@@ -350,12 +399,18 @@ dpif_linux_port_del(struct dpif *dpif_, uint16_t port_no)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
     struct dpif_linux_vport vport;
+    int error;
 
     dpif_linux_vport_init(&vport);
     vport.cmd = ODP_VPORT_CMD_DEL;
     vport.dp_ifindex = dpif->dp_ifindex;
     vport.port_no = port_no;
-    return dpif_linux_vport_transact(&vport, NULL, NULL);
+    error = dpif_linux_vport_transact(&vport, NULL, NULL);
+
+    if (!error) {
+        dpif_linux_push_port(dpif, port_no);
+    }
+    return error;
 }
 
 static int
-- 
1.7.4.2

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to