1 /*- 2 * Copyright (c) 1997, 1998 3 * Nan Yang Computer Services Limited. All rights reserved. 4 * 5 * Parts copyright (c) 1997, 1998 Cybernet Corporation, NetMAX project. 6 * 7 * Written by Greg Lehey 8 * 9 * This software is distributed under the so-called ``Berkeley 10 * License'': 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by Nan Yang Computer 23 * Services Limited. 24 * 4. Neither the name of the Company nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * This software is provided ``as is'', and any express or implied 29 * warranties, including, but not limited to, the implied warranties of 30 * merchantability and fitness for a particular purpose are disclaimed. 31 * In no event shall the company or contributors be liable for any 32 * direct, indirect, incidental, special, exemplary, or consequential 33 * damages (including, but not limited to, procurement of substitute 34 * goods or services; loss of use, data, or profits; or business 35 * interruption) however caused and on any theory of liability, whether 36 * in contract, strict liability, or tort (including negligence or 37 * otherwise) arising in any way out of the use of this software, even if 38 * advised of the possibility of such damage. 39 * 40 * $Id: vinumlock.c,v 1.13 2000/05/02 23:25:02 grog Exp grog $ 41 * $FreeBSD: src/sys/dev/vinum/vinumlock.c,v 1.18.2.3 2001/04/04 06:27:11 grog Exp $ 42 * $DragonFly: src/sys/dev/raid/vinum/vinumlock.c,v 1.4 2003/08/07 21:17:09 dillon Exp $ 43 */ 44 45 #include "vinumhdr.h" 46 #include "request.h" 47 48 /* Lock a drive, wait if it's in use */ 49 #if VINUMDEBUG 50 int 51 lockdrive(struct drive *drive, char *file, int line) 52 #else 53 int 54 lockdrive(struct drive *drive) 55 #endif 56 { 57 int error; 58 59 /* XXX get rid of drive->flags |= VF_LOCKING; */ 60 if ((drive->flags & VF_LOCKED) /* it's locked */ 61 &&(drive->pid == curproc->p_pid)) { /* by us! */ 62 #ifdef VINUMDEBUG 63 log(LOG_WARNING, 64 "vinum lockdrive: already locking %s from %s:%d, called from %s:%d\n", 65 drive->label.name, 66 drive->lockfilename, 67 drive->lockline, 68 basename(file), 69 line); 70 #else 71 log(LOG_WARNING, 72 "vinum lockdrive: already locking %s\n", 73 drive->label.name); 74 #endif 75 return 0; 76 } 77 while ((drive->flags & VF_LOCKED) != 0) { 78 /* 79 * There are problems sleeping on a unique identifier, 80 * since the drive structure can move, and the unlock 81 * function can be called after killing the drive. 82 * Solve this by waiting on this function; the number 83 * of conflicts is negligible. 84 */ 85 if ((error = tsleep(&lockdrive, 0, "vindrv", 0)) != 0) 86 return error; 87 } 88 drive->flags |= VF_LOCKED; 89 drive->pid = curproc->p_pid; /* it's a panic error if curproc is null */ 90 #ifdef VINUMDEBUG 91 bcopy(basename(file), drive->lockfilename, 15); 92 drive->lockfilename[15] = '\0'; /* truncate if necessary */ 93 drive->lockline = line; 94 #endif 95 return 0; 96 } 97 98 /* Unlock a drive and let the next one at it */ 99 void 100 unlockdrive(struct drive *drive) 101 { 102 drive->flags &= ~VF_LOCKED; 103 /* we don't reset pid: it's of hysterical interest */ 104 wakeup(&lockdrive); 105 } 106 107 /* Lock a stripe of a plex, wait if it's in use */ 108 struct rangelock * 109 lockrange(daddr_t stripe, struct buf *bp, struct plex *plex) 110 { 111 int s; 112 struct rangelock *lock; 113 struct rangelock *pos; /* position of first free lock */ 114 int foundlocks; /* number of locks found */ 115 116 /* 117 * We could get by without counting the number 118 * of locks we find, but we have a linear search 119 * through a table which in most cases will be 120 * empty. It's faster to stop when we've found 121 * all the locks that are there. This is also 122 * the reason why we put pos at the beginning 123 * instead of the end, though it requires an 124 * extra test. 125 */ 126 pos = NULL; 127 foundlocks = 0; 128 129 /* 130 * we can't use 0 as a valid address, so 131 * increment all addresses by 1. 132 */ 133 stripe++; 134 /* 135 * We give the locks back from an interrupt 136 * context, so we need to raise the spl here. 137 */ 138 s = splbio(); 139 140 /* Wait here if the table is full */ 141 while (plex->usedlocks == PLEX_LOCKS) /* all in use */ 142 tsleep(&plex->usedlocks, 0, "vlock", 0); 143 144 #ifdef DIAGNOSTIC 145 if (plex->usedlocks >= PLEX_LOCKS) 146 panic("lockrange: Too many locks in use"); 147 #endif 148 149 lock = plex->lock; /* pointer in lock table */ 150 if (plex->usedlocks > 0) /* something locked, */ 151 /* Search the lock table for our stripe */ 152 for (; lock < &plex->lock[PLEX_LOCKS] 153 && foundlocks < plex->usedlocks; 154 lock++) { 155 if (lock->stripe) { /* in use */ 156 foundlocks++; /* found another one in use */ 157 if ((lock->stripe == stripe) /* it's our stripe */ 158 &&(lock->bp != bp)) { /* but not our request */ 159 #ifdef VINUMDEBUG 160 if (debug & DEBUG_LASTREQS) { 161 struct rangelock info; 162 163 info.stripe = stripe; 164 info.bp = bp; 165 logrq(loginfo_lockwait, (union rqinfou) &info, bp); 166 } 167 #endif 168 plex->lockwaits++; /* waited one more time */ 169 tsleep(lock, 0, "vrlock", 0); 170 lock = &plex->lock[-1]; /* start again */ 171 foundlocks = 0; 172 pos = NULL; 173 } 174 } else if (pos == NULL) /* still looking for somewhere? */ 175 pos = lock; /* a place to put this one */ 176 } 177 /* 178 * This untidy looking code ensures that we'll 179 * always end up pointing to the first free lock 180 * entry, thus minimizing the number of 181 * iterations necessary. 182 */ 183 if (pos == NULL) /* didn't find one on the way, */ 184 pos = lock; /* use the one we're pointing to */ 185 186 /* 187 * The address range is free, and we're pointing 188 * to the first unused entry. Make it ours. 189 */ 190 pos->stripe = stripe; 191 pos->bp = bp; 192 plex->usedlocks++; /* one more lock */ 193 splx(s); 194 #ifdef VINUMDEBUG 195 if (debug & DEBUG_LASTREQS) 196 logrq(loginfo_lock, (union rqinfou) pos, bp); 197 #endif 198 return pos; 199 } 200 201 /* Unlock a volume and let the next one at it */ 202 void 203 unlockrange(int plexno, struct rangelock *lock) 204 { 205 struct plex *plex; 206 207 plex = &PLEX[plexno]; 208 #ifdef DIAGNOSTIC 209 if (lock < &plex->lock[0] || lock >= &plex->lock[PLEX_LOCKS]) 210 panic("vinum: rangelock %p on plex %d invalid, not between %p and %p", 211 lock, 212 plexno, 213 &plex->lock[0], 214 &plex->lock[PLEX_LOCKS]); 215 #endif 216 #ifdef VINUMDEBUG 217 if (debug & DEBUG_LASTREQS) 218 logrq(loginfo_unlock, (union rqinfou) lock, lock->bp); 219 #endif 220 lock->stripe = 0; /* no longer used */ 221 plex->usedlocks--; /* one less lock */ 222 if (plex->usedlocks == PLEX_LOCKS - 1) /* we were full, */ 223 wakeup(&plex->usedlocks); /* get a waiter if one's there */ 224 wakeup((void *) lock); 225 } 226 227 /* Get a lock for the global config, wait if it's not available */ 228 int 229 lock_config(void) 230 { 231 int error; 232 233 while ((vinum_conf.flags & VF_LOCKED) != 0) { 234 vinum_conf.flags |= VF_LOCKING; 235 if ((error = tsleep(&vinum_conf, 0, "vincfg", 0)) != 0) 236 return error; 237 } 238 vinum_conf.flags |= VF_LOCKED; 239 return 0; 240 } 241 242 /* Unlock and wake up any waiters */ 243 void 244 unlock_config(void) 245 { 246 vinum_conf.flags &= ~VF_LOCKED; 247 if ((vinum_conf.flags & VF_LOCKING) != 0) { 248 vinum_conf.flags &= ~VF_LOCKING; 249 wakeup(&vinum_conf); 250 } 251 } 252 /* Local Variables: */ 253 /* fill-column: 50 */ 254 /* End: */ 255