X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fdpif-linux.c;h=0f6a1405c3a1bd93bf925e92db5e858c441ea9ae;hb=de5cdb90f7c02d22b0595c7dc311c5306291b02f;hp=bdc7703697f1e7b9584d4b558c68e4920a72882b;hpb=d0c23a1a57f5bd994d3af124673a218e21528dd3;p=sliver-openvswitch.git diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c index bdc770369..0f6a1405c 100644 --- a/lib/dpif-linux.c +++ b/lib/dpif-linux.c @@ -32,8 +32,12 @@ #include #include +#include "bitmap.h" #include "dpif-provider.h" +#include "dynamic-string.h" +#include "flow.h" #include "netdev.h" +#include "netdev-linux.h" #include "netdev-vport.h" #include "netlink-socket.h" #include "netlink.h" @@ -52,6 +56,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 +136,12 @@ struct dpif_linux { struct sset changed_ports; /* Ports that have changed. */ struct rtnetlink_notifier port_notifier; bool change_error; + + /* Queue of unused ports. */ + unsigned long *lru_bitmap; + 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 +172,29 @@ 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) +{ + if (port < LRU_MAX_PORTS && !bitmap_is_set(dp->lru_bitmap, port)) { + bitmap_set1(dp->lru_bitmap, port); + dp->lru_ports[dp->lru_head++ & LRU_MASK] = port; + } +} + +static uint32_t +dpif_linux_pop_port(struct dpif_linux *dp) +{ + uint16_t port; + + if (dp->lru_head == dp->lru_tail) { + return UINT32_MAX; + } + + port = dp->lru_ports[dp->lru_tail++ & LRU_MASK]; + bitmap_set0(dp->lru_bitmap, port); + return port; +} + static int dpif_linux_enumerate(struct sset *all_dps) { @@ -235,6 +272,12 @@ 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; + dpif->lru_bitmap = bitmap_allocate(LRU_MAX_PORTS); + bitmap_set1(dpif->lru_bitmap, ODPP_LOCAL); + for (i = 1; i < LRU_MAX_PORTS; i++) { + dpif_linux_push_port(dpif, i); + } return 0; error_free: @@ -246,8 +289,10 @@ static void dpif_linux_close(struct dpif *dpif_) { struct dpif_linux *dpif = dpif_linux_cast(dpif_); + nl_sock_destroy(dpif->mc_sock); rtnetlink_link_notifier_unregister(&dpif->port_notifier); sset_destroy(&dpif->changed_ports); + free(dpif->lru_bitmap); free(dpif); } @@ -336,11 +381,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 +401,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 @@ -378,6 +435,12 @@ dpif_linux_port_query__(const struct dpif *dpif, uint32_t port_no, dpif_port->name = xstrdup(reply.name); dpif_port->type = xstrdup(netdev_vport_get_netdev_type(&reply)); dpif_port->port_no = reply.port_no; + if (reply.stats) { + netdev_stats_from_rtnl_link_stats64(&dpif_port->stats, + reply.stats); + } else { + memset(&dpif_port->stats, 0xff, sizeof dpif_port->stats); + } ofpbuf_delete(buf); } return error; @@ -419,6 +482,8 @@ dpif_linux_flow_flush(struct dpif *dpif_) struct dpif_linux_port_state { struct nl_dump dump; + unsigned long *port_bitmap; /* Ports in the datapath. */ + bool complete; /* Dump completed without error. */ }; static int @@ -430,6 +495,8 @@ dpif_linux_port_dump_start(const struct dpif *dpif_, void **statep) struct ofpbuf *buf; *statep = state = xmalloc(sizeof *state); + state->port_bitmap = bitmap_allocate(LRU_MAX_PORTS); + state->complete = false; dpif_linux_vport_init(&request); request.cmd = ODP_DP_CMD_GET; @@ -453,6 +520,7 @@ dpif_linux_port_dump_next(const struct dpif *dpif OVS_UNUSED, void *state_, int error; if (!nl_dump_next(&state->dump, &buf)) { + state->complete = true; return EOF; } @@ -461,17 +529,39 @@ dpif_linux_port_dump_next(const struct dpif *dpif OVS_UNUSED, void *state_, return error; } + if (vport.port_no < LRU_MAX_PORTS) { + bitmap_set1(state->port_bitmap, vport.port_no); + } + dpif_port->name = (char *) vport.name; dpif_port->type = (char *) netdev_vport_get_netdev_type(&vport); dpif_port->port_no = vport.port_no; + if (vport.stats) { + netdev_stats_from_rtnl_link_stats64(&dpif_port->stats, vport.stats); + } else { + memset(&dpif_port->stats, 0xff, sizeof dpif_port->stats); + } return 0; } static int -dpif_linux_port_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_) +dpif_linux_port_dump_done(const struct dpif *dpif_, void *state_) { + struct dpif_linux *dpif = dpif_linux_cast(dpif_); struct dpif_linux_port_state *state = state_; int error = nl_dump_done(&state->dump); + + if (state->complete) { + uint16_t i; + + for (i = 0; i < LRU_MAX_PORTS; i++) { + if (!bitmap_is_set(state->port_bitmap, i)) { + dpif_linux_push_port(dpif, i); + } + } + } + + free(state->port_bitmap); free(state); return error; } @@ -696,11 +786,11 @@ dpif_linux_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_) } static int -dpif_linux_execute(struct dpif *dpif_, - const struct nlattr *actions, size_t actions_len, - const struct ofpbuf *packet) +dpif_linux_execute__(int dp_ifindex, + const struct nlattr *key, size_t key_len, + const struct nlattr *actions, size_t actions_len, + const struct ofpbuf *packet) { - struct dpif_linux *dpif = dpif_linux_cast(dpif_); struct odp_header *execute; struct ofpbuf *buf; int error; @@ -711,9 +801,10 @@ dpif_linux_execute(struct dpif *dpif_, ODP_PACKET_CMD_EXECUTE, 1); execute = ofpbuf_put_uninit(buf, sizeof *execute); - execute->dp_ifindex = dpif->dp_ifindex; + execute->dp_ifindex = dp_ifindex; nl_msg_put_unspec(buf, ODP_PACKET_ATTR_PACKET, packet->data, packet->size); + nl_msg_put_unspec(buf, ODP_PACKET_ATTR_KEY, key, key_len); nl_msg_put_unspec(buf, ODP_PACKET_ATTR_ACTIONS, actions, actions_len); error = nl_sock_transact(genl_sock, buf, NULL); @@ -721,6 +812,18 @@ dpif_linux_execute(struct dpif *dpif_, return error; } +static int +dpif_linux_execute(struct dpif *dpif_, + const struct nlattr *key, size_t key_len, + const struct nlattr *actions, size_t actions_len, + const struct ofpbuf *packet) +{ + struct dpif_linux *dpif = dpif_linux_cast(dpif_); + + return dpif_linux_execute__(dpif->dp_ifindex, key, key_len, + actions, actions_len, packet); +} + static int dpif_linux_recv_get_mask(const struct dpif *dpif_, int *listen_mask) { @@ -941,12 +1044,12 @@ dpif_linux_recv_purge(struct dpif *dpif_) const struct dpif_class dpif_linux_class = { "system", - NULL, /* run */ - NULL, /* wait */ dpif_linux_enumerate, dpif_linux_open, dpif_linux_close, dpif_linux_destroy, + NULL, /* run */ + NULL, /* wait */ dpif_linux_get_stats, dpif_linux_get_drop_frags, dpif_linux_set_drop_frags, @@ -1019,7 +1122,7 @@ dpif_linux_is_internal_device(const char *name) error = dpif_linux_vport_get(name, &reply, &buf); if (!error) { ofpbuf_delete(buf); - } else if (error != ENODEV) { + } else if (error != ENODEV && error != ENOENT) { VLOG_WARN_RL(&error_rl, "%s: vport query failed (%s)", name, strerror(error)); } @@ -1027,6 +1130,28 @@ dpif_linux_is_internal_device(const char *name) return reply.type == ODP_VPORT_TYPE_INTERNAL; } +int +dpif_linux_vport_send(int dp_ifindex, uint32_t port_no, + const void *data, size_t size) +{ + struct ofpbuf actions, key, packet; + struct odputil_keybuf keybuf; + struct flow flow; + uint64_t action; + + ofpbuf_use_const(&packet, data, size); + flow_extract(&packet, htonll(0), 0, &flow); + + ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); + odp_flow_key_from_flow(&key, &flow); + + ofpbuf_use_stack(&actions, &action, sizeof action); + nl_msg_put_u32(&actions, ODP_ACTION_ATTR_OUTPUT, port_no); + + return dpif_linux_execute__(dp_ifindex, key.data, key.size, + actions.data, actions.size, &packet); +} + static void dpif_linux_port_changed(const struct rtnetlink_link_change *change, void *dpif_) @@ -1201,6 +1326,15 @@ dpif_linux_vport_transact(const struct dpif_linux_vport *request, assert((reply != NULL) == (bufp != NULL)); + error = dpif_linux_init(); + if (error) { + if (reply) { + *bufp = NULL; + dpif_linux_vport_init(reply); + } + return error; + } + request_buf = ofpbuf_new(1024); dpif_linux_vport_to_ofpbuf(request, request_buf); error = nl_sock_transact(genl_sock, request_buf, bufp); @@ -1349,7 +1483,7 @@ dpif_linux_dp_to_ofpbuf(const struct dpif_linux_dp *dp, struct ofpbuf *buf) } /* Clears 'dp' to "empty" values. */ -void +static void dpif_linux_dp_init(struct dpif_linux_dp *dp) { memset(dp, 0, sizeof *dp); @@ -1376,7 +1510,7 @@ dpif_linux_dp_dump_start(struct nl_dump *dump) * result of the command is expected to be of the same form, which is decoded * and stored in '*reply' and '*bufp'. The caller must free '*bufp' when the * reply is no longer needed ('reply' will contain pointers into '*bufp'). */ -int +static int dpif_linux_dp_transact(const struct dpif_linux_dp *request, struct dpif_linux_dp *reply, struct ofpbuf **bufp) { @@ -1406,7 +1540,7 @@ dpif_linux_dp_transact(const struct dpif_linux_dp *request, /* Obtains information about 'dpif_' and stores it into '*reply' and '*bufp'. * The caller must free '*bufp' when the reply is no longer needed ('reply' * will contain pointers into '*bufp'). */ -int +static int dpif_linux_dp_get(const struct dpif *dpif_, struct dpif_linux_dp *reply, struct ofpbuf **bufp) { @@ -1516,7 +1650,7 @@ dpif_linux_flow_to_ofpbuf(const struct dpif_linux_flow *flow, } /* Clears 'flow' to "empty" values. */ -void +static void dpif_linux_flow_init(struct dpif_linux_flow *flow) { memset(flow, 0, sizeof *flow); @@ -1528,7 +1662,7 @@ dpif_linux_flow_init(struct dpif_linux_flow *flow) * result of the command is expected to be a flow also, which is decoded and * stored in '*reply' and '*bufp'. The caller must free '*bufp' when the reply * is no longer needed ('reply' will contain pointers into '*bufp'). */ -int +static int dpif_linux_flow_transact(const struct dpif_linux_flow *request, struct dpif_linux_flow *reply, struct ofpbuf **bufp) {