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 | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 51 insertions(+), 1 deletions(-) diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c index b5590c4..2887fc4 100644 --- a/lib/dpif-linux.c +++ b/lib/dpif-linux.c @@ -52,6 +52,10 @@ VLOG_DEFINE_THIS_MODULE(dpif_linux); +enum { DP_MAX_PORTS = 1024 }; +enum { LRU_MASK = DP_MAX_PORTS - 1}; +BUILD_ASSERT_DECL(IS_POW2(DP_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[DP_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,35 @@ 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; + + /* Currently this is an O(n) operation. It could be easily optimized with + * a hash table, however dpif_linux_push_port() is only called at + * initialization and port deletion time. */ + for (i = dp->lru_tail; i < dp->lru_head; i++) { + if (dp->lru_ports[i & LRU_MASK] == port) { + return; + } + } + + assert(dp->lru_head - dp->lru_tail < DP_MAX_PORTS); + dp->lru_ports[dp->lru_head++ & LRU_MASK] = port; +} + +static bool +dpif_linux_pop_port(struct dpif_linux *dp, uint16_t *port) +{ + if (dp->lru_head == dp->lru_tail) { + return false; + } + + *port = dp->lru_ports[dp->lru_tail++ & LRU_MASK]; + return true; +} + static int dpif_linux_enumerate(struct sset *all_dps) { @@ -235,6 +273,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 < DP_MAX_PORTS; i++) { + dpif_linux_push_port(dpif, i); + } return 0; error_free: @@ -336,7 +378,14 @@ dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev, request.options_len = options->size; } - error = dpif_linux_vport_transact(&request, &reply, &buf); + /* Loop until we find a port that isn't used. */ + do { + uint16_t port; + + request.port_no = dpif_linux_pop_port(dpif, &port) ? port : UINT32_MAX; + error = dpif_linux_vport_transact(&request, &reply, &buf); + } while (error == EBUSY); + if (!error) { *port_nop = reply.port_no; ofpbuf_delete(buf); @@ -355,6 +404,7 @@ dpif_linux_port_del(struct dpif *dpif_, uint16_t port_no) vport.cmd = ODP_VPORT_CMD_DEL; vport.dp_ifindex = dpif->dp_ifindex; vport.port_no = port_no; + dpif_linux_push_port(dpif, port_no); return dpif_linux_vport_transact(&vport, NULL, NULL); } -- 1.7.4.2 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev