On Sat, Jul 16, 2022 at 10:28:56AM +0000, Visa Hankala wrote:
> The index of a network interface is assigned in if_idxmap_insert().
> ifq_init() and ifiq_init() use if_index before it has its final value.
> As a consequence, interfaces tend to use net_tq(0) as their first
> softnet task queue even though the other softnet task queues could be
> used as well. To fix this, allocate if_index before queue setup.
>
> The patch tracks index allocations using a separate bitmap so that
> if_get() does not need extra logic.
>
> Because the memory allocation of the map array can sleep, replace the
> kernel lock with an rwlock to serialize idxmap updates more robustly.
>
> Also, correct the "too many interfaces" condition because the valid
> index range is from 1 to USHRT_MAX.
>
> OK?
OK bluhm@
> Index: net/if.c
> ===================================================================
> RCS file: src/sys/net/if.c,v
> retrieving revision 1.659
> diff -u -p -r1.659 if.c
> --- net/if.c 14 Jul 2022 11:03:15 -0000 1.659
> +++ net/if.c 16 Jul 2022 10:07:14 -0000
> @@ -217,9 +217,12 @@ struct if_idxmap {
> unsigned int serial;
> unsigned int count;
> struct srp map;
> + struct rwlock lock;
> + unsigned char *usedidx; /* bitmap of indices in use */
> };
>
> void if_idxmap_init(unsigned int);
> +void if_idxmap_alloc(struct ifnet *);
> void if_idxmap_insert(struct ifnet *);
> void if_idxmap_remove(struct ifnet *);
>
> @@ -273,7 +276,9 @@ ifinit(void)
> static struct if_idxmap if_idxmap = {
> 0,
> 0,
> - SRP_INITIALIZER()
> + SRP_INITIALIZER(),
> + RWLOCK_INITIALIZER("idxmaplk"),
> + NULL,
> };
>
> struct srp_gc if_ifp_gc = SRP_GC_INITIALIZER(if_ifp_dtor, NULL);
> @@ -298,12 +303,15 @@ if_idxmap_init(unsigned int limit)
> for (i = 0; i < limit; i++)
> srp_init(&map[i]);
>
> + if_idxmap.usedidx = malloc(howmany(limit, NBBY),
> + M_IFADDR, M_WAITOK | M_ZERO);
> +
> /* this is called early so there's nothing to race with */
> srp_update_locked(&if_map_gc, &if_idxmap.map, if_map);
> }
>
> void
> -if_idxmap_insert(struct ifnet *ifp)
> +if_idxmap_alloc(struct ifnet *ifp)
> {
> struct if_map *if_map;
> struct srp *map;
> @@ -311,10 +319,9 @@ if_idxmap_insert(struct ifnet *ifp)
>
> refcnt_init(&ifp->if_refcnt);
>
> - /* the kernel lock guarantees serialised modifications to if_idxmap */
> - KERNEL_ASSERT_LOCKED();
> + rw_enter_write(&if_idxmap.lock);
>
> - if (++if_idxmap.count > USHRT_MAX)
> + if (++if_idxmap.count >= USHRT_MAX)
> panic("too many interfaces");
>
> if_map = srp_get_locked(&if_idxmap.map);
> @@ -327,6 +334,7 @@ if_idxmap_insert(struct ifnet *ifp)
> struct srp *nmap;
> unsigned int nlimit;
> struct ifnet *nifp;
> + unsigned char *nusedidx;
>
> nlimit = if_map->limit * 2;
> nif_map = malloc(sizeof(*nif_map) + nlimit * sizeof(*nmap),
> @@ -348,6 +356,14 @@ if_idxmap_insert(struct ifnet *ifp)
> i++;
> }
>
> + nusedidx = malloc(howmany(nlimit, NBBY),
> + M_IFADDR, M_WAITOK | M_ZERO);
> + memcpy(nusedidx, if_idxmap.usedidx,
> + howmany(if_map->limit, NBBY));
> + free(if_idxmap.usedidx, M_IFADDR,
> + howmany(if_map->limit, NBBY));
> + if_idxmap.usedidx = nusedidx;
> +
> srp_update_locked(&if_map_gc, &if_idxmap.map, nif_map);
> if_map = nif_map;
> map = nmap;
> @@ -355,15 +371,39 @@ if_idxmap_insert(struct ifnet *ifp)
>
> /* pick the next free index */
> for (i = 0; i < USHRT_MAX; i++) {
> - if (index != 0 && srp_get_locked(&map[index]) == NULL)
> + if (index != 0 && isclr(if_idxmap.usedidx, index))
> break;
>
> index = if_idxmap.serial++ & USHRT_MAX;
> }
> + KASSERT(index != 0 && index < if_map->limit);
> + KASSERT(isclr(if_idxmap.usedidx, index));
>
> - /* commit */
> + setbit(if_idxmap.usedidx, index);
> ifp->if_index = index;
> +
> + rw_exit_write(&if_idxmap.lock);
> +}
> +
> +void
> +if_idxmap_insert(struct ifnet *ifp)
> +{
> + struct if_map *if_map;
> + struct srp *map;
> + unsigned int index = ifp->if_index;
> +
> + rw_enter_write(&if_idxmap.lock);
> +
> + if_map = srp_get_locked(&if_idxmap.map);
> + map = (struct srp *)(if_map + 1);
> +
> + KASSERT(index != 0 && index < if_map->limit);
> + KASSERT(isset(if_idxmap.usedidx, index));
> +
> + /* commit */
> srp_update_locked(&if_ifp_gc, &map[index], if_ref(ifp));
> +
> + rw_exit_write(&if_idxmap.lock);
> }
>
> void
> @@ -375,8 +415,7 @@ if_idxmap_remove(struct ifnet *ifp)
>
> index = ifp->if_index;
>
> - /* the kernel lock guarantees serialised modifications to if_idxmap */
> - KERNEL_ASSERT_LOCKED();
> + rw_enter_write(&if_idxmap.lock);
>
> if_map = srp_get_locked(&if_idxmap.map);
> KASSERT(index < if_map->limit);
> @@ -386,7 +425,12 @@ if_idxmap_remove(struct ifnet *ifp)
>
> srp_update_locked(&if_ifp_gc, &map[index], NULL);
> if_idxmap.count--;
> +
> + KASSERT(isset(if_idxmap.usedidx, index));
> + clrbit(if_idxmap.usedidx, index);
> /* end of if_idxmap modifications */
> +
> + rw_exit_write(&if_idxmap.lock);
> }
>
> void
> @@ -606,6 +650,8 @@ if_attach_common(struct ifnet *ifp)
> KASSERTMSG(ifp->if_qstart != NULL,
> "%s: if_qstart not set with MPSAFE set", ifp->if_xname);
> }
> +
> + if_idxmap_alloc(ifp);
>
> ifq_init(&ifp->if_snd, ifp, 0);
>