datapath: Change ODP_PORT_LIST semantics.
[openvswitch] / lib / dpif.c
index 21f1173fa8fe132479e1e03255aad9db7a20d0d9..85d05bb714ce61df9078aa8654f71a183c02dab7 100644 (file)
 #include "vlog.h"
 #define THIS_MODULE VLM_dpif
 
+/* A datapath interface. */
+struct dpif {
+    char *name;
+    unsigned int minor;
+    int fd;
+};
+
 /* 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
  * we really need to see them. */
@@ -60,24 +67,26 @@ static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
 static int get_minor_from_name(const char *name, unsigned int *minor);
 static int name_to_minor(const char *name, unsigned int *minor);
 static int lookup_minor(const char *name, unsigned int *minor);
-static int open_by_minor(unsigned int minor, struct dpif *);
+static int open_by_minor(unsigned int minor, struct dpif **dpifp);
 static int make_openvswitch_device(unsigned int minor, char **fnp);
 static void check_rw_odp_flow(struct odp_flow *);
 
 int
-dpif_open(const char *name, struct dpif *dpif)
+dpif_open(const char *name, struct dpif **dpifp)
 {
+    struct dpif *dpif;
+    unsigned int minor;
     int listen_mask;
     int error;
 
-    dpif->fd = -1;
+    *dpifp = NULL;
 
-    error = name_to_minor(name, &dpif->minor);
+    error = name_to_minor(name, &minor);
     if (error) {
         return error;
     }
 
-    error = open_by_minor(dpif->minor, dpif);
+    error = open_by_minor(minor, &dpif);
     if (error) {
         return error;
     }
@@ -89,12 +98,13 @@ dpif_open(const char *name, struct dpif *dpif)
     if (ioctl(dpif->fd, ODP_GET_LISTEN_MASK, &listen_mask)) {
         error = errno;
         if (error != ENODEV) {
-            VLOG_WARN("dp%u: probe returned unexpected error: %s",
-                      dpif->minor, strerror(error));
+            VLOG_WARN("%s: probe returned unexpected error: %s",
+                      dpif_name(dpif), strerror(error));
         }
         dpif_close(dpif);
         return error;
     }
+    *dpifp = dpif;
     return 0;
 }
 
@@ -102,8 +112,9 @@ void
 dpif_close(struct dpif *dpif)
 {
     if (dpif) {
+        free(dpif->name);
         close(dpif->fd);
-        dpif->fd = -1;
+        free(dpif);
     }
 }
 
@@ -114,49 +125,51 @@ do_ioctl(const struct dpif *dpif, int cmd, const char *cmd_name,
     int error = ioctl(dpif->fd, cmd, arg) ? errno : 0;
     if (cmd_name) {
         if (error) {
-            VLOG_WARN_RL(&error_rl, "dp%u: ioctl(%s) failed (%s)",
-                         dpif->minor, cmd_name, strerror(error));
+            VLOG_WARN_RL(&error_rl, "%s: ioctl(%s) failed (%s)",
+                         dpif_name(dpif), cmd_name, strerror(error));
         } else {
-            VLOG_DBG_RL(&dpmsg_rl, "dp%u: ioctl(%s): success",
-                        dpif->minor, cmd_name);
+            VLOG_DBG_RL(&dpmsg_rl, "%s: ioctl(%s): success",
+                        dpif_name(dpif), cmd_name);
         }
     }
     return error;
 }
 
 int
-dpif_create(const char *name, struct dpif *dpif)
+dpif_create(const char *name, struct dpif **dpifp)
 {
     unsigned int minor;
     int error;
 
+    *dpifp = NULL;
     if (!get_minor_from_name(name, &minor)) {
         /* Minor was specified in 'name', go ahead and create it. */
-        error = open_by_minor(minor, dpif);
+        struct dpif *dpif;
+
+        error = open_by_minor(minor, &dpif);
         if (error) {
             return error;
         }
 
-        if (!strncmp(name, "nl:", 3)) {
-            char devname[128];
-            sprintf(devname, "of%u", minor);
-            error = ioctl(dpif->fd, ODP_DP_CREATE, devname) < 0 ? errno : 0;
+        error = ioctl(dpif->fd, ODP_DP_CREATE, name) < 0 ? errno : 0;
+        if (!error) {
+            *dpifp = dpif;
         } else {
-            error = ioctl(dpif->fd, ODP_DP_CREATE, name) < 0 ? errno : 0;
-        }
-        if (error) {
             dpif_close(dpif);
         }
         return error;
     } else {
         for (minor = 0; minor < ODP_MAX; minor++) {
-            error = open_by_minor(minor, dpif);
+            struct dpif *dpif;
+
+            error = open_by_minor(minor, &dpif);
             if (error) {
                 return error;
             }
 
             error = ioctl(dpif->fd, ODP_DP_CREATE, name) < 0 ? errno : 0;
             if (!error) {
+                *dpifp = dpif;
                 return 0;
             }
             dpif_close(dpif);
@@ -168,20 +181,10 @@ dpif_create(const char *name, struct dpif *dpif)
     }
 }
 
-int
-dpif_get_name(struct dpif *dpif, char *name, size_t name_size)
+const char *
+dpif_name(const struct dpif *dpif)
 {
-    struct odp_port port;
-    int error;
-
-    assert(name_size > 0);
-    *name = '\0';
-
-    error = dpif_port_query_by_number(dpif, ODPP_LOCAL, &port);
-    if (!error) {
-        ovs_strlcpy(name, port.devname, name_size);
-    }
-    return error;
+    return dpif->name;
 }
 
 int
@@ -215,25 +218,7 @@ dpif_set_drop_frags(struct dpif *dpif, bool drop_frags)
 }
 
 int
-dpif_get_listen_mask(const struct dpif *dpif, int *listen_mask)
-{
-    int error = do_ioctl(dpif, ODP_GET_LISTEN_MASK, "ODP_GET_LISTEN_MASK",
-                         listen_mask);
-    if (error) {
-        *listen_mask = 0;
-    }
-    return error;
-}
-
-int
-dpif_set_listen_mask(struct dpif *dpif, int listen_mask)
-{
-    return do_ioctl(dpif, ODP_SET_LISTEN_MASK, "ODP_SET_LISTEN_MASK",
-                    &listen_mask);
-}
-
-int
-dpif_purge(struct dpif *dpif)
+dpif_recv_purge(struct dpif *dpif)
 {
     struct odp_stats stats;
     unsigned int i;
@@ -258,26 +243,33 @@ dpif_purge(struct dpif *dpif)
 }
 
 int
-dpif_port_add(struct dpif *dpif, const char *devname, uint16_t port_no,
-              uint16_t flags)
+dpif_port_add(struct dpif *dpif, const char *devname, uint16_t flags,
+              uint16_t *port_nop)
 {
     struct odp_port port;
+    uint16_t port_no;
+    int error;
 
     COVERAGE_INC(dpif_port_add);
+
     memset(&port, 0, sizeof port);
     strncpy(port.devname, devname, sizeof port.devname);
-    port.port = port_no;
     port.flags = flags;
-    if (!ioctl(dpif->fd, ODP_PORT_ADD, &port)) {
-        VLOG_DBG_RL(&dpmsg_rl, "dp%u: added %s as port %"PRIu16,
-                    dpif->minor, devname, port_no);
-        return 0;
+
+    error = do_ioctl(dpif, ODP_PORT_ADD, NULL, &port);
+    if (!error) {
+        port_no = port.port;
+        VLOG_DBG_RL(&dpmsg_rl, "%s: added %s as port %"PRIu16,
+                    dpif_name(dpif), devname, port_no);
     } else {
-        VLOG_WARN_RL(&error_rl, "dp%u: failed to add %s as port "
-                     "%"PRIu16": %s", dpif->minor, devname, port_no,
-                     strerror(errno));
-        return errno;
+        port_no = UINT16_MAX;
+        VLOG_WARN_RL(&error_rl, "%s: failed to add %s as port: %s",
+                     dpif_name(dpif), devname, strerror(errno));
     }
+    if (port_nop) {
+        *port_nop = port_no;
+    }
+    return error;
 }
 
 int
@@ -295,12 +287,12 @@ dpif_port_query_by_number(const struct dpif *dpif, uint16_t port_no,
     memset(port, 0, sizeof *port);
     port->port = port_no;
     if (!ioctl(dpif->fd, ODP_PORT_QUERY, port)) {
-        VLOG_DBG_RL(&dpmsg_rl, "dp%u: port %"PRIu16" is device %s",
-                    dpif->minor, port_no, port->devname);
+        VLOG_DBG_RL(&dpmsg_rl, "%s: port %"PRIu16" is device %s",
+                    dpif_name(dpif), port_no, port->devname);
         return 0;
     } else {
-        VLOG_WARN_RL(&error_rl, "dp%u: failed to query port %"PRIu16": %s",
-                     dpif->minor, port_no, strerror(errno));
+        VLOG_WARN_RL(&error_rl, "%s: failed to query port %"PRIu16": %s",
+                     dpif_name(dpif), port_no, strerror(errno));
         return errno;
     }
 }
@@ -312,45 +304,81 @@ dpif_port_query_by_name(const struct dpif *dpif, const char *devname,
     memset(port, 0, sizeof *port);
     strncpy(port->devname, devname, sizeof port->devname);
     if (!ioctl(dpif->fd, ODP_PORT_QUERY, port)) {
-        VLOG_DBG_RL(&dpmsg_rl, "dp%u: device %s is on port %"PRIu16,
-                    dpif->minor, devname, port->port);
+        VLOG_DBG_RL(&dpmsg_rl, "%s: device %s is on port %"PRIu16,
+                    dpif_name(dpif), devname, port->port);
         return 0;
     } else {
-        VLOG_WARN_RL(&error_rl, "dp%u: failed to query port %s: %s",
-                     dpif->minor, devname, strerror(errno));
+        /* Log level is DBG here because all the current callers are interested
+         * in whether 'dpif' actually has a port 'devname', so that it's not an
+         * issue worth logging if it doesn't. */
+        VLOG_DBG_RL(&error_rl, "%s: failed to query port %s: %s",
+                    dpif_name(dpif), devname, strerror(errno));
         return errno;
     }
 }
 
+int
+dpif_port_get_name(struct dpif *dpif, uint16_t port_no,
+                   char *name, size_t name_size)
+{
+    struct odp_port port;
+    int error;
+
+    assert(name_size > 0);
+
+    error = dpif_port_query_by_number(dpif, port_no, &port);
+    if (!error) {
+        ovs_strlcpy(name, port.devname, name_size);
+    } else {
+        *name = '\0';
+    }
+    return error;
+}
+
 int
 dpif_port_list(const struct dpif *dpif,
-               struct odp_port **ports, size_t *n_ports)
+               struct odp_port **portsp, size_t *n_portsp)
 {
-    struct odp_portvec pv;
-    struct odp_stats stats;
+    struct odp_port *ports;
+    size_t n_ports;
     int error;
 
-    do {
+    for (;;) {
+        struct odp_stats stats;
+        struct odp_portvec pv;
+
         error = dpif_get_dp_stats(dpif, &stats);
         if (error) {
-            goto error;
+            goto exit;
         }
 
-        *ports = xcalloc(1, stats.n_ports * sizeof **ports);
-        pv.ports = *ports;
+        ports = xcalloc(stats.n_ports, sizeof *ports);
+        pv.ports = ports;
         pv.n_ports = stats.n_ports;
         error = do_ioctl(dpif, ODP_PORT_LIST, "ODP_PORT_LIST", &pv);
         if (error) {
-            free(*ports);
-            goto error;
+            /* Hard error. */
+            free(ports);
+            goto exit;
+        } else if (pv.n_ports <= stats.n_ports) {
+            /* Success. */
+            error = 0;
+            n_ports = pv.n_ports;
+            goto exit;
+        } else {
+            /* Soft error: port count increased behind our back.  Try again. */
+            free(ports);
         }
-    } while (pv.n_ports != stats.n_ports);
-    *n_ports = pv.n_ports;
-    return 0;
+    }
 
-error:
-    *ports = NULL;
-    *n_ports = 0;
+exit:
+    if (error) {
+        *portsp = NULL;
+        *n_portsp = 0;
+    } else {
+        *portsp = ports;
+        *n_portsp = n_ports;
+    }
     return error;
 }
 
@@ -368,21 +396,39 @@ dpif_port_group_set(struct dpif *dpif, uint16_t group,
     return do_ioctl(dpif, ODP_PORT_GROUP_SET, "ODP_PORT_GROUP_SET", &pg);
 }
 
-/* Careful: '*n_out' can be greater than 'n_ports' on return, if 'n_ports' is
- * less than the number of ports in 'group'. */
 int
 dpif_port_group_get(const struct dpif *dpif, uint16_t group,
-                    uint16_t ports[], size_t n_ports, size_t *n_out)
+                    uint16_t **ports, size_t *n_ports)
 {
-    struct odp_port_group pg;
     int error;
 
-    assert(n_ports <= UINT16_MAX);
-    pg.group = group;
-    pg.ports = ports;
-    pg.n_ports = n_ports;
-    error = do_ioctl(dpif, ODP_PORT_GROUP_GET, "ODP_PORT_GROUP_GET", &pg);
-    *n_out = error ? 0 : pg.n_ports;
+    *ports = NULL;
+    *n_ports = 0;
+    for (;;) {
+        struct odp_port_group pg;
+        pg.group = group;
+        pg.ports = *ports;
+        pg.n_ports = *n_ports;
+
+        error = do_ioctl(dpif, ODP_PORT_GROUP_GET, "ODP_PORT_GROUP_GET", &pg);
+        if (error) {
+            /* Hard error. */
+            free(*ports);
+            *ports = NULL;
+            *n_ports = 0;
+            break;
+        } else if (pg.n_ports <= *n_ports) {
+            /* Success. */
+            *n_ports = pg.n_ports;
+            break;
+        } else {
+            /* Soft error: there were more ports than we expected in the
+             * group.  Try again. */
+            free(*ports);
+            *ports = xcalloc(pg.n_ports, sizeof **ports);
+            *n_ports = pg.n_ports;
+        }
+    }
     return error;
 }
 
@@ -413,7 +459,7 @@ log_flow_message(const struct dpif *dpif, int error,
                  const union odp_action *actions, size_t n_actions)
 {
     struct ds ds = DS_EMPTY_INITIALIZER;
-    ds_put_format(&ds, "dp%u: ", dpif->minor);
+    ds_put_format(&ds, "%s: ", dpif_name(dpif));
     if (error) {
         ds_put_cstr(&ds, "failed to ");
     }
@@ -491,10 +537,23 @@ dpif_flow_del(struct dpif *dpif, struct odp_flow *flow)
 int
 dpif_flow_get(const struct dpif *dpif, struct odp_flow *flow)
 {
-    COVERAGE_INC(dpif_flow_query);
+    struct odp_flowvec fv;
+    int error;
+
+    COVERAGE_INC(dpif_flow_get);
+
     check_rw_odp_flow(flow);
-    memset(&flow->stats, 0, sizeof flow->stats);
-    return do_flow_ioctl(dpif, ODP_FLOW_GET, flow, "get flow", true);
+    fv.flows = flow;
+    fv.n_flows = 1;
+    error = do_ioctl(dpif, ODP_FLOW_GET, "ODP_FLOW_GET", &fv);
+    if (!error) {
+        error = flow->stats.error;
+        if (error) {
+            VLOG_WARN_RL(&error_rl, "%s: ioctl(ODP_FLOW_GET) failed (%s)",
+                         dpif_name(dpif), strerror(error));
+        }
+    }
+    return error;
 }
 
 int
@@ -510,7 +569,7 @@ dpif_flow_get_multiple(const struct dpif *dpif,
     for (i = 0; i < n; i++) {
         check_rw_odp_flow(&flows[i]);
     }
-    return do_ioctl(dpif, ODP_FLOW_GET_MULTIPLE, "ODP_FLOW_GET_MULTIPLE",
+    return do_ioctl(dpif, ODP_FLOW_GET, "ODP_FLOW_GET",
                     &fv);
 }
 
@@ -536,12 +595,13 @@ dpif_flow_list(const struct dpif *dpif, struct odp_flow flows[], size_t n,
     error = do_ioctl(dpif, ODP_FLOW_LIST, NULL, &fv);
     if (error) {
         *n_out = 0;
-        VLOG_WARN_RL(&error_rl, "dp%u: flow list failed (%s)",
-                     dpif->minor, strerror(error));
+        VLOG_WARN_RL(&error_rl, "%s: flow list failed (%s)",
+                     dpif_name(dpif), strerror(error));
     } else {
         COVERAGE_ADD(dpif_flow_query_list_n, fv.n_flows);
         *n_out = fv.n_flows;
-        VLOG_DBG_RL(&dpmsg_rl, "dp%u: listed %zu flows", dpif->minor, *n_out);
+        VLOG_DBG_RL(&dpmsg_rl, "%s: listed %zu flows",
+                    dpif_name(dpif), *n_out);
     }
     return error;
 }
@@ -571,9 +631,9 @@ dpif_flow_list_all(const struct dpif *dpif,
     }
 
     if (stats.n_flows != n_flows) {
-        VLOG_WARN_RL(&error_rl, "dp%u: datapath stats reported %"PRIu32" "
+        VLOG_WARN_RL(&error_rl, "%s: datapath stats reported %"PRIu32" "
                      "flows but flow listing reported %zu",
-                     dpif->minor, stats.n_flows, n_flows);
+                     dpif_name(dpif), stats.n_flows, n_flows);
     }
     *flowsp = flows;
     *np = n_flows;
@@ -604,7 +664,7 @@ dpif_execute(struct dpif *dpif, uint16_t in_port,
     if (!(error ? VLOG_DROP_WARN(&error_rl) : VLOG_DROP_DBG(&dpmsg_rl))) {
         struct ds ds = DS_EMPTY_INITIALIZER;
         char *packet = ofp_packet_to_string(buf->data, buf->size, buf->size);
-        ds_put_format(&ds, "dp%u: execute ", dpif->minor);
+        ds_put_format(&ds, "%s: execute ", dpif_name(dpif));
         format_odp_actions(&ds, actions, n_actions);
         if (error) {
             ds_put_format(&ds, " failed (%s)", strerror(error));
@@ -617,6 +677,24 @@ dpif_execute(struct dpif *dpif, uint16_t in_port,
     return error;
 }
 
+int
+dpif_recv_get_mask(const struct dpif *dpif, int *listen_mask)
+{
+    int error = do_ioctl(dpif, ODP_GET_LISTEN_MASK, "ODP_GET_LISTEN_MASK",
+                         listen_mask);
+    if (error) {
+        *listen_mask = 0;
+    }
+    return error;
+}
+
+int
+dpif_recv_set_mask(struct dpif *dpif, int listen_mask)
+{
+    return do_ioctl(dpif, ODP_SET_LISTEN_MASK, "ODP_SET_LISTEN_MASK",
+                    &listen_mask);
+}
+
 int
 dpif_recv(struct dpif *dpif, struct ofpbuf **bufp)
 {
@@ -629,8 +707,8 @@ dpif_recv(struct dpif *dpif, struct ofpbuf **bufp)
     if (retval < 0) {
         error = errno;
         if (error != EAGAIN) {
-            VLOG_WARN_RL(&error_rl, "dp%u: read failed: %s",
-                         dpif->minor, strerror(error));
+            VLOG_WARN_RL(&error_rl, "%s: read failed: %s",
+                         dpif_name(dpif), strerror(error));
         }
     } else if (retval >= sizeof(struct odp_msg)) {
         struct odp_msg *msg = buf->data;
@@ -640,8 +718,8 @@ dpif_recv(struct dpif *dpif, struct ofpbuf **bufp)
                 void *payload = msg + 1;
                 size_t length = buf->size - sizeof *msg;
                 char *s = ofp_packet_to_string(payload, length, length);
-                VLOG_DBG_RL(&dpmsg_rl, "dp%u: received %s message of length "
-                            "%zu on port %"PRIu16": %s", dpif->minor,
+                VLOG_DBG_RL(&dpmsg_rl, "%s: received %s message of length "
+                            "%zu on port %"PRIu16": %s", dpif_name(dpif),
                             (msg->type == _ODPL_MISS_NR ? "miss"
                              : msg->type == _ODPL_ACTION_NR ? "action"
                              : "<unknown>"),
@@ -653,18 +731,18 @@ dpif_recv(struct dpif *dpif, struct ofpbuf **bufp)
             COVERAGE_INC(dpif_recv);
             return 0;
         } else {
-            VLOG_WARN_RL(&error_rl, "dp%u: discarding message truncated "
+            VLOG_WARN_RL(&error_rl, "%s: discarding message truncated "
                          "from %zu bytes to %d",
-                         dpif->minor, msg->length, retval);
+                         dpif_name(dpif), msg->length, retval);
             error = ERANGE;
         }
     } else if (!retval) {
-        VLOG_WARN_RL(&error_rl, "dp%u: unexpected end of file", dpif->minor);
+        VLOG_WARN_RL(&error_rl, "%s: unexpected end of file", dpif_name(dpif));
         error = EPROTO;
     } else {
         VLOG_WARN_RL(&error_rl,
-                     "dp%u: discarding too-short message (%d bytes)",
-                     dpif->minor, retval);
+                     "%s: discarding too-short message (%d bytes)",
+                     dpif_name(dpif), retval);
         error = ERANGE;
     }
 
@@ -678,9 +756,16 @@ dpif_recv_wait(struct dpif *dpif)
 {
     poll_fd_wait(dpif->fd, POLLIN);
 }
+
+void
+dpif_get_netflow_ids(const struct dpif *dpif,
+                     uint8_t *engine_type, uint8_t *engine_id)
+{
+    *engine_type = *engine_id = dpif->minor;
+}
 \f
 struct dpifmon {
-    struct dpif dpif;
+    struct dpif *dpif;
     struct nl_sock *sock;
     int local_ifindex;
 };
@@ -698,7 +783,8 @@ dpifmon_create(const char *datapath_name, struct dpifmon **monp)
     if (error) {
         goto error;
     }
-    error = dpif_get_name(&mon->dpif, local_name, sizeof local_name);
+    error = dpif_port_get_name(mon->dpif, ODPP_LOCAL,
+                               local_name, sizeof local_name);
     if (error) {
         goto error_close_dpif;
     }
@@ -720,7 +806,7 @@ dpifmon_create(const char *datapath_name, struct dpifmon **monp)
     return 0;
 
 error_close_dpif:
-    dpif_close(&mon->dpif);
+    dpif_close(mon->dpif);
 error:
     free(mon);
     *monp = NULL;
@@ -731,7 +817,7 @@ void
 dpifmon_destroy(struct dpifmon *mon)
 {
     if (mon) {
-        dpif_close(&mon->dpif);
+        dpif_close(mon->dpif);
         nl_sock_destroy(mon->sock);
     }
 }
@@ -766,13 +852,9 @@ again:
                 uint32_t master_ifindex = nl_attr_get_u32(attrs[IFLA_MASTER]);
                 for_us = master_ifindex == mon->local_ifindex;
             } else {
-                /* It's for us if that device is one of our ports.  This is
-                 * open-coded instead of using dpif_port_query_by_name() to
-                 * avoid logging a warning on failure. */
+                /* It's for us if that device is one of our ports. */
                 struct odp_port port;
-                memset(&port, 0, sizeof port);
-                strncpy(port.devname, devname, sizeof port.devname);
-                for_us = !ioctl(mon->dpif.fd, ODP_PORT_QUERY, &port);
+                for_us = !dpif_port_query_by_name(mon->dpif, devname, &port);
             }
 
             if (!for_us) {
@@ -998,24 +1080,20 @@ get_minor_from_name(const char *name, unsigned int *minor)
     if (!strncmp(name, "dp", 2) && isdigit(name[2])) {
         *minor = atoi(name + 2);
         return 0;
-    } else if (!strncmp(name, "nl:", 3) && isdigit(name[3])) {
-        /* This is for compatibility only and will be dropped. */
-        *minor = atoi(name + 3);
-        return 0;
     } else {
         return EINVAL;
     }
 }
 
 static int
-open_by_minor(unsigned int minor, struct dpif *dpif)
+open_by_minor(unsigned int minor, struct dpif **dpifp)
 {
+    struct dpif *dpif;
     int error;
     char *fn;
     int fd;
 
-    dpif->minor = -1;
-    dpif->fd = -1;
+    *dpifp = NULL;
     error = make_openvswitch_device(minor, &fn);
     if (error) {
         return error;
@@ -1028,10 +1106,13 @@ open_by_minor(unsigned int minor, struct dpif *dpif)
         free(fn);
         return error;
     }
-
     free(fn);
+
+    dpif = xmalloc(sizeof *dpif);
+    dpif->name = xasprintf("dp%u", dpif->minor);
     dpif->minor = minor;
     dpif->fd = fd;
+    *dpifp = dpif;
     return 0;
 }
 \f