+ struct sysctl_oid *removed_oidp = NULL; /* OID removed from tree */
+ struct sysctl_oid *old_oidp = NULL; /* OID compatibility copy */
+ funnel_t *fnl = NULL; /* compiler doesn't notice CTLFLAG_LOCKED */
+
+ if (!(oidp->oid_kind & CTLFLAG_LOCKED))
+ fnl = spl_kernel_funnel();
+
+ /* Get the write lock to modify the geometry */
+ lck_rw_lock_exclusive(sysctl_geometry_lock);
+
+ if (!(oidp->oid_kind & CTLFLAG_OID2)) {
+ /*
+ * We're using a copy so we can get the new fields in an
+ * old structure, so we have to iterate to compare the
+ * partial structure; when we find a match, we remove it
+ * normally and free the memory.
+ */
+ SLIST_FOREACH(old_oidp, oidp->oid_parent, oid_link) {
+ if (!memcmp(&oidp->oid_number, &old_oidp->oid_number, (offsetof(struct sysctl_oid, oid_descr)-offsetof(struct sysctl_oid, oid_number)))) {
+ break;
+ }
+ }
+ if (old_oidp != NULL) {
+ SLIST_REMOVE(old_oidp->oid_parent, old_oidp, sysctl_oid, oid_link);
+ removed_oidp = old_oidp;
+ }
+ } else {
+ /* It's a later version; handle the versions we know about */
+ switch (oidp->oid_version) {
+ case SYSCTL_OID_VERSION:
+ /* We can just remove the OID directly... */
+ SLIST_REMOVE(oidp->oid_parent, oidp, sysctl_oid, oid_link);
+ removed_oidp = oidp;
+ break;
+ default:
+ /* XXX: Can't happen; probably tree coruption.*/
+ break; /* rejects unknown version */
+ }
+ }
+
+ /*
+ * We've removed it from the list at this point, but we don't want
+ * to return to the caller until all handler references have drained
+ * out. Doing things in this order prevent other people coming in
+ * and starting new operations against the OID node we want removed.
+ *
+ * Note: oidp could be NULL if it wasn't found.
+ */
+ while(removed_oidp && removed_oidp->oid_refcnt) {
+ lck_rw_sleep(sysctl_geometry_lock, LCK_SLEEP_EXCLUSIVE, &removed_oidp->oid_refcnt, THREAD_UNINT);
+ }
+
+ /* Release the write lock */
+ lck_rw_unlock_exclusive(sysctl_geometry_lock);
+
+ /* If it was allocated, free it after dropping the lock */
+ if (old_oidp != NULL) {
+ FREE(old_oidp, M_TEMP);
+ }