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 */ 43 44 #include "vinumhdr.h" 45 #include "request.h" 46 47 /* Lock a drive, wait if it's in use */ 48 #if VINUMDEBUG 49 int 50 lockdrive(struct drive *drive, char *file, int line) 51 #else 52 int 53 lockdrive(struct drive *drive) 54 #endif 55 { 56 int error; 57 58 /* XXX get rid of drive->flags |= VF_LOCKING; */ 59 if ((drive->flags & VF_LOCKED) /* it's locked */ 60 &&(drive->pid == curproc->p_pid)) { /* by us! */ 61 #ifdef VINUMDEBUG 62 log(LOG_WARNING, 63 "vinum lockdrive: already locking %s from %s:%d, called from %s:%d\n", 64 drive->label.name, 65 drive->lockfilename, 66 drive->lockline, 67 basename(file), 68 line); 69 #else 70 log(LOG_WARNING, 71 "vinum lockdrive: already locking %s\n", 72 drive->label.name); 73 #endif 74 return 0; 75 } 76 while ((drive->flags & VF_LOCKED) != 0) { 77 /* 78 * There are problems sleeping on a unique identifier, 79 * since the drive structure can move, and the unlock 80 * function can be called after killing the drive. 81 * Solve this by waiting on this function; the number 82 * of conflicts is negligible. 83 */ 84 if ((error = tsleep(&lockdrive, 0, "vindrv", 0)) != 0) 85 return error; 86 } 87 drive->flags |= VF_LOCKED; 88 drive->pid = curproc->p_pid; /* it's a panic error if curproc is null */ 89 #ifdef VINUMDEBUG 90 bcopy(basename(file), drive->lockfilename, 15); 91 drive->lockfilename[15] = '\0'; /* truncate if necessary */ 92 drive->lockline = line; 93 #endif 94 return 0; 95 } 96 97 /* Unlock a drive and let the next one at it */ 98 void 99 unlockdrive(struct drive *drive) 100 { 101 drive->flags &= ~VF_LOCKED; 102 /* we don't reset pid: it's of hysterical interest */ 103 wakeup(&lockdrive); 104 } 105 106 /* Lock a stripe of a plex, wait if it's in use */ 107 struct rangelock * 108 lockrange(vinum_off_t stripe, struct buf *bp, struct plex *plex) 109 { 110 struct rangelock *lock; 111 struct rangelock *pos; /* position of first free lock */ 112 int foundlocks; /* number of locks found */ 113 114 /* 115 * We could get by without counting the number 116 * of locks we find, but we have a linear search 117 * through a table which in most cases will be 118 * empty. It's faster to stop when we've found 119 * all the locks that are there. This is also 120 * the reason why we put pos at the beginning 121 * instead of the end, though it requires an 122 * extra test. 123 */ 124 pos = NULL; 125 foundlocks = 0; 126 127 /* 128 * we can't use 0 as a valid address, so 129 * increment all addresses by 1. 130 */ 131 stripe++; 132 /* 133 * We give the locks back from an interrupt 134 * context, so we need to raise the spl here. 135 */ 136 crit_enter(); 137 138 /* Wait here if the table is full */ 139 while (plex->usedlocks == PLEX_LOCKS) /* all in use */ 140 tsleep(&plex->usedlocks, 0, "vlock", 0); 141 142 #ifdef DIAGNOSTIC 143 if (plex->usedlocks >= PLEX_LOCKS) 144 panic("lockrange: Too many locks in use"); 145 #endif 146 147 lock = plex->lock; /* pointer in lock table */ 148 if (plex->usedlocks > 0) /* something locked, */ 149 /* Search the lock table for our stripe */ 150 for (; lock < &plex->lock[PLEX_LOCKS] 151 && foundlocks < plex->usedlocks; 152 lock++) { 153 if (lock->stripe) { /* in use */ 154 foundlocks++; /* found another one in use */ 155 if ((lock->stripe == stripe) /* it's our stripe */ 156 &&(lock->bp != bp)) { /* but not our request */ 157 #ifdef VINUMDEBUG 158 if (debug & DEBUG_LASTREQS) { 159 struct rangelock info; 160 161 info.stripe = stripe; 162 info.bp = bp; 163 logrq(loginfo_lockwait, (union rqinfou) &info, &bp->b_bio1); 164 } 165 #endif 166 plex->lockwaits++; /* waited one more time */ 167 tsleep(lock, 0, "vrlock", 0); 168 lock = &plex->lock[-1]; /* start again */ 169 foundlocks = 0; 170 pos = NULL; 171 } 172 } else if (pos == NULL) /* still looking for somewhere? */ 173 pos = lock; /* a place to put this one */ 174 } 175 /* 176 * This untidy looking code ensures that we'll 177 * always end up pointing to the first free lock 178 * entry, thus minimizing the number of 179 * iterations necessary. 180 */ 181 if (pos == NULL) /* didn't find one on the way, */ 182 pos = lock; /* use the one we're pointing to */ 183 184 /* 185 * The address range is free, and we're pointing 186 * to the first unused entry. Make it ours. 187 */ 188 pos->stripe = stripe; 189 pos->bp = bp; 190 plex->usedlocks++; /* one more lock */ 191 crit_exit(); 192 #ifdef VINUMDEBUG 193 if (debug & DEBUG_LASTREQS) 194 logrq(loginfo_lock, (union rqinfou) pos, &bp->b_bio1); 195 #endif 196 return pos; 197 } 198 199 /* Unlock a volume and let the next one at it */ 200 void 201 unlockrange(int plexno, struct rangelock *lock) 202 { 203 struct plex *plex; 204 205 plex = &PLEX[plexno]; 206 #ifdef DIAGNOSTIC 207 if (lock < &plex->lock[0] || lock >= &plex->lock[PLEX_LOCKS]) 208 panic("vinum: rangelock %p on plex %d invalid, not between %p and %p", 209 lock, 210 plexno, 211 &plex->lock[0], 212 &plex->lock[PLEX_LOCKS]); 213 #endif 214 #ifdef VINUMDEBUG 215 if (debug & DEBUG_LASTREQS) 216 logrq(loginfo_unlock, (union rqinfou) lock, &lock->bp->b_bio1); 217 #endif 218 lock->stripe = 0; /* no longer used */ 219 plex->usedlocks--; /* one less lock */ 220 if (plex->usedlocks == PLEX_LOCKS - 1) /* we were full, */ 221 wakeup(&plex->usedlocks); /* get a waiter if one's there */ 222 wakeup((void *) lock); 223 } 224 225 /* Get a lock for the global config, wait if it's not available */ 226 int 227 lock_config(void) 228 { 229 int error; 230 231 while ((vinum_conf.flags & VF_LOCKED) != 0) { 232 vinum_conf.flags |= VF_LOCKING; 233 if ((error = tsleep(&vinum_conf, 0, "vincfg", 0)) != 0) 234 return error; 235 } 236 vinum_conf.flags |= VF_LOCKED; 237 return 0; 238 } 239 240 /* Unlock and wake up any waiters */ 241 void 242 unlock_config(void) 243 { 244 vinum_conf.flags &= ~VF_LOCKED; 245 if ((vinum_conf.flags & VF_LOCKING) != 0) { 246 vinum_conf.flags &= ~VF_LOCKING; 247 wakeup(&vinum_conf); 248 } 249 } 250