diff options
-rw-r--r-- | sys/kern/vfs_init.c | 103 |
1 files changed, 82 insertions, 21 deletions
diff --git a/sys/kern/vfs_init.c b/sys/kern/vfs_init.c index 7446258..6c86c2a 100644 --- a/sys/kern/vfs_init.c +++ b/sys/kern/vfs_init.c @@ -90,18 +90,34 @@ static int *vfs_op_desc_refs; /* Number of descriptions */ static int num_op_descs; /* Number of entries in each description */ -static int vfs_opv_numops; +static int vfs_opv_numops = 64; + +/* Allow this number to be tuned at boot */ +TUNABLE_INT("vfs.opv_numops", &vfs_opv_numops); +SYSCTL_INT(_vfs, OID_AUTO, opv_numops, CTLFLAG_RD, &vfs_opv_numops, + 0, "Maximum number of operations in vop_t vector"); + +static int int_cmp(const void *a, const void *b); + +static int +int_cmp(const void *a, const void *b) +{ + return(*(const int *)a - *(const int *)b); +} /* * Recalculate the operations vector/description (those parts of it that can * be recalculated, that is.) - * XXX It may be preferable to replace this function with an invariant check - * and a set of functions that should keep the table invariant. + * Always allocate operations vector large enough to hold vfs_opv_numops + * entries. The vector is never freed or deallocated once it is initialized, + * so that vnodes might safely reference it through their v_op pointer without + * vector changing suddenly from under them. */ static void vfs_opv_recalc(void) { - int i, j; + int i, j, k; + int *vfs_op_offsets; vop_t ***opv_desc_vector_p; vop_t **opv_desc_vector; struct vnodeopv_entry_desc *opve_descp; @@ -111,31 +127,66 @@ vfs_opv_recalc(void) panic("vfs_opv_recalc called with null vfs_op_descs"); /* - * Run through and make sure all known descs have an offset - * + * Allocate and initialize temporary array to store + * offsets. Sort it to put all uninitialized entries + * first and to make holes in existing offset sequence + * detectable. + */ + MALLOC(vfs_op_offsets, int *, + num_op_descs * sizeof(int), M_TEMP, M_WAITOK); + if (vfs_op_offsets == NULL) + panic("vfs_opv_recalc: no memory"); + for (i = 0; i < num_op_descs; i++) + vfs_op_offsets[i] = vfs_op_descs[i]->vdesc_offset; + qsort(vfs_op_offsets, num_op_descs, sizeof(int), int_cmp); + + /* + * Run through and make sure all known descs have an offset. + * Use vfs_op_offsets to locate holes in offset sequence and + * reuse them. * vop_default_desc is hardwired at offset 1, and offset 0 * is a panic sanity check. */ - vfs_opv_numops = 0; - for (i = 0; i < num_op_descs; i++) - if (vfs_opv_numops < (vfs_op_descs[i]->vdesc_offset + 1)) - vfs_opv_numops = vfs_op_descs[i]->vdesc_offset + 1; - for (i = 0; i < num_op_descs; i++) - if (vfs_op_descs[i]->vdesc_offset == 0) - vfs_op_descs[i]->vdesc_offset = vfs_opv_numops++; + j = 1; k = 1; + for (i = 0; i < num_op_descs; i++) { + if (vfs_op_descs[i]->vdesc_offset != 0) + continue; + /* + * Look at two adjacent entries vfs_op_offsets[j - 1] and + * vfs_op_offsets[j] and see if we can fit a new offset + * number in between. If not, look at the next pair until + * hole is found or the end of the vfs_op_offsets vector is + * reached. j has been initialized to 1 above so that + * referencing (j-1)-th element is safe and the loop will + * never execute if num_op_descs is 1. For each new value s + * of i the j loop pick up from where previous iteration has + * left off. When the last hole has been consumed or if no + * hole has been found, we will start allocating new numbers + * starting from the biggest already available offset + 1. + */ + for (; j < num_op_descs; j++) { + if (vfs_op_offsets[j - 1] < k && vfs_op_offsets[j] > k) + break; + k = vfs_op_offsets[j] + 1; + } + vfs_op_descs[i]->vdesc_offset = k++; + } + FREE(vfs_op_offsets, M_TEMP); + + /* Panic if new vops will cause vector overflow */ + if (k > vfs_opv_numops) + panic("VFS: Ran out of vop_t vector entries. %d entries required, only %d available.\n", k, vfs_opv_numops); + /* * Allocate and fill in the vectors */ for (i = 0; i < vnodeopv_num; i++) { opv = vnodeopv_descs[i]; opv_desc_vector_p = opv->opv_desc_vector_p; -#ifdef WANT_BAD_JUJU - if (*opv_desc_vector_p) - FREE(*opv_desc_vector_p, M_VNODE); -#endif - MALLOC(*opv_desc_vector_p, vop_t **, - vfs_opv_numops * sizeof(vop_t *), M_VNODE, - M_WAITOK | M_ZERO); + if (*opv_desc_vector_p == NULL) + MALLOC(*opv_desc_vector_p, vop_t **, + vfs_opv_numops * sizeof(vop_t *), M_VNODE, + M_WAITOK | M_ZERO); if (*opv_desc_vector_p == NULL) panic("no memory for vop_t ** vector"); @@ -240,7 +291,6 @@ vfs_rm_vnodeops(const void *data) opv = (const struct vnodeopv_desc *)data; /* Lower ref counts on descs in the table and release if zero */ - opv_desc_vector = *(opv->opv_desc_vector_p); for (i = 0; (desc = opv->opv_desc_ops[i].opve_op); i++) { for (j = 0; j < num_op_descs; j++) { if (desc == vfs_op_descs[j]) { @@ -254,6 +304,14 @@ vfs_rm_vnodeops(const void *data) continue; if (vfs_op_desc_refs[j] < 0) panic("vfs_remove_vnodeops: negative refcnt"); + /* Entry is going away - replace it with defaultop */ + for (k = 0; k < vnodeopv_num; k++) { + opv_desc_vector = + *(vnodeopv_descs[k]->opv_desc_vector_p); + if (opv_desc_vector != NULL) + opv_desc_vector[desc->vdesc_offset] = + opv_desc_vector[1]; + } MALLOC(newop, struct vnodeop_desc **, (num_op_descs - 1) * sizeof(*newop), M_VNODE, M_WAITOK); @@ -290,6 +348,9 @@ vfs_rm_vnodeops(const void *data) } if (i == vnodeopv_num) panic("vfs_remove_vnodeops: opv not found"); + opv_desc_vector = *(opv->opv_desc_vector_p); + if (opv_desc_vector != NULL) + FREE(opv_desc_vector, M_VNODE); MALLOC(newopv, const struct vnodeopv_desc **, (vnodeopv_num - 1) * sizeof(*newopv), M_VNODE, M_WAITOK); if (newopv == NULL) |