Index: if.c =================================================================== RCS file: /cvs/src/sys/net/if.c,v retrieving revision 1.369 diff -u -p -r1.369 if.c --- if.c 10 Sep 2015 18:11:05 -0000 1.369 +++ if.c 10 Sep 2015 19:06:18 -0000 @@ -171,18 +171,21 @@ void ifa_print_all(void); * if_get(0) returns NULL. */ +void if_ifp_dtor(void *, void *); +void if_map_dtor(void *, void *); + struct if_map { unsigned long limit; - /* followed by limit ifnet * pointers */ + /* followed by limit ifnet srp pointers */ }; struct if_idxmap { unsigned int serial; unsigned int count; - struct if_map *map; + struct srp map; }; -void if_idxmap_init(void); +void if_idxmap_init(unsigned int); void if_idxmap_insert(struct ifnet *); void if_idxmap_remove(struct ifnet *); @@ -206,7 +209,7 @@ struct taskq *softnettq; void ifinit() { - if_idxmap_init(); + if_idxmap_init(8); timeout_set(&net_tick_to, net_tick, &net_tick_to); @@ -221,27 +224,42 @@ ifinit() static struct if_idxmap if_idxmap = { 0, 0, - NULL + SRP_INITIALIZER() }; +struct srp_gc if_ifp_gc = SRP_GC_INITIALIZER(if_ifp_dtor, NULL); +struct srp_gc if_map_gc = SRP_GC_INITIALIZER(if_map_dtor, NULL); + struct ifnet_head ifnet = TAILQ_HEAD_INITIALIZER(ifnet); struct ifnet_head iftxlist = TAILQ_HEAD_INITIALIZER(iftxlist); struct ifnet *lo0ifp; void -if_idxmap_init(void) +if_idxmap_init(unsigned int limit) { + struct if_map *if_map; + struct srp *map; + unsigned int i; + if_idxmap.serial = 1; /* skip ifidx 0 so it can return NULL */ - if_idxmap.map = malloc(sizeof(*if_idxmap.map) + - 8 * sizeof(struct ifnet *), M_IFADDR, M_WAITOK | M_ZERO); - if_idxmap.map->limit = 8; + + if_map = malloc(sizeof(*if_map) + limit * sizeof(*map), + M_IFADDR, M_WAITOK); + + if_map->limit = limit; + map = (struct srp *)(if_map + 1); + for (i = 0; i < limit; i++) + srp_init(&map[i]); + + /* 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) { struct if_map *if_map; - struct ifnet **map; + struct srp *map; unsigned int index, i; /* @@ -256,38 +274,43 @@ if_idxmap_insert(struct ifnet *ifp) if (++if_idxmap.count > USHRT_MAX) panic("too many interfaces"); - if_map = if_idxmap.map; - map = (struct ifnet **)(if_map + 1); + if_map = srp_get_locked(&if_idxmap.map); + map = (struct srp *)(if_map + 1); index = if_idxmap.serial++ & USHRT_MAX; if (index >= if_map->limit) { struct if_map *nif_map; - struct ifnet **nmap; + struct srp *nmap; unsigned int nlimit; + struct ifnet *nifp; nlimit = if_map->limit * 2; nif_map = malloc(sizeof(*nif_map) + nlimit * sizeof(*nmap), M_IFADDR, M_WAITOK); - nmap = (struct ifnet **)(nif_map + 1); + nmap = (struct srp *)(nif_map + 1); nif_map->limit = nlimit; - for (i = 0; i < if_map->limit; i++) - nmap[i] = map[i]; + for (i = 0; i < if_map->limit; i++) { + srp_init(&nmap[i]); + nifp = srp_get_locked(&map[i]); + if (nifp != NULL) { + srp_update_locked(&if_ifp_gc, &nmap[i], + if_ref(nifp)); + } + } while (i < nlimit) - nmap[i] = NULL; + srp_init(&nmap[i]); - if_idxmap.map = nif_map; - free(if_map, M_IFADDR, sizeof(*nif_map) + - if_map->limit * sizeof(*map)); + srp_update_locked(&if_map_gc, &if_idxmap.map, nif_map); if_map = nif_map; map = nmap; } /* pick the next free index */ for (i = 0; i < USHRT_MAX; i++) { - if (index != 0 && map[index] == NULL) + if (index != 0 && srp_get_locked(&map[index]) == NULL) break; index = if_idxmap.serial++ & USHRT_MAX; @@ -295,27 +318,28 @@ if_idxmap_insert(struct ifnet *ifp) /* commit */ ifp->if_index = index; - map[index] = if_ref(ifp); + srp_update_locked(&if_ifp_gc, &map[index], if_ref(ifp)); } void if_idxmap_remove(struct ifnet *ifp) { struct if_map *if_map; - struct ifnet **map; + struct srp *map; unsigned int index, r; - index = ifp->if_index & USHRT_MAX; + index = ifp->if_index; /* the kernel lock guarantees serialised modifications to if_idxmap */ KERNEL_ASSERT_LOCKED(); - if_map = if_idxmap.map; - map = (struct ifnet **)(if_map + 1); - KASSERT(ifp == map[index]); + if_map = srp_get_locked(&if_idxmap.map); + KASSERT(index < if_map->limit); - map[index] = NULL; - if_put(ifp); + map = (struct srp *)(if_map + 1); + KASSERT(ifp == (struct ifnet *)srp_get_locked(&map[index])); + + srp_update_locked(&if_ifp_gc, &map[index], NULL); if_idxmap.count--; /* end of if_idxmap modifications */ @@ -325,6 +349,29 @@ if_idxmap_remove(struct ifnet *ifp) printf("%s: refcnt %u\n", ifp->if_xname, r); } +void +if_ifp_dtor(void *null, void *ifp) +{ + if_put(ifp); +} + +void +if_map_dtor(void *null, void *m) +{ + struct if_map *if_map = m; + struct srp *map = (struct srp *)(if_map + 1); + unsigned int i; + + /* + * dont need if_idx.lk to use update_locked since this is the last + * reference to this map. there's nothing to race against. + */ + for (i = 0; i < if_map->limit; i++) + srp_update_locked(&if_ifp_gc, &map[i], NULL); + + free(if_map, M_IFADDR, sizeof(*if_map) + if_map->limit * sizeof(*map)); +} + /* * Attach an interface to the * list of "active" interfaces. @@ -1402,17 +1449,22 @@ ifunit(const char *name) struct ifnet * if_get(unsigned int index) { - struct if_map *if_map = if_idxmap.map; - struct ifnet **map = (struct ifnet **)(if_map + 1); + struct if_map *if_map; + struct srp *map; struct ifnet *ifp = NULL; + if_map = srp_enter(&if_idxmap.map); if (index < if_map->limit) { - ifp = map[index]; + map = (struct srp *)(if_map + 1); + + ifp = srp_follow(&if_idxmap.map, if_map, &map[index]); if (ifp != NULL) { KASSERT(ifp->if_index == index); if_ref(ifp); } - } + srp_leave(&map[index], ifp); + } else + srp_leave(&if_idxmap.map, map); return (ifp); }