X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=switch%2Fdatapath.c;h=c25bc3e1cafb12a04bfac70b1b8b2360b1064fd8;hb=7d7d973d8b37a791d0c153dbcf55a559418bab6b;hp=4e7b6256bfb775ec4c5c85fd06e885f69212282f;hpb=b41a6a0a6d23322da667c39d1c5e65496ae8b02f;p=sliver-openvswitch.git diff --git a/switch/datapath.c b/switch/datapath.c index 4e7b6256b..c25bc3e1c 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -54,7 +54,19 @@ #define THIS_MODULE VLM_datapath #include "vlog.h" -#define BRIDGE_PORT_NO_FLOOD 0x00000001 +enum br_port_flags { + BRPF_NO_FLOOD = 1 << 0, +}; + +enum br_port_status { + BRPS_PORT_DOWN = 1 << 0, + BRPS_LINK_DOWN = 1 << 1, +}; + +extern char mfr_desc; +extern char hw_desc; +extern char sw_desc; +extern char serial_num; /* Capabilities supported by this implementation. */ #define OFP_SUPPORTED_CAPABILITIES ( OFPC_FLOW_STATS \ @@ -73,7 +85,8 @@ | (1 << OFPAT_SET_TP_DST) ) struct sw_port { - uint32_t flags; + uint32_t flags; /* BRPF_* flags */ + uint32_t status; /* BRPS_* flags */ struct datapath *dp; struct netdev *netdev; struct list node; /* Element in datapath.ports. */ @@ -127,6 +140,8 @@ struct datapath { struct list port_list; /* List of ports, for flooding. */ }; +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60); + static struct remote *remote_create(struct datapath *, struct rconn *); static void remote_run(struct datapath *, struct remote *); static void remote_wait(struct remote *); @@ -134,11 +149,12 @@ static void remote_destroy(struct remote *); void dp_output_port(struct datapath *, struct buffer *, int in_port, int out_port); -void dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp); +void dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm); void dp_output_control(struct datapath *, struct buffer *, int in_port, size_t max_len, int reason); static void send_flow_expired(struct datapath *, struct sw_flow *, enum ofp_flow_expired_reason); +static int update_port_status(struct sw_port *p); static void send_port_status(struct sw_port *p, uint8_t status); static void del_switch_port(struct sw_port *p); static void execute_actions(struct datapath *, struct buffer *, @@ -230,7 +246,7 @@ dp_add_port(struct datapath *dp, const char *name) } error = netdev_set_flags(netdev, NETDEV_UP | NETDEV_PROMISC, false); if (error) { - VLOG_ERR("Couldn't set promiscuous mode on %s device", name); + VLOG_ERR("couldn't set promiscuous mode on %s device", name); netdev_close(netdev); return error; } @@ -282,6 +298,12 @@ dp_run(struct datapath *dp) struct list deleted = LIST_INITIALIZER(&deleted); struct sw_flow *f, *n; + LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) { + if (update_port_status(p)) { + send_port_status(p, OFPPR_MOD); + } + } + chain_timeout(dp->chain, &deleted); LIST_FOR_EACH_SAFE (f, n, struct sw_flow, node, &deleted) { send_flow_expired(dp, f, f->reason); @@ -312,9 +334,8 @@ dp_run(struct datapath *dp) fwd_port_input(dp, buffer, port_no(dp, p)); buffer = NULL; } else if (error != EAGAIN) { - VLOG_ERR("Error receiving data from %s: %s", - netdev_get_name(p->netdev), strerror(error)); - del_switch_port(p); + VLOG_ERR_RL(&rl, "error receiving data from %s: %s", + netdev_get_name(p->netdev), strerror(error)); } } buffer_delete(buffer); @@ -331,7 +352,7 @@ dp_run(struct datapath *dp) retval = vconn_accept(dp->listen_vconn, &new_vconn); if (retval) { if (retval != EAGAIN) { - VLOG_WARN("accept failed (%s)", strerror(retval)); + VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval)); } break; } @@ -367,7 +388,7 @@ remote_run(struct datapath *dp, struct remote *r) sender.xid = oh->xid; fwd_control_input(dp, &sender, buffer->data, buffer->size); } else { - VLOG_WARN("received too-short OpenFlow message"); + VLOG_WARN_RL(&rl, "received too-short OpenFlow message"); } buffer_delete(buffer); } else { @@ -375,7 +396,8 @@ remote_run(struct datapath *dp, struct remote *r) int error = r->cb_dump(dp, r->cb_aux); if (error <= 0) { if (error) { - VLOG_WARN("dump callback error: %s", strerror(-error)); + VLOG_WARN_RL(&rl, "dump callback error: %s", + strerror(-error)); } r->cb_done(r->cb_aux); r->cb_dump = NULL; @@ -504,7 +526,7 @@ output_all(struct datapath *dp, struct buffer *buffer, int in_port, int flood) if (port_no(dp, p) == in_port) { continue; } - if (flood && p->flags & BRIDGE_PORT_NO_FLOOD) { + if (flood && p->flags & BRPF_NO_FLOOD) { continue; } if (prev_port != -1) { @@ -525,7 +547,7 @@ output_packet(struct datapath *dp, struct buffer *buffer, int out_port) { if (out_port >= 0 && out_port < OFPP_MAX) { struct sw_port *p = &dp->ports[out_port]; - if (p->netdev != NULL) { + if (p->netdev != NULL && !(p->status & BRPS_PORT_DOWN)) { if (!netdev_send(p->netdev, buffer)) { p->tx_packets++; p->tx_bytes += buffer->size; @@ -537,8 +559,7 @@ output_packet(struct datapath *dp, struct buffer *buffer, int out_port) } buffer_delete(buffer); - /* FIXME: ratelimit */ - VLOG_DBG("can't forward to bad port %d\n", out_port); + VLOG_DBG_RL(&rl, "can't forward to bad port %d\n", out_port); } /* Takes ownership of 'buffer' and transmits it to 'out_port' on 'dp'. @@ -563,8 +584,7 @@ dp_output_port(struct datapath *dp, struct buffer *buffer, } } else { if (in_port == out_port) { - /* FIXME: ratelimit */ - VLOG_DBG("can't directly forward to input port"); + VLOG_DBG_RL(&rl, "can't directly forward to input port"); return; } output_packet(dp, buffer, out_port); @@ -592,8 +612,8 @@ send_openflow_buffer(struct datapath *dp, struct buffer *buffer, ? rconn_send(rconn, buffer, &remote->n_txq) : EAGAIN); if (retval) { - VLOG_WARN("send to %s failed: %s", - rconn_get_name(rconn), strerror(retval)); + VLOG_WARN_RL(&rl, "send to %s failed: %s", + rconn_get_name(rconn), strerror(retval)); buffer_delete(buffer); } return retval; @@ -640,9 +660,17 @@ static void fill_port_desc(struct datapath *dp, struct sw_port *p, sizeof desc->name); desc->name[sizeof desc->name - 1] = '\0'; memcpy(desc->hw_addr, netdev_get_etheraddr(p->netdev), ETH_ADDR_LEN); - desc->flags = htonl(p->flags); + desc->flags = 0; desc->features = htonl(netdev_get_features(p->netdev)); desc->speed = htonl(netdev_get_speed(p->netdev)); + + if (p->flags & BRPF_NO_FLOOD) { + desc->flags |= htonl(OFPPFL_NO_FLOOD); + } else if (p->status & BRPS_PORT_DOWN) { + desc->flags |= htonl(OFPPFL_PORT_DOWN); + } else if (p->status & BRPS_LINK_DOWN) { + desc->flags |= htonl(OFPPFL_LINK_DOWN); + } } static void @@ -671,8 +699,9 @@ dp_send_features_reply(struct datapath *dp, const struct sender *sender) } void -dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp) +dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm) { + const struct ofp_phy_port *opp = &opm->desc; int port_no = ntohs(opp->port_no); if (port_no < OFPP_MAX) { struct sw_port *p = &dp->ports[port_no]; @@ -682,10 +711,66 @@ dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp) ETH_ADDR_LEN) != 0) { return; } - p->flags = htonl(opp->flags); + + + if (opm->mask & htonl(OFPPFL_NO_FLOOD)) { + if (opp->flags & htonl(OFPPFL_NO_FLOOD)) + p->flags |= BRPF_NO_FLOOD; + else + p->flags &= ~BRPF_NO_FLOOD; + } + + if (opm->mask & htonl(OFPPFL_PORT_DOWN)) { + if ((opp->flags & htonl(OFPPFL_PORT_DOWN)) + && (p->status & BRPS_PORT_DOWN) == 0) { + p->status |= BRPS_PORT_DOWN; + netdev_turn_flags_off(p->netdev, NETDEV_UP, true); + } else if ((opp->flags & htonl(OFPPFL_PORT_DOWN)) == 0 + && (p->status & BRPS_PORT_DOWN)) { + p->status &= ~BRPS_PORT_DOWN; + netdev_turn_flags_on(p->netdev, NETDEV_UP, true); + } + } } } +/* Update the port status field of the bridge port. A non-zero return + * value indicates some field has changed. + * + * NB: Callers of this function may hold the RCU read lock, so any + * additional checks must not sleep. + */ +static int +update_port_status(struct sw_port *p) +{ + int retval; + enum netdev_flags flags; + uint32_t orig_status = p->status; + + if (netdev_get_flags(p->netdev, &flags) < 0) { + VLOG_WARN_RL(&rl, "could not get netdev flags for %s", + netdev_get_name(p->netdev)); + return 0; + } else { + if (flags & NETDEV_UP) { + p->status &= ~BRPS_PORT_DOWN; + } else { + p->status |= BRPS_PORT_DOWN; + } + } + + /* Not all cards support this getting link status, so don't warn on + * error. */ + retval = netdev_get_link_status(p->netdev); + if (retval == 1) { + p->status &= ~BRPS_LINK_DOWN; + } else if (retval == 0) { + p->status |= BRPS_LINK_DOWN; + } + + return (orig_status != p->status); +} + static void send_port_status(struct sw_port *p, uint8_t status) { @@ -743,7 +828,7 @@ fill_flow_stats(struct buffer *buffer, struct sw_flow *flow, ofs->length = htons(length); ofs->table_id = table_idx; ofs->pad = 0; - ofs->match.wildcards = htons(flow->key.wildcards); + ofs->match.wildcards = htonl(flow->key.wildcards); ofs->match.in_port = flow->key.flow.in_port; memcpy(ofs->match.dl_src, flow->key.flow.dl_src, ETH_ADDR_LEN); memcpy(ofs->match.dl_dst, flow->key.flow.dl_dst, ETH_ADDR_LEN); @@ -752,7 +837,7 @@ fill_flow_stats(struct buffer *buffer, struct sw_flow *flow, ofs->match.nw_src = flow->key.flow.nw_src; ofs->match.nw_dst = flow->key.flow.nw_dst; ofs->match.nw_proto = flow->key.flow.nw_proto; - memset(ofs->match.pad, 0, sizeof ofs->match.pad); + ofs->match.pad = 0; ofs->match.tp_src = flow->key.flow.tp_src; ofs->match.tp_dst = flow->key.flow.tp_dst; ofs->duration = htonl(now - flow->created); @@ -1027,7 +1112,7 @@ recv_packet_out(struct datapath *dp, const struct sender *sender UNUSED, int act_len = n_actions * sizeof opo->actions[0]; if (act_len > (ntohs(opo->header.length) - sizeof *opo)) { - VLOG_DBG("message too short for number of actions"); + VLOG_DBG_RL(&rl, "message too short for number of actions"); return -EINVAL; } @@ -1056,7 +1141,7 @@ recv_port_mod(struct datapath *dp, const struct sender *sender UNUSED, { const struct ofp_port_mod *opm = msg; - dp_update_port_flags(dp, &opm->desc); + dp_update_port_flags(dp, opm); return 0; } @@ -1155,6 +1240,19 @@ recv_flow(struct datapath *dp, const struct sender *sender UNUSED, } } +static int desc_stats_dump(struct datapath *dp, void *state, + struct buffer *buffer) +{ + struct ofp_desc_stats *ods = buffer_put_uninit(buffer, sizeof *ods); + + strncpy(ods->mfr_desc, &mfr_desc, sizeof ods->mfr_desc); + strncpy(ods->hw_desc, &hw_desc, sizeof ods->hw_desc); + strncpy(ods->sw_desc, &sw_desc, sizeof ods->sw_desc); + strncpy(ods->serial_num, &serial_num, sizeof ods->serial_num); + + return 0; +} + struct flow_stats_state { int table_idx; struct sw_table_position position; @@ -1374,6 +1472,13 @@ struct stats_type { }; static const struct stats_type stats[] = { + [OFPST_DESC] = { + 0, + 0, + NULL, + desc_stats_dump, + NULL + }, [OFPST_FLOW] = { sizeof(struct ofp_flow_stats_request), sizeof(struct ofp_flow_stats_request), @@ -1472,7 +1577,7 @@ recv_stats_request(struct datapath *dp, const struct sender *sender, type = ntohs(rq->type); if (type >= ARRAY_SIZE(stats) || !stats[type].dump) { - VLOG_WARN("received stats request of unknown type %d", type); + VLOG_WARN_RL(&rl, "received stats request of unknown type %d", type); return -EINVAL; } @@ -1485,8 +1590,8 @@ recv_stats_request(struct datapath *dp, const struct sender *sender, body_len = rq_len - offsetof(struct ofp_stats_request, body); if (body_len < cb->s->min_body || body_len > cb->s->max_body) { - VLOG_WARN("stats request type %d with bad body length %d", - type, body_len); + VLOG_WARN_RL(&rl, "stats request type %d with bad body length %d", + type, body_len); err = -EINVAL; goto error; } @@ -1494,8 +1599,9 @@ recv_stats_request(struct datapath *dp, const struct sender *sender, if (cb->s->init) { err = cb->s->init(dp, rq->body, body_len, &cb->state); if (err) { - VLOG_WARN("failed initialization of stats request type %d: %s", - type, strerror(-err)); + VLOG_WARN_RL(&rl, + "failed initialization of stats request type %d: %s", + type, strerror(-err)); goto error; } }