-static void
-handle_odp_miss_msg(struct ofproto *p, struct ofpbuf *packet)
-{
- struct odp_msg *msg = packet->data;
- struct ofpbuf payload;
- struct facet *facet;
- struct flow flow;
-
- ofpbuf_use_const(&payload, msg + 1, msg->length - sizeof *msg);
- flow_extract(&payload, msg->arg, msg->port, &flow);
-
- packet->l2 = payload.l2;
- packet->l3 = payload.l3;
- packet->l4 = payload.l4;
- packet->l7 = payload.l7;
-
- /* Check with in-band control to see if this packet should be sent
- * to the local port regardless of the flow table. */
- if (in_band_msg_in_hook(p->in_band, &flow, &payload)) {
- struct ofpbuf odp_actions;
-
- ofpbuf_init(&odp_actions, 32);
- nl_msg_put_u32(&odp_actions, ODPAT_OUTPUT, ODPP_LOCAL);
- dpif_execute(p->dpif, odp_actions.data, odp_actions.size, &payload);
- ofpbuf_uninit(&odp_actions);
- }
-
- facet = facet_lookup_valid(p, &flow);
- if (!facet) {
- struct rule *rule = rule_lookup(p, &flow);
- if (!rule) {
- /* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */
- struct ofport *port = get_port(p, msg->port);
- if (port) {
- if (port->opp.config & OFPPC_NO_PACKET_IN) {
- COVERAGE_INC(ofproto_no_packet_in);
- /* XXX install 'drop' flow entry */
- ofpbuf_delete(packet);
- return;
- }
- } else {
- VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16,
- msg->port);
- }
-
- COVERAGE_INC(ofproto_packet_in);
- send_packet_in(p, packet);
- return;
- }
-
- facet = facet_create(p, rule, &flow, packet);
- } else if (!facet->may_install) {
- /* The facet is not installable, that is, we need to process every
- * packet, so process the current packet's actions into 'facet'. */
- facet_make_actions(p, facet, packet);
- }
-
- if (facet->rule->cr.priority == FAIL_OPEN_PRIORITY) {
- /*
- * Extra-special case for fail-open mode.
- *
- * We are in fail-open mode and the packet matched the fail-open rule,
- * but we are connected to a controller too. We should send the packet
- * up to the controller in the hope that it will try to set up a flow
- * and thereby allow us to exit fail-open.
- *
- * See the top-level comment in fail-open.c for more information.
- */
- send_packet_in(p, ofpbuf_clone_with_headroom(packet,
- DPIF_RECV_MSG_PADDING));
- }
-
- ofpbuf_pull(packet, sizeof *msg);
- facet_execute(p, facet, packet);
- facet_install(p, facet, false);
-}
-
-static void
-handle_odp_msg(struct ofproto *p, struct ofpbuf *packet)
-{
- struct odp_msg *msg = packet->data;
-
- switch (msg->type) {
- case _ODPL_ACTION_NR:
- COVERAGE_INC(ofproto_ctlr_action);
- send_packet_in(p, packet);
- break;
-
- case _ODPL_SFLOW_NR:
- if (p->sflow) {
- ofproto_sflow_received(p->sflow, msg);
- }
- ofpbuf_delete(packet);
- break;
-
- case _ODPL_MISS_NR:
- handle_odp_miss_msg(p, packet);
- break;
-
- default:
- VLOG_WARN_RL(&rl, "received ODP message of unexpected type %"PRIu32,
- msg->type);
- break;
- }
-}
-\f
-/* Flow expiration. */
-
-static int ofproto_dp_max_idle(const struct ofproto *);
-static void ofproto_update_used(struct ofproto *);
-static void rule_expire(struct ofproto *, struct rule *);
-static void ofproto_expire_facets(struct ofproto *, int dp_max_idle);
-
-/* This function is called periodically by ofproto_run(). Its job is to
- * collect updates for the flows that have been installed into the datapath,
- * most importantly when they last were used, and then use that information to
- * expire flows that have not been used recently.
- *
- * Returns the number of milliseconds after which it should be called again. */
-static int
-ofproto_expire(struct ofproto *ofproto)
-{
- struct rule *rule, *next_rule;
- struct cls_cursor cursor;
- int dp_max_idle;
-
- /* Update 'used' for each flow in the datapath. */
- ofproto_update_used(ofproto);
-
- /* Expire facets that have been idle too long. */
- dp_max_idle = ofproto_dp_max_idle(ofproto);
- ofproto_expire_facets(ofproto, dp_max_idle);
-
- /* Expire OpenFlow flows whose idle_timeout or hard_timeout has passed. */
- cls_cursor_init(&cursor, &ofproto->cls, NULL);
- CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
- rule_expire(ofproto, rule);
- }
-
- /* Let the hook know that we're at a stable point: all outstanding data
- * in existing flows has been accounted to the account_cb. Thus, the
- * hook can now reasonably do operations that depend on having accurate
- * flow volume accounting (currently, that's just bond rebalancing). */
- if (ofproto->ofhooks->account_checkpoint_cb) {
- ofproto->ofhooks->account_checkpoint_cb(ofproto->aux);
- }
-
- return MIN(dp_max_idle, 1000);
-}
-
-/* Update 'used' member of installed facets. */
-static void
-ofproto_update_used(struct ofproto *p)
-{
- struct odp_flow *flows;
- size_t n_flows;
- size_t i;
- int error;
-
- error = dpif_flow_list_all(p->dpif, &flows, &n_flows);
- if (error) {
- return;
- }
-
- for (i = 0; i < n_flows; i++) {
- struct odp_flow *f = &flows[i];
- struct facet *facet;
- struct flow flow;
-
- odp_flow_key_to_flow(&f->key, &flow);
- facet = facet_find(p, &flow);
-
- if (facet && facet->installed) {
- facet_update_time(p, facet, &f->stats);
- facet_account(p, facet, f->stats.n_bytes);
- } else {
- /* There's a flow in the datapath that we know nothing about.
- * Delete it. */
- COVERAGE_INC(ofproto_unexpected_rule);
- dpif_flow_del(p->dpif, f);
- }
-
- }
- free(flows);
-}
-
-/* Calculates and returns the number of milliseconds of idle time after which
- * facets should expire from the datapath and we should fold their statistics
- * into their parent rules in userspace. */
-static int
-ofproto_dp_max_idle(const struct ofproto *ofproto)
-{
- /*
- * Idle time histogram.
- *
- * Most of the time a switch has a relatively small number of facets. When
- * this is the case we might as well keep statistics for all of them in
- * userspace and to cache them in the kernel datapath for performance as
- * well.
- *
- * As the number of facets increases, the memory required to maintain
- * statistics about them in userspace and in the kernel becomes
- * significant. However, with a large number of facets it is likely that
- * only a few of them are "heavy hitters" that consume a large amount of
- * bandwidth. At this point, only heavy hitters are worth caching in the
- * kernel and maintaining in userspaces; other facets we can discard.
- *
- * The technique used to compute the idle time is to build a histogram with
- * N_BUCKETS buckets whose width is BUCKET_WIDTH msecs each. Each facet
- * that is installed in the kernel gets dropped in the appropriate bucket.
- * After the histogram has been built, we compute the cutoff so that only
- * the most-recently-used 1% of facets (but at least 1000 flows) are kept
- * cached. At least the most-recently-used bucket of facets is kept, so
- * actually an arbitrary number of facets can be kept in any given
- * expiration run (though the next run will delete most of those unless
- * they receive additional data).
- *
- * This requires a second pass through the facets, in addition to the pass
- * made by ofproto_update_used(), because the former function never looks
- * at uninstallable facets.
- */
- enum { BUCKET_WIDTH = ROUND_UP(100, TIME_UPDATE_INTERVAL) };
- enum { N_BUCKETS = 5000 / BUCKET_WIDTH };
- int buckets[N_BUCKETS] = { 0 };
- struct facet *facet;
- int total, bucket;
- long long int now;
- int i;
-
- total = hmap_count(&ofproto->facets);
- if (total <= 1000) {
- return N_BUCKETS * BUCKET_WIDTH;
- }
-
- /* Build histogram. */
- now = time_msec();
- HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
- long long int idle = now - facet->used;
- int bucket = (idle <= 0 ? 0
- : idle >= BUCKET_WIDTH * N_BUCKETS ? N_BUCKETS - 1
- : (unsigned int) idle / BUCKET_WIDTH);
- buckets[bucket]++;
- }
-
- /* Find the first bucket whose flows should be expired. */
- for (bucket = 0; bucket < N_BUCKETS; bucket++) {
- if (buckets[bucket]) {
- int subtotal = 0;
- do {
- subtotal += buckets[bucket++];
- } while (bucket < N_BUCKETS && subtotal < MAX(1000, total / 100));
- break;
- }
- }
-
- if (VLOG_IS_DBG_ENABLED()) {
- struct ds s;
-
- ds_init(&s);
- ds_put_cstr(&s, "keep");
- for (i = 0; i < N_BUCKETS; i++) {
- if (i == bucket) {
- ds_put_cstr(&s, ", drop");
- }
- if (buckets[i]) {
- ds_put_format(&s, " %d:%d", i * BUCKET_WIDTH, buckets[i]);
- }
- }
- VLOG_INFO("%s: %s (msec:count)",
- dpif_name(ofproto->dpif), ds_cstr(&s));
- ds_destroy(&s);
- }
-
- return bucket * BUCKET_WIDTH;
-}
-
-static void
-facet_active_timeout(struct ofproto *ofproto, struct facet *facet)
-{
- if (ofproto->netflow && !facet_is_controller_flow(facet) &&
- netflow_active_timeout_expired(ofproto->netflow, &facet->nf_flow)) {
- struct ofexpired expired;
- struct odp_flow odp_flow;
-
- /* Get updated flow stats.
- *
- * XXX We could avoid this call entirely if (1) ofproto_update_used()
- * updated TCP flags and (2) the dpif_flow_list_all() in
- * ofproto_update_used() zeroed TCP flags. */
- memset(&odp_flow, 0, sizeof odp_flow);
- if (facet->installed) {
- odp_flow_key_from_flow(&odp_flow.key, &facet->flow);
- odp_flow.flags = ODPFF_ZERO_TCP_FLAGS;
- dpif_flow_get(ofproto->dpif, &odp_flow);
-
- if (odp_flow.stats.n_packets) {
- facet_update_time(ofproto, facet, &odp_flow.stats);
- netflow_flow_update_flags(&facet->nf_flow,
- odp_flow.stats.tcp_flags);
- }
- }
-
- expired.flow = facet->flow;
- expired.packet_count = facet->packet_count +
- odp_flow.stats.n_packets;
- expired.byte_count = facet->byte_count + odp_flow.stats.n_bytes;
- expired.used = facet->used;
-
- netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
- }
-}
-
-static void
-ofproto_expire_facets(struct ofproto *ofproto, int dp_max_idle)
-{
- long long int cutoff = time_msec() - dp_max_idle;
- struct facet *facet, *next_facet;
-
- HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) {
- facet_active_timeout(ofproto, facet);
- if (facet->used < cutoff) {
- facet_remove(ofproto, facet);
- }
- }
-}
-
-/* If 'rule' is an OpenFlow rule, that has expired according to OpenFlow rules,
- * then delete it entirely. */
-static void
-rule_expire(struct ofproto *ofproto, struct rule *rule)
-{
- struct facet *facet, *next_facet;
- long long int now;
- uint8_t reason;
-
- /* Has 'rule' expired? */
- now = time_msec();
- if (rule->hard_timeout
- && now > rule->created + rule->hard_timeout * 1000) {
- reason = OFPRR_HARD_TIMEOUT;
- } else if (rule->idle_timeout && list_is_empty(&rule->facets)
- && now >rule->used + rule->idle_timeout * 1000) {
- reason = OFPRR_IDLE_TIMEOUT;
- } else {
- return;
- }
-
- COVERAGE_INC(ofproto_expired);
-
- /* Update stats. (This is a no-op if the rule expired due to an idle
- * timeout, because that only happens when the rule has no facets left.) */
- LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
- facet_remove(ofproto, facet);
- }
-
- /* Get rid of the rule. */
- if (!rule_is_hidden(rule)) {
- rule_send_removed(ofproto, rule, reason);
- }
- rule_remove(ofproto, rule);
-}
-\f
-static struct ofpbuf *
-compose_ofp_flow_removed(struct ofconn *ofconn, const struct rule *rule,
- uint8_t reason)
-{
- struct ofp_flow_removed *ofr;
- struct ofpbuf *buf;
-
- ofr = make_openflow(sizeof *ofr, OFPT_FLOW_REMOVED, &buf);
- ofputil_cls_rule_to_match(&rule->cr, ofconn->flow_format, &ofr->match,
- rule->flow_cookie, &ofr->cookie);
- ofr->priority = htons(rule->cr.priority);
- ofr->reason = reason;
- calc_flow_duration(rule->created, &ofr->duration_sec, &ofr->duration_nsec);
- ofr->idle_timeout = htons(rule->idle_timeout);
- ofr->packet_count = htonll(rule->packet_count);
- ofr->byte_count = htonll(rule->byte_count);
-
- return buf;
-}
-
-static struct ofpbuf *
-compose_nx_flow_removed(const struct rule *rule, uint8_t reason)
-{
- struct nx_flow_removed *nfr;
- struct ofpbuf *buf;
- int match_len;
-
- nfr = make_nxmsg(sizeof *nfr, NXT_FLOW_REMOVED, &buf);
-
- match_len = nx_put_match(buf, &rule->cr);
-
- nfr->cookie = rule->flow_cookie;
- nfr->priority = htons(rule->cr.priority);
- nfr->reason = reason;
- calc_flow_duration(rule->created, &nfr->duration_sec, &nfr->duration_nsec);
- nfr->idle_timeout = htons(rule->idle_timeout);
- nfr->match_len = htons(match_len);
- nfr->packet_count = htonll(rule->packet_count);
- nfr->byte_count = htonll(rule->byte_count);
-
- return buf;
-}
-
-static void
-rule_send_removed(struct ofproto *p, struct rule *rule, uint8_t reason)
-{
- struct ofconn *ofconn;
-
- if (!rule->send_flow_removed) {
- return;
- }
-
- LIST_FOR_EACH (ofconn, node, &p->all_conns) {
- struct ofpbuf *msg;
-
- if (!rconn_is_connected(ofconn->rconn)
- || !ofconn_receives_async_msgs(ofconn)) {
- continue;
- }
-
- msg = (ofconn->flow_format == NXFF_NXM
- ? compose_nx_flow_removed(rule, reason)
- : compose_ofp_flow_removed(ofconn, rule, reason));
-
- /* Account flow expirations under ofconn->reply_counter, the counter
- * for replies to OpenFlow requests. That works because preventing
- * OpenFlow requests from being processed also prevents new flows from
- * being added (and expiring). (It also prevents processing OpenFlow
- * requests that would not add new flows, so it is imperfect.) */
- queue_tx(msg, ofconn, ofconn->reply_counter);
- }
-}
-
-/* pinsched callback for sending 'packet' on 'ofconn'. */
-static void
-do_send_packet_in(struct ofpbuf *packet, void *ofconn_)
-{
- struct ofconn *ofconn = ofconn_;
-
- rconn_send_with_limit(ofconn->rconn, packet,
- ofconn->packet_in_counter, 100);
-}
-
-/* Takes 'packet', which has been converted with do_convert_to_packet_in(), and
- * finalizes its content for sending on 'ofconn', and passes it to 'ofconn''s
- * packet scheduler for sending.
- *
- * 'max_len' specifies the maximum number of bytes of the packet to send on
- * 'ofconn' (INT_MAX specifies no limit).
- *
- * If 'clone' is true, the caller retains ownership of 'packet'. Otherwise,
- * ownership is transferred to this function. */
-static void
-schedule_packet_in(struct ofconn *ofconn, struct ofpbuf *packet, int max_len,
- bool clone)
-{
- struct ofproto *ofproto = ofconn->ofproto;
- struct ofp_packet_in *opi = packet->data;
- uint16_t in_port = ofp_port_to_odp_port(ntohs(opi->in_port));
- int send_len, trim_size;
- uint32_t buffer_id;
-
- /* Get buffer. */
- if (opi->reason == OFPR_ACTION) {
- buffer_id = UINT32_MAX;
- } else if (ofproto->fail_open && fail_open_is_active(ofproto->fail_open)) {
- buffer_id = pktbuf_get_null();
- } else if (!ofconn->pktbuf) {
- buffer_id = UINT32_MAX;
- } else {
- struct ofpbuf payload;
-
- ofpbuf_use_const(&payload, opi->data,
- packet->size - offsetof(struct ofp_packet_in, data));
- buffer_id = pktbuf_save(ofconn->pktbuf, &payload, in_port);
- }
-
- /* Figure out how much of the packet to send. */
- send_len = ntohs(opi->total_len);
- if (buffer_id != UINT32_MAX) {
- send_len = MIN(send_len, ofconn->miss_send_len);
- }
- send_len = MIN(send_len, max_len);
-
- /* Adjust packet length and clone if necessary. */
- trim_size = offsetof(struct ofp_packet_in, data) + send_len;
- if (clone) {
- packet = ofpbuf_clone_data(packet->data, trim_size);
- opi = packet->data;
- } else {
- packet->size = trim_size;
- }
-
- /* Update packet headers. */
- opi->buffer_id = htonl(buffer_id);
- update_openflow_length(packet);
-
- /* Hand over to packet scheduler. It might immediately call into
- * do_send_packet_in() or it might buffer it for a while (until a later
- * call to pinsched_run()). */
- pinsched_send(ofconn->schedulers[opi->reason], in_port,
- packet, do_send_packet_in, ofconn);
-}
-
-/* Replace struct odp_msg header in 'packet' by equivalent struct
- * ofp_packet_in. The odp_msg must have sufficient headroom to do so (e.g. as
- * returned by dpif_recv()).
- *
- * The conversion is not complete: the caller still needs to trim any unneeded
- * payload off the end of the buffer, set the length in the OpenFlow header,
- * and set buffer_id. Those require us to know the controller settings and so
- * must be done on a per-controller basis.
- *
- * Returns the maximum number of bytes of the packet that should be sent to
- * the controller (INT_MAX if no limit). */
-static int
-do_convert_to_packet_in(struct ofpbuf *packet)
-{
- struct odp_msg *msg = packet->data;
- struct ofp_packet_in *opi;
- uint8_t reason;
- uint16_t total_len;
- uint16_t in_port;
- int max_len;
-
- /* Extract relevant header fields */
- if (msg->type == _ODPL_ACTION_NR) {
- reason = OFPR_ACTION;
- max_len = msg->arg;
- } else {
- reason = OFPR_NO_MATCH;
- max_len = INT_MAX;
- }
- total_len = msg->length - sizeof *msg;
- in_port = odp_port_to_ofp_port(msg->port);
-
- /* Repurpose packet buffer by overwriting header. */
- ofpbuf_pull(packet, sizeof(struct odp_msg));
- opi = ofpbuf_push_zeros(packet, offsetof(struct ofp_packet_in, data));
- opi->header.version = OFP_VERSION;
- opi->header.type = OFPT_PACKET_IN;
- opi->total_len = htons(total_len);
- opi->in_port = htons(in_port);
- opi->reason = reason;
-
- return max_len;
-}
-
-/* Given 'packet' containing an odp_msg of type _ODPL_ACTION_NR or
- * _ODPL_MISS_NR, sends an OFPT_PACKET_IN message to each OpenFlow controller
- * as necessary according to their individual configurations.
- *
- * 'packet' must have sufficient headroom to convert it into a struct
- * ofp_packet_in (e.g. as returned by dpif_recv()).
- *
- * Takes ownership of 'packet'. */
-static void
-send_packet_in(struct ofproto *ofproto, struct ofpbuf *packet)
-{
- struct ofconn *ofconn, *prev;
- int max_len;
-
- max_len = do_convert_to_packet_in(packet);
-
- prev = NULL;
- LIST_FOR_EACH (ofconn, node, &ofproto->all_conns) {
- if (ofconn_receives_async_msgs(ofconn)) {
- if (prev) {
- schedule_packet_in(prev, packet, max_len, true);
- }
- prev = ofconn;
- }
- }
- if (prev) {
- schedule_packet_in(prev, packet, max_len, false);
- } else {
- ofpbuf_delete(packet);
- }
-}
-