+ force = (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT));
+
+ /* There shouldn't be any open or lock state at this point */
+ lck_mtx_lock(&np->n_openlock);
+
+ if (nmp && (nmp->nm_vers >= NFS_VER4)) {
+ /* need to drop a delegation */
+ if (np->n_dreturn.tqe_next != NFSNOLIST) {
+ /* remove this node from the delegation return list */
+ lck_mtx_lock(&nmp->nm_lock);
+ if (np->n_dreturn.tqe_next != NFSNOLIST) {
+ TAILQ_REMOVE(&nmp->nm_dreturnq, np, n_dreturn);
+ np->n_dreturn.tqe_next = NFSNOLIST;
+ }
+ lck_mtx_unlock(&nmp->nm_lock);
+ }
+ if (np->n_dlink.tqe_next != NFSNOLIST) {
+ /* remove this node from the delegation list */
+ lck_mtx_lock(&nmp->nm_lock);
+ if (np->n_dlink.tqe_next != NFSNOLIST) {
+ TAILQ_REMOVE(&nmp->nm_delegations, np, n_dlink);
+ np->n_dlink.tqe_next = NFSNOLIST;
+ }
+ lck_mtx_unlock(&nmp->nm_lock);
+ }
+ if ((np->n_openflags & N_DELEG_MASK) && !force) {
+ /* try to return the delegation */
+ np->n_openflags &= ~N_DELEG_MASK;
+ nfs4_delegreturn_rpc(nmp, np->n_fhp, np->n_fhsize, &np->n_dstateid,
+ R_RECOVER, vfs_context_thread(ctx), vfs_context_ucred(ctx));
+ }
+ if (np->n_attrdirfh) {
+ FREE(np->n_attrdirfh, M_TEMP);
+ np->n_attrdirfh = NULL;
+ }
+ }
+
+ /* clean up file locks */
+ TAILQ_FOREACH_SAFE(nflp, &np->n_locks, nfl_link, nextnflp) {
+ if (!(nflp->nfl_flags & NFS_FILE_LOCK_DEAD) && !force) {
+ NP(np, "nfs_vnop_reclaim: lock 0x%llx 0x%llx 0x%x (bc %d)",
+ nflp->nfl_start, nflp->nfl_end, nflp->nfl_flags, nflp->nfl_blockcnt);
+ }
+ if (!(nflp->nfl_flags & (NFS_FILE_LOCK_BLOCKED|NFS_FILE_LOCK_DEAD))) {
+ /* try sending an unlock RPC if it wasn't delegated */
+ if (!(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED) && !force)
+ nmp->nm_funcs->nf_unlock_rpc(np, nflp->nfl_owner, F_WRLCK, nflp->nfl_start, nflp->nfl_end, R_RECOVER,
+ NULL, nflp->nfl_owner->nlo_open_owner->noo_cred);
+ lck_mtx_lock(&nflp->nfl_owner->nlo_lock);
+ TAILQ_REMOVE(&nflp->nfl_owner->nlo_locks, nflp, nfl_lolink);
+ lck_mtx_unlock(&nflp->nfl_owner->nlo_lock);
+ }
+ TAILQ_REMOVE(&np->n_locks, nflp, nfl_link);
+ nfs_file_lock_destroy(nflp);
+ }
+ /* clean up lock owners */
+ TAILQ_FOREACH_SAFE(nlop, &np->n_lock_owners, nlo_link, nextnlop) {
+ if (!TAILQ_EMPTY(&nlop->nlo_locks) && !force)
+ NP(np, "nfs_vnop_reclaim: lock owner with locks");
+ TAILQ_REMOVE(&np->n_lock_owners, nlop, nlo_link);
+ nfs_lock_owner_destroy(nlop);
+ }
+ /* clean up open state */
+ if (np->n_openrefcnt && !force)
+ NP(np, "nfs_vnop_reclaim: still open: %d", np->n_openrefcnt);
+ TAILQ_FOREACH_SAFE(nofp, &np->n_opens, nof_link, nextnofp) {
+ if (nofp->nof_flags & NFS_OPEN_FILE_BUSY)
+ NP(np, "nfs_vnop_reclaim: open file busy");
+ if (!(np->n_flag & NREVOKE) && !(nofp->nof_flags & NFS_OPEN_FILE_LOST)) {
+ if (nofp->nof_opencnt && !force)
+ NP(np, "nfs_vnop_reclaim: file still open: %d", nofp->nof_opencnt);
+ if (!force && (nofp->nof_access || nofp->nof_deny ||
+ nofp->nof_mmap_access || nofp->nof_mmap_deny ||
+ nofp->nof_r || nofp->nof_w || nofp->nof_rw ||
+ nofp->nof_r_dw || nofp->nof_w_dw || nofp->nof_rw_dw ||
+ nofp->nof_r_drw || nofp->nof_w_drw || nofp->nof_rw_drw ||
+ nofp->nof_d_r || nofp->nof_d_w || nofp->nof_d_rw ||
+ nofp->nof_d_r_dw || nofp->nof_d_w_dw || nofp->nof_d_rw_dw ||
+ nofp->nof_d_r_drw || nofp->nof_d_w_drw || nofp->nof_d_rw_drw)) {
+ NP(np, "nfs_vnop_reclaim: non-zero access: %d %d %d %d # %u.%u %u.%u %u.%u dw %u.%u %u.%u %u.%u drw %u.%u %u.%u %u.%u",
+ nofp->nof_access, nofp->nof_deny,
+ nofp->nof_mmap_access, nofp->nof_mmap_deny,
+ nofp->nof_r, nofp->nof_d_r,
+ nofp->nof_w, nofp->nof_d_w,
+ nofp->nof_rw, nofp->nof_d_rw,
+ nofp->nof_r_dw, nofp->nof_d_r_dw,
+ nofp->nof_w_dw, nofp->nof_d_w_dw,
+ nofp->nof_rw_dw, nofp->nof_d_rw_dw,
+ nofp->nof_r_drw, nofp->nof_d_r_drw,
+ nofp->nof_w_drw, nofp->nof_d_w_drw,
+ nofp->nof_rw_drw, nofp->nof_d_rw_drw);
+ /* try sending a close RPC if it wasn't delegated */
+ if (nofp->nof_r || nofp->nof_w || nofp->nof_rw ||
+ nofp->nof_r_dw || nofp->nof_w_dw || nofp->nof_rw_dw ||
+ nofp->nof_r_drw || nofp->nof_w_drw || nofp->nof_rw_drw)
+ nfs4_close_rpc(np, nofp, NULL, nofp->nof_owner->noo_cred, R_RECOVER);
+ }
+ }
+ TAILQ_REMOVE(&np->n_opens, nofp, nof_link);
+ nfs_open_file_destroy(nofp);
+ }
+ lck_mtx_unlock(&np->n_openlock);
+
+ if (np->n_monlink.le_next != NFSNOLIST) {
+ /* Wait for any in-progress getattr to complete, */
+ /* then remove this node from the monitored node list. */
+ lck_mtx_lock(&nmp->nm_lock);
+ while (np->n_mflag & NMMONSCANINPROG) {
+ struct timespec ts = { 1, 0 };
+ np->n_mflag |= NMMONSCANWANT;
+ msleep(&np->n_mflag, &nmp->nm_lock, PZERO-1, "nfswaitmonscan", &ts);
+ }
+ if (np->n_monlink.le_next != NFSNOLIST) {
+ LIST_REMOVE(np, n_monlink);
+ np->n_monlink.le_next = NFSNOLIST;
+ }
+ lck_mtx_unlock(&nmp->nm_lock);
+ }
+
+ lck_mtx_lock(nfs_buf_mutex);
+ if (!force && (!LIST_EMPTY(&np->n_dirtyblkhd) || !LIST_EMPTY(&np->n_cleanblkhd)))
+ NP(np, "nfs_reclaim: dropping %s buffers", (!LIST_EMPTY(&np->n_dirtyblkhd) ? "dirty" : "clean"));
+ lck_mtx_unlock(nfs_buf_mutex);
+ nfs_vinvalbuf(vp, V_IGNORE_WRITEERR, ap->a_context, 0);