diff options
author | dillon <dillon@FreeBSD.org> | 2002-04-30 18:44:32 +0000 |
---|---|---|
committer | dillon <dillon@FreeBSD.org> | 2002-04-30 18:44:32 +0000 |
commit | 8468513da03404c4a6038d5c9c248a7aa1853ec9 (patch) | |
tree | bb7202670f43e2569f5aa51044d817a3126999e6 /sys/kern | |
parent | 1e3fa59089dcab40bc0b7515b68230f5770790f7 (diff) | |
download | FreeBSD-src-8468513da03404c4a6038d5c9c248a7aa1853ec9.zip FreeBSD-src-8468513da03404c4a6038d5c9c248a7aa1853ec9.tar.gz |
These are Alexander Kabaev's VFSops fixes (see the thread 'Found: module
loading breakage'). The patch fixes serious issues with the VFS
operations vector array which results in a crash when a filesystem module
adding a new VOP is loaded into the kernel. Basically what was happening
before was that the old operations vector was being freed and a new one
allocated. The original MALLOC code tended to reuse the same address
for the case and so the bug did not rear its ugly head until the new memory
subsystem was emplaced.
This patch replaces the temporary workaround Dave O'Brien comitted in 1.58.
The patch is clean enough that I intend to MFC it to stable at some point.
Submitted by: Alexander Kabaev <ak03@gte.com>
MFC after: 1 week
Diffstat (limited to 'sys/kern')
-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) |