+static int
+lnet_net_append_cpts(__u32 *cpts, __u32 ncpts, struct lnet_net *net)
+{
+ __u32 *added_cpts = NULL;
+ int i, j = 0, rc = 0;
+
+ /*
+ * no need to go futher since a subset of the NIs already exist on
+ * all CPTs
+ */
+ if (net->net_ncpts == LNET_CPT_NUMBER)
+ return 0;
+
+ if (cpts == NULL) {
+ /* there is an NI which will exist on all CPTs */
+ if (net->net_cpts != NULL)
+ LIBCFS_FREE(net->net_cpts, sizeof(*net->net_cpts) *
+ net->net_ncpts);
+ net->net_cpts = NULL;
+ net->net_ncpts = LNET_CPT_NUMBER;
+ return 0;
+ }
+
+ if (net->net_cpts == NULL) {
+ LIBCFS_ALLOC(net->net_cpts, sizeof(*net->net_cpts) * ncpts);
+ if (net->net_cpts == NULL)
+ return -ENOMEM;
+ memcpy(net->net_cpts, cpts, ncpts);
+ net->net_ncpts = ncpts;
+ return 0;
+ }
+
+ LIBCFS_ALLOC(added_cpts, sizeof(*added_cpts) * LNET_CPT_NUMBER);
+ if (added_cpts == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ncpts; i++) {
+ if (!in_array(net->net_cpts, net->net_ncpts, cpts[i])) {
+ added_cpts[j] = cpts[i];
+ j++;
+ }
+ }
+
+ /* append the new cpts if any to the list of cpts in the net */
+ if (j > 0) {
+ __u32 *array = NULL, *loc;
+ __u32 total_entries = j + net->net_ncpts;
+
+ LIBCFS_ALLOC(array, sizeof(*net->net_cpts) * total_entries);
+ if (array == NULL) {
+ rc = -ENOMEM;
+ goto failed;
+ }
+
+ memcpy(array, net->net_cpts, net->net_ncpts);
+ loc = array + net->net_ncpts;
+ memcpy(loc, added_cpts, j);
+
+ LIBCFS_FREE(net->net_cpts, sizeof(*net->net_cpts) *
+ net->net_ncpts);
+ net->net_ncpts = total_entries;
+ net->net_cpts = array;
+ }
+
+failed:
+ LIBCFS_FREE(added_cpts, sizeof(*added_cpts) * LNET_CPT_NUMBER);
+
+ return rc;
+}
+
+static void
+lnet_net_remove_cpts(__u32 *cpts, __u32 ncpts, struct lnet_net *net)
+{
+ struct lnet_ni *ni;
+ int rc;
+
+ /*
+ * Operation Assumption:
+ * This function is called after an NI has been removed from
+ * its parent net.
+ *
+ * if we're removing an NI which exists on all CPTs then
+ * we have to check if any of the other NIs on this net also
+ * exists on all CPTs. If none, then we need to build our Net CPT
+ * list based on the remaining NIs.
+ *
+ * If the NI being removed exist on a subset of the CPTs then we
+ * alo rebuild the Net CPT list based on the remaining NIs, which
+ * should resutl in the expected Net CPT list.
+ */
+
+ /*
+ * sometimes this function can be called due to some failure
+ * creating an NI, before any of the cpts are allocated, so check
+ * for that case and don't do anything
+ */
+ if (ncpts == 0)
+ return;
+
+ if (ncpts == LNET_CPT_NUMBER) {
+ /*
+ * first iteration through the NI list in the net to see
+ * if any of the NIs exist on all the CPTs. If one is
+ * found then our job is done.
+ */
+ list_for_each_entry(ni, &net->net_ni_list, ni_netlist) {
+ if (ni->ni_ncpts == LNET_CPT_NUMBER)
+ return;
+ }
+ }
+
+ /*
+ * Rebuild the Net CPT list again, thereby only including only the
+ * CPTs which the remaining NIs are associated with.
+ */
+ if (net->net_cpts != NULL) {
+ LIBCFS_FREE(net->net_cpts,
+ sizeof(*net->net_cpts) * net->net_ncpts);
+ net->net_cpts = NULL;
+ }
+
+ list_for_each_entry(ni, &net->net_ni_list, ni_netlist) {
+ rc = lnet_net_append_cpts(ni->ni_cpts, ni->ni_ncpts,
+ net);
+ if (rc != 0) {
+ CERROR("Out of Memory\n");
+ /*
+ * do our best to keep on going. Delete
+ * the net cpts and set it to NULL. This
+ * way we can keep on going but less
+ * efficiently, since memory accesses might be
+ * accross CPT lines.
+ */
+ if (net->net_cpts != NULL) {
+ LIBCFS_FREE(net->net_cpts,
+ sizeof(*net->net_cpts) *
+ net->net_ncpts);
+ net->net_cpts = NULL;
+ net->net_ncpts = LNET_CPT_NUMBER;
+ }
+ return;
+ }
+ }
+}
+
+void
+lnet_ni_free(struct lnet_ni *ni)
+{
+ int i;
+
+ lnet_net_remove_cpts(ni->ni_cpts, ni->ni_ncpts, ni->ni_net);
+
+ if (ni->ni_refs != NULL)
+ cfs_percpt_free(ni->ni_refs);
+
+ if (ni->ni_tx_queues != NULL)
+ cfs_percpt_free(ni->ni_tx_queues);
+
+ if (ni->ni_cpts != NULL)
+ cfs_expr_list_values_free(ni->ni_cpts, ni->ni_ncpts);
+
+ for (i = 0; i < LNET_MAX_INTERFACES &&
+ ni->ni_interfaces[i] != NULL; i++) {
+ LIBCFS_FREE(ni->ni_interfaces[i],
+ strlen(ni->ni_interfaces[i]) + 1);
+ }
+
+ /* release reference to net namespace */
+ if (ni->ni_net_ns != NULL)
+ put_net(ni->ni_net_ns);
+
+ LIBCFS_FREE(ni, sizeof(*ni));
+}
+
+void
+lnet_net_free(struct lnet_net *net)
+{
+ struct list_head *tmp, *tmp2;
+ struct lnet_ni *ni;
+
+ LASSERT(list_empty(&net->net_ni_zombie));
+
+ /*
+ * delete any nis that haven't been added yet. This could happen
+ * if there is a failure on net startup
+ */
+ list_for_each_safe(tmp, tmp2, &net->net_ni_added) {
+ ni = list_entry(tmp, struct lnet_ni, ni_netlist);
+ list_del_init(&ni->ni_netlist);
+ lnet_ni_free(ni);
+ }
+
+ /* delete any nis which have been started. */
+ list_for_each_safe(tmp, tmp2, &net->net_ni_list) {
+ ni = list_entry(tmp, struct lnet_ni, ni_netlist);
+ list_del_init(&ni->ni_netlist);
+ lnet_ni_free(ni);
+ }
+
+ if (net->net_cpts != NULL)
+ LIBCFS_FREE(net->net_cpts,
+ sizeof(*net->net_cpts) * net->net_ncpts);
+
+ LIBCFS_FREE(net, sizeof(*net));
+}
+
+struct lnet_net *
+lnet_net_alloc(__u32 net_id, struct list_head *net_list)
+{
+ struct lnet_net *net;
+
+ if (!lnet_net_unique(net_id, net_list, NULL)) {
+ CERROR("Duplicate net %s. Ignore\n",
+ libcfs_net2str(net_id));
+ return NULL;
+ }
+
+ LIBCFS_ALLOC(net, sizeof(*net));
+ if (net == NULL) {
+ CERROR("Out of memory creating network %s\n",
+ libcfs_net2str(net_id));
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&net->net_list);
+ INIT_LIST_HEAD(&net->net_ni_list);
+ INIT_LIST_HEAD(&net->net_ni_added);
+ INIT_LIST_HEAD(&net->net_ni_zombie);
+
+ net->net_id = net_id;
+ net->net_state = LNET_NET_STATE_INIT;
+
+ /* initialize global paramters to undefiend */
+ net->net_tunables.lct_peer_timeout = -1;
+ net->net_tunables.lct_max_tx_credits = -1;
+ net->net_tunables.lct_peer_tx_credits = -1;
+ net->net_tunables.lct_peer_rtr_credits = -1;
+
+ if (net_list)
+ list_add_tail(&net->net_list, net_list);
+
+ return net;
+}
+
+static int
+lnet_ni_add_interface(struct lnet_ni *ni, char *iface)
+{
+ int niface = 0;
+
+ if (ni == NULL)
+ return -ENOMEM;
+
+ if (!lnet_ni_unique_ni(ni->ni_interfaces, iface))
+ return -EINVAL;
+
+ /* Allocate a separate piece of memory and copy
+ * into it the string, so we don't have
+ * a depencency on the tokens string. This way we
+ * can free the tokens at the end of the function.
+ * The newly allocated ni_interfaces[] can be
+ * freed when freeing the NI */
+ while (niface < LNET_MAX_INTERFACES &&
+ ni->ni_interfaces[niface] != NULL)
+ niface++;
+
+ if (niface >= LNET_MAX_INTERFACES) {
+ LCONSOLE_ERROR_MSG(0x115, "Too many interfaces "
+ "for net %s\n",
+ libcfs_net2str(LNET_NIDNET(ni->ni_nid)));
+ return -EINVAL;
+ }
+
+ LIBCFS_ALLOC(ni->ni_interfaces[niface],
+ strlen(iface) + 1);
+
+ if (ni->ni_interfaces[niface] == NULL) {
+ CERROR("Can't allocate net interface name\n");
+ return -ENOMEM;
+ }
+
+ strncpy(ni->ni_interfaces[niface], iface,
+ strlen(iface) + 1);
+
+ return 0;
+}
+
+static struct lnet_ni *
+lnet_ni_alloc_common(struct lnet_net *net, char *iface)
+{
+ struct lnet_tx_queue *tq;
+ struct lnet_ni *ni;
+ int i;
+
+ if (iface != NULL)
+ /* make sure that this NI is unique in the net it's
+ * being added to */
+ if (!lnet_ni_unique_net(&net->net_ni_added, iface))
+ return NULL;
+
+ LIBCFS_ALLOC(ni, sizeof(*ni));
+ if (ni == NULL) {
+ CERROR("Out of memory creating network interface %s%s\n",
+ libcfs_net2str(net->net_id),
+ (iface != NULL) ? iface : "");
+ return NULL;
+ }
+
+ spin_lock_init(&ni->ni_lock);
+ INIT_LIST_HEAD(&ni->ni_cptlist);
+ INIT_LIST_HEAD(&ni->ni_netlist);
+ ni->ni_refs = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(*ni->ni_refs[0]));
+ if (ni->ni_refs == NULL)
+ goto failed;
+
+ ni->ni_tx_queues = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(*ni->ni_tx_queues[0]));
+ if (ni->ni_tx_queues == NULL)
+ goto failed;
+
+ cfs_percpt_for_each(tq, i, ni->ni_tx_queues)
+ INIT_LIST_HEAD(&tq->tq_delayed);
+
+ ni->ni_net = net;
+ /* LND will fill in the address part of the NID */
+ ni->ni_nid = LNET_MKNID(net->net_id, 0);
+
+ /* Store net namespace in which current ni is being created */
+ if (current->nsproxy->net_ns != NULL)
+ ni->ni_net_ns = get_net(current->nsproxy->net_ns);
+ else
+ ni->ni_net_ns = NULL;
+
+ ni->ni_last_alive = ktime_get_real_seconds();
+ ni->ni_state = LNET_NI_STATE_INIT;
+ list_add_tail(&ni->ni_netlist, &net->net_ni_added);
+
+ /*
+ * if an interface name is provided then make sure to add in that
+ * interface name in NI
+ */
+ if (iface)
+ if (lnet_ni_add_interface(ni, iface) != 0)
+ goto failed;
+
+ return ni;
+failed:
+ lnet_ni_free(ni);
+ return NULL;
+}
+
+/* allocate and add to the provided network */
+struct lnet_ni *
+lnet_ni_alloc(struct lnet_net *net, struct cfs_expr_list *el, char *iface)
+{
+ struct lnet_ni *ni;
+ int rc;
+
+ ni = lnet_ni_alloc_common(net, iface);
+ if (!ni)
+ return NULL;
+
+ if (!el) {
+ ni->ni_cpts = NULL;
+ ni->ni_ncpts = LNET_CPT_NUMBER;
+ } else {
+ rc = cfs_expr_list_values(el, LNET_CPT_NUMBER, &ni->ni_cpts);
+ if (rc <= 0) {
+ CERROR("Failed to set CPTs for NI %s(%s): %d\n",
+ libcfs_net2str(net->net_id),
+ (iface != NULL) ? iface : "", rc);
+ goto failed;
+ }
+
+ LASSERT(rc <= LNET_CPT_NUMBER);
+ if (rc == LNET_CPT_NUMBER) {
+ LIBCFS_FREE(ni->ni_cpts, rc * sizeof(ni->ni_cpts[0]));
+ ni->ni_cpts = NULL;
+ }
+
+ ni->ni_ncpts = rc;
+ }
+
+ rc = lnet_net_append_cpts(ni->ni_cpts, ni->ni_ncpts, net);
+ if (rc != 0)
+ goto failed;
+
+ return ni;
+failed:
+ lnet_ni_free(ni);
+ return NULL;
+}
+
+struct lnet_ni *
+lnet_ni_alloc_w_cpt_array(struct lnet_net *net, __u32 *cpts, __u32 ncpts,
+ char *iface)
+{
+ struct lnet_ni *ni;
+ int rc;
+
+ ni = lnet_ni_alloc_common(net, iface);
+ if (!ni)
+ return NULL;
+
+ if (ncpts == 0) {
+ ni->ni_cpts = NULL;
+ ni->ni_ncpts = LNET_CPT_NUMBER;
+ } else {
+ size_t array_size = ncpts * sizeof(ni->ni_cpts[0]);
+ LIBCFS_ALLOC(ni->ni_cpts, array_size);
+ if (ni->ni_cpts == NULL)
+ goto failed;
+ memcpy(ni->ni_cpts, cpts, array_size);
+ ni->ni_ncpts = ncpts;
+ }
+
+ rc = lnet_net_append_cpts(ni->ni_cpts, ni->ni_ncpts, net);
+ if (rc != 0)
+ goto failed;
+
+ return ni;
+failed:
+ lnet_ni_free(ni);
+ return NULL;
+}
+
+/*
+ * Parse the networks string and create the matching set of NIs on the
+ * nilist.
+ */