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.5 2005/06/11 00:05:46 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 struct rangelock *lock; 112 struct rangelock *pos; /* position of first free lock */ 113 int foundlocks; /* number of locks found */ 114 115 /* 116 * We could get by without counting the number 117 * of locks we find, but we have a linear search 118 * through a table which in most cases will be 119 * empty. It's faster to stop when we've found 120 * all the locks that are there. This is also 121 * the reason why we put pos at the beginning 122 * instead of the end, though it requires an 123 * extra test. 124 */ 125 pos = NULL; 126 foundlocks = 0; 127 128 /* 129 * we can't use 0 as a valid address, so 130 * increment all addresses by 1. 131 */ 132 stripe++; 133 /* 134 * We give the locks back from an interrupt 135 * context, so we need to raise the spl here. 136 */ 137 crit_enter(); 138 139 /* Wait here if the table is full */ 140 while (plex->usedlocks == PLEX_LOCKS) /* all in use */ 141 tsleep(&plex->usedlocks, 0, "vlock", 0); 142 143 #ifdef DIAGNOSTIC 144 if (plex->usedlocks >= PLEX_LOCKS) 145 panic("lockrange: Too many locks in use"); 146 #endif 147 148 lock = plex->lock; /* pointer in lock table */ 149 if (plex->usedlocks > 0) /* something locked, */ 150 /* Search the lock table for our stripe */ 151 for (; lock < &plex->lock[PLEX_LOCKS] 152 && foundlocks < plex->usedlocks; 153 lock++) { 154 if (lock->stripe) { /* in use */ 155 foundlocks++; /* found another one in use */ 156 if ((lock->stripe == stripe) /* it's our stripe */ 157 &&(lock->bp != bp)) { /* but not our request */ 158 #ifdef VINUMDEBUG 159 if (debug & DEBUG_LASTREQS) { 160 struct rangelock info; 161 162 info.stripe = stripe; 163 info.bp = bp; 164 logrq(loginfo_lockwait, (union rqinfou) &info, bp); 165 } 166 #endif 167 plex->lockwaits++; /* waited one more time */ 168 tsleep(lock, 0, "vrlock", 0); 169 lock = &plex->lock[-1]; /* start again */ 170 foundlocks = 0; 171 pos = NULL; 172 } 173 } else if (pos == NULL) /* still looking for somewhere? */ 174 pos = lock; /* a place to put this one */ 175 } 176 /* 177 * This untidy looking code ensures that we'll 178 * always end up pointing to the first free lock 179 * entry, thus minimizing the number of 180 * iterations necessary. 181 */ 182 if (pos == NULL) /* didn't find one on the way, */ 183 pos = lock; /* use the one we're pointing to */ 184 185 /* 186 * The address range is free, and we're pointing 187 * to the first unused entry. Make it ours. 188 */ 189 pos->stripe = stripe; 190 pos->bp = bp; 191 plex->usedlocks++; /* one more lock */ 192 crit_exit(); 193 #ifdef VINUMDEBUG 194 if (debug & DEBUG_LASTREQS) 195 logrq(loginfo_lock, (union rqinfou) pos, bp); 196 #endif 197 return pos; 198 } 199 200 /* Unlock a volume and let the next one at it */ 201 void 202 unlockrange(int plexno, struct rangelock *lock) 203 { 204 struct plex *plex; 205 206 plex = &PLEX[plexno]; 207 #ifdef DIAGNOSTIC 208 if (lock < &plex->lock[0] || lock >= &plex->lock[PLEX_LOCKS]) 209 panic("vinum: rangelock %p on plex %d invalid, not between %p and %p", 210 lock, 211 plexno, 212 &plex->lock[0], 213 &plex->lock[PLEX_LOCKS]); 214 #endif 215 #ifdef VINUMDEBUG 216 if (debug & DEBUG_LASTREQS) 217 logrq(loginfo_unlock, (union rqinfou) lock, lock->bp); 218 #endif 219 lock->stripe = 0; /* no longer used */ 220 plex->usedlocks--; /* one less lock */ 221 if (plex->usedlocks == PLEX_LOCKS - 1) /* we were full, */ 222 wakeup(&plex->usedlocks); /* get a waiter if one's there */ 223 wakeup((void *) lock); 224 } 225 226 /* Get a lock for the global config, wait if it's not available */ 227 int 228 lock_config(void) 229 { 230 int error; 231 232 while ((vinum_conf.flags & VF_LOCKED) != 0) { 233 vinum_conf.flags |= VF_LOCKING; 234 if ((error = tsleep(&vinum_conf, 0, "vincfg", 0)) != 0) 235 return error; 236 } 237 vinum_conf.flags |= VF_LOCKED; 238 return 0; 239 } 240 241 /* Unlock and wake up any waiters */ 242 void 243 unlock_config(void) 244 { 245 vinum_conf.flags &= ~VF_LOCKED; 246 if ((vinum_conf.flags & VF_LOCKING) != 0) { 247 vinum_conf.flags &= ~VF_LOCKING; 248 wakeup(&vinum_conf); 249 } 250 } 251 /* Local Variables: */ 252 /* fill-column: 50 */ 253 /* End: */ 254