X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fdpif.c;h=8286da402ae808c48019dcef9c019e7f7338ebef;hb=38a997566d7faa1e8c4b9c029faed6789e7528ff;hp=06f793ca636ec4c13163a947c1f3df508a1d91fc;hpb=b4a7a3f3dea8fdf2a9da184c1676eeccd76f4700;p=sliver-openvswitch.git diff --git a/lib/dpif.c b/lib/dpif.c index 06f793ca6..8286da402 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -33,6 +33,7 @@ #include "ofpbuf.h" #include "packets.h" #include "poll-loop.h" +#include "shash.h" #include "svec.h" #include "util.h" #include "valgrind.h" @@ -40,11 +41,18 @@ #include "vlog.h" #define THIS_MODULE VLM_dpif -static const struct dpif_class *dpif_classes[] = { +static const struct dpif_class *base_dpif_classes[] = { +#ifdef HAVE_NETLINK &dpif_linux_class, +#endif &dpif_netdev_class, }; -enum { N_DPIF_CLASSES = ARRAY_SIZE(dpif_classes) }; + +struct registered_dpif_class { + struct dpif_class dpif_class; + int refcount; +}; +static struct shash dpif_classes = SHASH_INITIALIZER(&dpif_classes); /* Rate limit for individual messages going to or from the datapath, output at * DBG level. This is very high because, if these are enabled, it is because @@ -52,7 +60,7 @@ enum { N_DPIF_CLASSES = ARRAY_SIZE(dpif_classes) }; static struct vlog_rate_limit dpmsg_rl = VLOG_RATE_LIMIT_INIT(600, 600); /* Not really much point in logging many dpif errors. */ -static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5); +static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5); static void log_operation(const struct dpif *, const char *operation, int error); @@ -63,6 +71,21 @@ static void log_flow_put(struct dpif *, int error, static bool should_log_flow_message(int error); static void check_rw_odp_flow(struct odp_flow *); +static void +dp_initialize(void) +{ + static int status = -1; + + if (status < 0) { + int i; + + status = 0; + for (i = 0; i < ARRAY_SIZE(base_dpif_classes); i++) { + dp_register_provider(base_dpif_classes[i]); + } + } +} + /* Performs periodic work needed by all the various kinds of dpifs. * * If your program opens any dpifs, it must call both this function and @@ -70,11 +93,11 @@ static void check_rw_odp_flow(struct odp_flow *); void dp_run(void) { - int i; - for (i = 0; i < N_DPIF_CLASSES; i++) { - const struct dpif_class *class = dpif_classes[i]; - if (class->run) { - class->run(); + struct shash_node *node; + SHASH_FOR_EACH(node, &dpif_classes) { + const struct registered_dpif_class *registered_class = node->data; + if (registered_class->dpif_class.run) { + registered_class->dpif_class.run(); } } } @@ -86,108 +109,200 @@ dp_run(void) void dp_wait(void) { - int i; - for (i = 0; i < N_DPIF_CLASSES; i++) { - const struct dpif_class *class = dpif_classes[i]; - if (class->wait) { - class->wait(); + struct shash_node *node; + SHASH_FOR_EACH(node, &dpif_classes) { + const struct registered_dpif_class *registered_class = node->data; + if (registered_class->dpif_class.wait) { + registered_class->dpif_class.wait(); } } } -/* Clears 'all_dps' and enumerates the names of all known created datapaths, - * where possible, into it. The caller must first initialize 'all_dps'. - * Returns 0 if successful, otherwise a positive errno value. +/* Registers a new datapath provider. After successful registration, new + * datapaths of that type can be opened using dpif_open(). */ +int +dp_register_provider(const struct dpif_class *new_class) +{ + struct registered_dpif_class *registered_class; + + if (shash_find(&dpif_classes, new_class->type)) { + VLOG_WARN("attempted to register duplicate datapath provider: %s", + new_class->type); + return EEXIST; + } + + registered_class = xmalloc(sizeof *registered_class); + memcpy(®istered_class->dpif_class, new_class, + sizeof registered_class->dpif_class); + registered_class->refcount = 0; + + shash_add(&dpif_classes, new_class->type, registered_class); + + return 0; +} + +/* Unregisters a datapath provider. 'type' must have been previously + * registered and not currently be in use by any dpifs. After unregistration + * new datapaths of that type cannot be opened using dpif_open(). */ +int +dp_unregister_provider(const char *type) +{ + struct shash_node *node; + struct registered_dpif_class *registered_class; + + node = shash_find(&dpif_classes, type); + if (!node) { + VLOG_WARN("attempted to unregister a datapath provider that is not " + "registered: %s", type); + return EAFNOSUPPORT; + } + + registered_class = node->data; + if (registered_class->refcount) { + VLOG_WARN("attempted to unregister in use datapath provider: %s", type); + return EBUSY; + } + + shash_delete(&dpif_classes, node); + free(registered_class); + + return 0; +} + +/* Clears 'types' and enumerates the types of all currently registered datapath + * providers into it. The caller must first initialize the svec. */ +void +dp_enumerate_types(struct svec *types) +{ + struct shash_node *node; + + dp_initialize(); + svec_clear(types); + + SHASH_FOR_EACH(node, &dpif_classes) { + const struct registered_dpif_class *registered_class = node->data; + svec_add(types, registered_class->dpif_class.type); + } +} + +/* Clears 'names' and enumerates the names of all known created datapaths with + * the given 'type'. The caller must first initialize the svec. Returns 0 if + * successful, otherwise a positive errno value. * * Some kinds of datapaths might not be practically enumerable. This is not * considered an error. */ int -dp_enumerate(struct svec *all_dps) +dp_enumerate_names(const char *type, struct svec *names) { + const struct registered_dpif_class *registered_class; + const struct dpif_class *dpif_class; int error; - int i; - - svec_clear(all_dps); - error = 0; - for (i = 0; i < N_DPIF_CLASSES; i++) { - const struct dpif_class *class = dpif_classes[i]; - int retval = class->enumerate ? class->enumerate(all_dps) : 0; - if (retval) { - VLOG_WARN("failed to enumerate %s datapaths: %s", - class->name, strerror(retval)); - if (!error) { - error = retval; - } - } + + dp_initialize(); + svec_clear(names); + + registered_class = shash_find_data(&dpif_classes, type); + if (!registered_class) { + VLOG_WARN("could not enumerate unknown type: %s", type); + return EAFNOSUPPORT; + } + + dpif_class = ®istered_class->dpif_class; + error = dpif_class->enumerate ? dpif_class->enumerate(names) : 0; + + if (error) { + VLOG_WARN("failed to enumerate %s datapaths: %s", dpif_class->type, + strerror(error)); } + return error; } +/* Parses 'datapath name', which is of the form type@name into its + * component pieces. 'name' and 'type' must be freed by the caller. */ +void +dp_parse_name(const char *datapath_name_, char **name, char **type) +{ + char *datapath_name = xstrdup(datapath_name_); + char *separator; + + separator = strchr(datapath_name, '@'); + if (separator) { + *separator = '\0'; + *type = datapath_name; + *name = xstrdup(separator + 1); + } else { + *name = datapath_name; + *type = NULL; + } +} + static int -do_open(const char *name_, bool create, struct dpif **dpifp) +do_open(const char *name, const char *type, bool create, struct dpif **dpifp) { - char *name = xstrdup(name_); - char *prefix, *suffix, *colon; struct dpif *dpif = NULL; int error; - int i; + struct registered_dpif_class *registered_class; - colon = strchr(name, ':'); - if (colon) { - *colon = '\0'; - prefix = name; - suffix = colon + 1; - } else { - prefix = ""; - suffix = name; + dp_initialize(); + + if (!type || *type == '\0') { + type = "system"; } - for (i = 0; i < N_DPIF_CLASSES; i++) { - const struct dpif_class *class = dpif_classes[i]; - if (!strcmp(prefix, class->prefix)) { - error = class->open(name_, suffix, create, &dpif); - goto exit; - } + registered_class = shash_find_data(&dpif_classes, type); + if (!registered_class) { + VLOG_WARN("could not create datapath %s of unknown type %s", name, + type); + error = EAFNOSUPPORT; + goto exit; + } + + error = registered_class->dpif_class.open(name, type, create, &dpif); + if (!error) { + registered_class->refcount++; } - error = EAFNOSUPPORT; exit: *dpifp = error ? NULL : dpif; return error; } -/* Tries to open an existing datapath named 'name'. Will fail if no datapath - * named 'name' exists. Returns 0 if successful, otherwise a positive errno - * value. On success stores a pointer to the datapath in '*dpifp', otherwise a - * null pointer. */ +/* Tries to open an existing datapath named 'name' and type 'type'. Will fail + * if no datapath with 'name' and 'type' exists. 'type' may be either NULL or + * the empty string to specify the default system type. Returns 0 if + * successful, otherwise a positive errno value. On success stores a pointer + * to the datapath in '*dpifp', otherwise a null pointer. */ int -dpif_open(const char *name, struct dpif **dpifp) +dpif_open(const char *name, const char *type, struct dpif **dpifp) { - return do_open(name, false, dpifp); + return do_open(name, type, false, dpifp); } -/* Tries to create and open a new datapath with the given 'name'. Will fail if - * a datapath named 'name' already exists. Returns 0 if successful, otherwise - * a positive errno value. On success stores a pointer to the datapath in - * '*dpifp', otherwise a null pointer. */ +/* Tries to create and open a new datapath with the given 'name' and 'type'. + * 'type' may be either NULL or the empty string to specify the default system + * type. Will fail if a datapath with 'name' and 'type' already exists. + * Returns 0 if successful, otherwise a positive errno value. On success + * stores a pointer to the datapath in '*dpifp', otherwise a null pointer. */ int -dpif_create(const char *name, struct dpif **dpifp) +dpif_create(const char *name, const char *type, struct dpif **dpifp) { - return do_open(name, true, dpifp); + return do_open(name, type, true, dpifp); } -/* Tries to open a datapath with the given 'name', creating it if it does not - * exist. Returns 0 if successful, otherwise a positive errno value. On - * success stores a pointer to the datapath in '*dpifp', otherwise a null - * pointer. */ +/* Tries to open a datapath with the given 'name' and 'type', creating it if it + * does not exist. 'type' may be either NULL or the empty string to specify + * the default system type. Returns 0 if successful, otherwise a positive + * errno value. On success stores a pointer to the datapath in '*dpifp', + * otherwise a null pointer. */ int -dpif_create_and_open(const char *name, struct dpif **dpifp) +dpif_create_and_open(const char *name, const char *type, struct dpif **dpifp) { int error; - error = dpif_create(name, dpifp); + error = dpif_create(name, type, dpifp); if (error == EEXIST || error == EBUSY) { - error = dpif_open(name, dpifp); + error = dpif_open(name, type, dpifp); if (error) { VLOG_WARN("datapath %s already exists but cannot be opened: %s", name, strerror(error)); @@ -204,17 +319,32 @@ void dpif_close(struct dpif *dpif) { if (dpif) { - char *name = dpif->name; - dpif->class->close(dpif); - free(name); + struct registered_dpif_class *registered_class; + + registered_class = shash_find_data(&dpif_classes, + dpif->dpif_class->type); + assert(registered_class); + assert(registered_class->refcount); + + registered_class->refcount--; + dpif_uninit(dpif, true); } } -/* Returns the name of datapath 'dpif' (for use in log messages). */ +/* Returns the name of datapath 'dpif' prefixed with the type + * (for use in log messages). */ const char * dpif_name(const struct dpif *dpif) { - return dpif->name; + return dpif->full_name; +} + +/* Returns the name of datapath 'dpif' without the type + * (for use in device names). */ +const char * +dpif_base_name(const struct dpif *dpif) +{ + return dpif->base_name; } /* Enumerates all names that may be used to open 'dpif' into 'all_names'. The @@ -229,8 +359,8 @@ dpif_name(const struct dpif *dpif) int dpif_get_all_names(const struct dpif *dpif, struct svec *all_names) { - if (dpif->class->get_all_names) { - int error = dpif->class->get_all_names(dpif, all_names); + if (dpif->dpif_class->get_all_names) { + int error = dpif->dpif_class->get_all_names(dpif, all_names); if (error) { VLOG_WARN_RL(&error_rl, "failed to retrieve names for datpath %s: %s", @@ -238,7 +368,7 @@ dpif_get_all_names(const struct dpif *dpif, struct svec *all_names) } return error; } else { - svec_add(all_names, dpif_name(dpif)); + svec_add(all_names, dpif_base_name(dpif)); return 0; } } @@ -253,7 +383,7 @@ dpif_delete(struct dpif *dpif) COVERAGE_INC(dpif_destroy); - error = dpif->class->delete(dpif); + error = dpif->dpif_class->destroy(dpif); log_operation(dpif, "delete", error); return error; } @@ -263,7 +393,7 @@ dpif_delete(struct dpif *dpif) int dpif_get_dp_stats(const struct dpif *dpif, struct odp_stats *stats) { - int error = dpif->class->get_stats(dpif, stats); + int error = dpif->dpif_class->get_stats(dpif, stats); if (error) { memset(stats, 0, sizeof *stats); } @@ -279,7 +409,7 @@ dpif_get_dp_stats(const struct dpif *dpif, struct odp_stats *stats) int dpif_get_drop_frags(const struct dpif *dpif, bool *drop_frags) { - int error = dpif->class->get_drop_frags(dpif, drop_frags); + int error = dpif->dpif_class->get_drop_frags(dpif, drop_frags); if (error) { *drop_frags = false; } @@ -293,12 +423,11 @@ dpif_get_drop_frags(const struct dpif *dpif, bool *drop_frags) int dpif_set_drop_frags(struct dpif *dpif, bool drop_frags) { - int error = dpif->class->set_drop_frags(dpif, drop_frags); + int error = dpif->dpif_class->set_drop_frags(dpif, drop_frags); log_operation(dpif, "set_drop_frags", error); return error; } - /* Attempts to add 'devname' as a port on 'dpif', given the combination of * ODP_PORT_* flags in 'flags'. If successful, returns 0 and sets '*port_nop' * to the new port's port number (if 'port_nop' is non-null). On failure, @@ -313,7 +442,7 @@ dpif_port_add(struct dpif *dpif, const char *devname, uint16_t flags, COVERAGE_INC(dpif_port_add); - error = dpif->class->port_add(dpif, devname, flags, &port_no); + error = dpif->dpif_class->port_add(dpif, devname, flags, &port_no); if (!error) { VLOG_DBG_RL(&dpmsg_rl, "%s: added %s as port %"PRIu16, dpif_name(dpif), devname, port_no); @@ -337,7 +466,7 @@ dpif_port_del(struct dpif *dpif, uint16_t port_no) COVERAGE_INC(dpif_port_del); - error = dpif->class->port_del(dpif, port_no); + error = dpif->dpif_class->port_del(dpif, port_no); log_operation(dpif, "port_del", error); return error; } @@ -349,7 +478,7 @@ int dpif_port_query_by_number(const struct dpif *dpif, uint16_t port_no, struct odp_port *port) { - int error = dpif->class->port_query_by_number(dpif, port_no, port); + int error = dpif->dpif_class->port_query_by_number(dpif, port_no, port); if (!error) { VLOG_DBG_RL(&dpmsg_rl, "%s: port %"PRIu16" is device %s", dpif_name(dpif), port_no, port->devname); @@ -368,7 +497,7 @@ int dpif_port_query_by_name(const struct dpif *dpif, const char *devname, struct odp_port *port) { - int error = dpif->class->port_query_by_name(dpif, devname, port); + int error = dpif->dpif_class->port_query_by_name(dpif, devname, port); if (!error) { VLOG_DBG_RL(&dpmsg_rl, "%s: device %s is on port %"PRIu16, dpif_name(dpif), devname, port->port); @@ -433,7 +562,7 @@ dpif_port_list(const struct dpif *dpif, } ports = xcalloc(stats.n_ports, sizeof *ports); - retval = dpif->class->port_list(dpif, ports, stats.n_ports); + retval = dpif->dpif_class->port_list(dpif, ports, stats.n_ports); if (retval < 0) { /* Hard error. */ error = -retval; @@ -481,7 +610,7 @@ exit: int dpif_port_poll(const struct dpif *dpif, char **devnamep) { - int error = dpif->class->port_poll(dpif, devnamep); + int error = dpif->dpif_class->port_poll(dpif, devnamep); if (error) { *devnamep = NULL; } @@ -493,7 +622,7 @@ dpif_port_poll(const struct dpif *dpif, char **devnamep) void dpif_port_poll_wait(const struct dpif *dpif) { - dpif->class->port_poll_wait(dpif); + dpif->dpif_class->port_poll_wait(dpif); } /* Retrieves a list of the port numbers in port group 'group' in 'dpif'. @@ -514,8 +643,8 @@ dpif_port_group_get(const struct dpif *dpif, uint16_t group, *ports = NULL; *n_ports = 0; for (;;) { - int retval = dpif->class->port_group_get(dpif, group, - *ports, *n_ports); + int retval = dpif->dpif_class->port_group_get(dpif, group, + *ports, *n_ports); if (retval < 0) { /* Hard error. */ error = -retval; @@ -553,7 +682,7 @@ dpif_port_group_set(struct dpif *dpif, uint16_t group, COVERAGE_INC(dpif_port_group_set); - error = dpif->class->port_group_set(dpif, group, ports, n_ports); + error = dpif->dpif_class->port_group_set(dpif, group, ports, n_ports); log_operation(dpif, "port_group_set", error); return error; } @@ -567,7 +696,7 @@ dpif_flow_flush(struct dpif *dpif) COVERAGE_INC(dpif_flow_flush); - error = dpif->class->flow_flush(dpif); + error = dpif->dpif_class->flow_flush(dpif); log_operation(dpif, "flow_flush", error); return error; } @@ -593,10 +722,15 @@ dpif_flow_get(const struct dpif *dpif, struct odp_flow *flow) COVERAGE_INC(dpif_flow_get); check_rw_odp_flow(flow); - error = dpif->class->flow_get(dpif, flow, 1); + error = dpif->dpif_class->flow_get(dpif, flow, 1); if (!error) { error = flow->stats.error; } + if (error) { + /* Make the results predictable on error. */ + memset(&flow->stats, 0, sizeof flow->stats); + flow->n_actions = 0; + } if (should_log_flow_message(error)) { log_flow_operation(dpif, "flow_get", error, flow); } @@ -643,7 +777,7 @@ dpif_flow_get_multiple(const struct dpif *dpif, check_rw_odp_flow(&flows[i]); } - error = dpif->class->flow_get(dpif, flows, n); + error = dpif->dpif_class->flow_get(dpif, flows, n); log_operation(dpif, "flow_get_multiple", error); return error; } @@ -671,7 +805,7 @@ dpif_flow_put(struct dpif *dpif, struct odp_flow_put *put) COVERAGE_INC(dpif_flow_put); - error = dpif->class->flow_put(dpif, put); + error = dpif->dpif_class->flow_put(dpif, put); if (should_log_flow_message(error)) { log_flow_put(dpif, error, put); } @@ -693,7 +827,7 @@ dpif_flow_del(struct dpif *dpif, struct odp_flow *flow) check_rw_odp_flow(flow); memset(&flow->stats, 0, sizeof flow->stats); - error = dpif->class->flow_del(dpif, flow); + error = dpif->dpif_class->flow_del(dpif, flow); if (should_log_flow_message(error)) { log_flow_operation(dpif, "delete flow", error, flow); } @@ -722,7 +856,7 @@ dpif_flow_list(const struct dpif *dpif, struct odp_flow flows[], size_t n, flows[i].n_actions = 0; } } - retval = dpif->class->flow_list(dpif, flows, n); + retval = dpif->dpif_class->flow_list(dpif, flows, n); if (retval < 0) { *n_out = 0; VLOG_WARN_RL(&error_rl, "%s: flow list failed (%s)", @@ -799,7 +933,8 @@ dpif_execute(struct dpif *dpif, uint16_t in_port, COVERAGE_INC(dpif_execute); if (n_actions > 0) { - error = dpif->class->execute(dpif, in_port, actions, n_actions, buf); + error = dpif->dpif_class->execute(dpif, in_port, actions, + n_actions, buf); } else { error = 0; } @@ -826,7 +961,7 @@ dpif_execute(struct dpif *dpif, uint16_t in_port, int dpif_recv_get_mask(const struct dpif *dpif, int *listen_mask) { - int error = dpif->class->recv_get_mask(dpif, listen_mask); + int error = dpif->dpif_class->recv_get_mask(dpif, listen_mask); if (error) { *listen_mask = 0; } @@ -840,7 +975,7 @@ dpif_recv_get_mask(const struct dpif *dpif, int *listen_mask) int dpif_recv_set_mask(struct dpif *dpif, int listen_mask) { - int error = dpif->class->recv_set_mask(dpif, listen_mask); + int error = dpif->dpif_class->recv_set_mask(dpif, listen_mask); log_operation(dpif, "recv_set_mask", error); return error; } @@ -854,8 +989,8 @@ dpif_recv_set_mask(struct dpif *dpif, int listen_mask) int dpif_get_sflow_probability(const struct dpif *dpif, uint32_t *probability) { - int error = (dpif->class->get_sflow_probability - ? dpif->class->get_sflow_probability(dpif, probability) + int error = (dpif->dpif_class->get_sflow_probability + ? dpif->dpif_class->get_sflow_probability(dpif, probability) : EOPNOTSUPP); if (error) { *probability = 0; @@ -873,8 +1008,8 @@ dpif_get_sflow_probability(const struct dpif *dpif, uint32_t *probability) int dpif_set_sflow_probability(struct dpif *dpif, uint32_t probability) { - int error = (dpif->class->set_sflow_probability - ? dpif->class->set_sflow_probability(dpif, probability) + int error = (dpif->dpif_class->set_sflow_probability + ? dpif->dpif_class->set_sflow_probability(dpif, probability) : EOPNOTSUPP); log_operation(dpif, "set_sflow_probability", error); return error; @@ -882,7 +1017,8 @@ dpif_set_sflow_probability(struct dpif *dpif, uint32_t probability) /* Attempts to receive a message from 'dpif'. If successful, stores the * message into '*packetp'. The message, if one is received, will begin with - * 'struct odp_msg' as a header. Only messages of the types selected with + * 'struct odp_msg' as a header, and will have at least DPIF_RECV_MSG_PADDING + * bytes of headroom. Only messages of the types selected with * dpif_set_listen_mask() will ordinarily be received (but if a message type is * enabled and then later disabled, some stragglers might pop up). * @@ -891,10 +1027,12 @@ dpif_set_sflow_probability(struct dpif *dpif, uint32_t probability) int dpif_recv(struct dpif *dpif, struct ofpbuf **packetp) { - int error = dpif->class->recv(dpif, packetp); + int error = dpif->dpif_class->recv(dpif, packetp); if (!error) { + struct ofpbuf *buf = *packetp; + + assert(ofpbuf_headroom(buf) >= DPIF_RECV_MSG_PADDING); if (VLOG_IS_DBG_ENABLED()) { - struct ofpbuf *buf = *packetp; struct odp_msg *msg = buf->data; void *payload = msg + 1; size_t payload_len = buf->size - sizeof *msg; @@ -946,7 +1084,7 @@ dpif_recv_purge(struct dpif *dpif) void dpif_recv_wait(struct dpif *dpif) { - dpif->class->recv_wait(dpif); + dpif->dpif_class->recv_wait(dpif); } /* Obtains the NetFlow engine type and engine ID for 'dpif' into '*engine_type' @@ -960,14 +1098,36 @@ dpif_get_netflow_ids(const struct dpif *dpif, } void -dpif_init(struct dpif *dpif, const struct dpif_class *class, const char *name, +dpif_init(struct dpif *dpif, const struct dpif_class *dpif_class, + const char *name, uint8_t netflow_engine_type, uint8_t netflow_engine_id) { - dpif->class = class; - dpif->name = xstrdup(name); + dpif->dpif_class = dpif_class; + dpif->base_name = xstrdup(name); + dpif->full_name = xasprintf("%s@%s", dpif_class->type, name); dpif->netflow_engine_type = netflow_engine_type; dpif->netflow_engine_id = netflow_engine_id; } + +/* Undoes the results of initialization. + * + * Normally this function only needs to be called from dpif_close(). + * However, it may be called by providers due to an error on opening + * that occurs after initialization. It this case dpif_close() would + * never be called. */ +void +dpif_uninit(struct dpif *dpif, bool close) +{ + char *base_name = dpif->base_name; + char *full_name = dpif->full_name; + + if (close) { + dpif->dpif_class->close(dpif); + } + + free(base_name); + free(full_name); +} static void log_operation(const struct dpif *dpif, const char *operation, int error)