This change adds a rwlock, the ND lock, to synchonize access to the
per-vnet default router list. The intent is to use this lock to protect
the prefix list as well. This change also introduces a number of
subroutines used to manage the lifecycle of defrouter objects.
A rwlock is used because most accesses of the list are read-only.
Additionally, rwlock read locks are always recursive, which will be
important for locking the prefix list. This lock is only acquired in NDP
and interface configuration operations so I believe it will not have any
impact on critical performance paths.
In this change, the ifafdata and lle locks may be acquired with the ND
lock held. It is a bug to acquire a rtentry lock with the ND lock held.
The process of deleting a defrouter object becomes more complicated. We
generally don't want to call defrouter_del() with the ND lock held,
since it flushes routing table entries and acquires a number of locks
doing so. Moreover, nd6_rtrequest() must acquire the ND read lock, so we
cannot call defrouter_del() or defrouter_delreq() with the ND lock held.
As a result, the following pattern is used in a few places:
- unlink defrouters from the global list with the ND write lock held, and stash them in a local list
- release the ND lock and invoke defrouter_del() on the unlinked defrouters
A refcount field is also added to struct nd_defrouter. There are code
paths where a defrouter must be held without the ND lock held; prefixes
will also hold a reference to their advertising router.
defrouter_reset() becomes pretty ugly: to avoid calling
defrouter_delreq() with the ND lock held, we make a temporary array
containing pointers to existing defrouter list entries, and call
defrouter_delreq() on entries from the array. This function is only
called in ioctl context though; suggestions for better approaches
are more than welcome.