summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/kern/vfs_init.c103
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)
OpenPOWER on IntegriCloud