Most frequently used vops boil down to checking SDT probes, doing the call and checking again. There is no vop_post/pre in their case. The check after the call prevents tail call optimisation from taking place. Instead, check once upfront. Kernels with debug or vops with non-empty vop_post still don't short circuit.
Generated code:
int VOP_LOCK1_APV(struct vop_vector *vop, struct vop_lock1_args *a) { int rc; VNASSERT(a->a_gen.a_desc == &vop_lock1_desc, a->a_vp, ("Wrong a_desc in vop_lock1(%p, %p)", a->a_vp, a)); VNASSERT(vop != NULL, a->a_vp, ("No vop_lock1(%p, %p)", a->a_vp, a)); KTR_START4(KTR_VOP, "VOP", "VOP_LOCK1", (uintptr_t)a, "vp:0x%jX", (uintptr_t)a->a_vp, "flags:0x%jX", a->a_flags, "file:0x%jX", a->a_file, "line:0x%jX", a->a_line); vop_lock_pre(a); if (__predict_true(!SDT_PROBES_ENABLED() && vop->vop_lock1 != NULL)) { rc = vop->vop_lock1(a); } else { SDT_PROBE2(vfs, vop, vop_lock1, entry, a->a_vp, a); if (vop->vop_lock1 != NULL) rc = vop->vop_lock1(a); else rc = vop->vop_bypass(&a->a_gen); SDT_PROBE3(vfs, vop, vop_lock1, return, a->a_vp, a, rc); } if (rc == 0) { } else { } vop_lock_post(a, rc); KTR_STOP4(KTR_VOP, "VOP", "VOP_LOCK1", (uintptr_t)a, "vp:0x%jX", (uintptr_t)a->a_vp, "flags:0x%jX", a->a_flags, "file:0x%jX", a->a_file, "line:0x%jX", a->a_line); return (rc); }
I verified that at least on amd64 tail call is indeed used.