DF-5662 / cam_extend.fixed.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | /* * Written by Julian Elischer (julian@tfs.com) * for TRW Financial Systems for use under the MACH(2.5) operating system. * * TRW Financial Systems, in accordance with their agreement with Carnegie * Mellon University, makes this software available to CMU to distribute * or use in any manner that they see fit as long as this message is kept * with the software. For this reason TFS also grants any other persons * or organisations permission to use or modify this software. * * TFS supplies this software to be publicly redistributed * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * * $FreeBSD: src/sys/cam/cam_extend.c,v 1.3 1999/08/28 00:40:39 peter Exp $ */ /* * XXX XXX XXX XXX We should get DEVFS working so that we * don't have to do this, possibly sparse, array based junk. * XXX: We can do this now with cdev_t, that's even better. */ /* * Extensible arrays: Use a realloc like implementation to permit * the arrays to be extend. */ #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/spinlock2.h> #include <sys/limits.h> #include "cam_extend.h" struct extend_array { int nelem; void **ps; struct spinlock ex_lock; }; /* EXTEND_CHUNK: Number of extend slots to allocate whenever we need a new * one. */ #ifndef EXTEND_CHUNK #define EXTEND_CHUNK 8 #endif struct extend_array * cam_extend_new(void) { struct extend_array *ea; ea = kmalloc(sizeof(struct extend_array), M_DEVBUF, M_INTWAIT | M_ZERO); spin_init(&ea->ex_lock, "cam_extend"); return (ea); } /* * Returns the stored value on success, NULL on failure (bad index, * allocation failure, or the slot already has storage). * * NOTE: The index is fully validated. The old code only checked the * upper bound, so any negative index indexed before the array * (an out-of-bounds kernel write) or faulted on a NULL ps. * * The array is also synchronized now: growth frees the old ps[] * chunk, which racing lockless cam_extend_get() readers could * still dereference (use-after-free read handed back as the * stored object pointer). */ void * cam_extend_set(struct extend_array *ea, int index, void *value) { void **space, **oldps; if (index < 0 || index > INT_MAX - EXTEND_CHUNK) return (NULL); if (index >= ea->nelem) { space = kmalloc(sizeof(void *) * (index + EXTEND_CHUNK), M_DEVBUF, M_INTWAIT | M_ZERO); if (space == NULL) return (NULL); /* * Re-check under the lock; the allocation was done * unlocked because M_INTWAIT may block. */ spin_lock(&ea->ex_lock); if (index >= ea->nelem) { /* Make sure we have something to copy before we * copy it */ if (ea->nelem) { bcopy(ea->ps, space, sizeof(void *) * ea->nelem); } oldps = ea->ps; ea->ps = space; ea->nelem = index + EXTEND_CHUNK; spin_unlock(&ea->ex_lock); if (oldps != NULL) kfree(oldps, M_DEVBUF); } else { spin_unlock(&ea->ex_lock); kfree(space, M_DEVBUF); } } spin_lock(&ea->ex_lock); if (ea->ps[index]) { spin_unlock(&ea->ex_lock); kprintf("extend_set: entry %d already has storage.\n", index); return (NULL); } ea->ps[index] = value; spin_unlock(&ea->ex_lock); return (value); } void * cam_extend_get(struct extend_array *ea, int index) { void *p; if (ea == NULL || index >= ea->nelem || index < 0) return NULL; spin_lock(&ea->ex_lock); p = ea->ps[index]; spin_unlock(&ea->ex_lock); return (p); } void cam_extend_release(struct extend_array *ea, int index) { if (ea == NULL || index >= ea->nelem || index < 0) return; spin_lock(&ea->ex_lock); if (ea->ps[index] != NULL) { ea->ps[index] = NULL; } spin_unlock(&ea->ex_lock); } |