1 /* 2 * Copyright (c) 1982, 1986, 1988 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 * 7 * @(#)ufs_disksubr.c 7.15 (Berkeley) 03/19/91 8 */ 9 10 #include "param.h" 11 #include "systm.h" 12 #include "buf.h" 13 #include "disklabel.h" 14 #include "syslog.h" 15 16 /* 17 * Seek sort for disks. We depend on the driver 18 * which calls us using b_resid as the current cylinder number. 19 * 20 * The argument dp structure holds a b_actf activity chain pointer 21 * on which we keep two queues, sorted in ascending cylinder order. 22 * The first queue holds those requests which are positioned after 23 * the current cylinder (in the first request); the second holds 24 * requests which came in after their cylinder number was passed. 25 * Thus we implement a one way scan, retracting after reaching the 26 * end of the drive to the first request on the second queue, 27 * at which time it becomes the first queue. 28 * 29 * A one-way scan is natural because of the way UNIX read-ahead 30 * blocks are allocated. 31 */ 32 33 #define b_cylin b_resid 34 35 disksort(dp, bp) 36 register struct buf *dp, *bp; 37 { 38 register struct buf *ap; 39 40 /* 41 * If nothing on the activity queue, then 42 * we become the only thing. 43 */ 44 ap = dp->b_actf; 45 if(ap == NULL) { 46 dp->b_actf = bp; 47 dp->b_actl = bp; 48 bp->av_forw = NULL; 49 return; 50 } 51 /* 52 * If we lie after the first (currently active) 53 * request, then we must locate the second request list 54 * and add ourselves to it. 55 */ 56 if (bp->b_cylin < ap->b_cylin) { 57 while (ap->av_forw) { 58 /* 59 * Check for an ``inversion'' in the 60 * normally ascending cylinder numbers, 61 * indicating the start of the second request list. 62 */ 63 if (ap->av_forw->b_cylin < ap->b_cylin) { 64 /* 65 * Search the second request list 66 * for the first request at a larger 67 * cylinder number. We go before that; 68 * if there is no such request, we go at end. 69 */ 70 do { 71 if (bp->b_cylin < ap->av_forw->b_cylin) 72 goto insert; 73 if (bp->b_cylin == ap->av_forw->b_cylin && 74 bp->b_blkno < ap->av_forw->b_blkno) 75 goto insert; 76 ap = ap->av_forw; 77 } while (ap->av_forw); 78 goto insert; /* after last */ 79 } 80 ap = ap->av_forw; 81 } 82 /* 83 * No inversions... we will go after the last, and 84 * be the first request in the second request list. 85 */ 86 goto insert; 87 } 88 /* 89 * Request is at/after the current request... 90 * sort in the first request list. 91 */ 92 while (ap->av_forw) { 93 /* 94 * We want to go after the current request 95 * if there is an inversion after it (i.e. it is 96 * the end of the first request list), or if 97 * the next request is a larger cylinder than our request. 98 */ 99 if (ap->av_forw->b_cylin < ap->b_cylin || 100 bp->b_cylin < ap->av_forw->b_cylin || 101 (bp->b_cylin == ap->av_forw->b_cylin && 102 bp->b_blkno < ap->av_forw->b_blkno)) 103 goto insert; 104 ap = ap->av_forw; 105 } 106 /* 107 * Neither a second list nor a larger 108 * request... we go at the end of the first list, 109 * which is the same as the end of the whole schebang. 110 */ 111 insert: 112 bp->av_forw = ap->av_forw; 113 ap->av_forw = bp; 114 if (ap == dp->b_actl) 115 dp->b_actl = bp; 116 } 117 118 /* 119 * Attempt to read a disk label from a device 120 * using the indicated stategy routine. 121 * The label must be partly set up before this: 122 * secpercyl and anything required in the strategy routine 123 * (e.g., sector size) must be filled in before calling us. 124 * Returns null on success and an error string on failure. 125 */ 126 char * 127 readdisklabel(dev, strat, lp) 128 dev_t dev; 129 int (*strat)(); 130 register struct disklabel *lp; 131 { 132 register struct buf *bp; 133 struct disklabel *dlp; 134 char *msg = NULL; 135 136 if (lp->d_secperunit == 0) 137 lp->d_secperunit = 0x1fffffff; 138 lp->d_npartitions = 1; 139 if (lp->d_partitions[0].p_size == 0) 140 lp->d_partitions[0].p_size = 0x1fffffff; 141 lp->d_partitions[0].p_offset = 0; 142 143 bp = geteblk((int)lp->d_secsize); 144 bp->b_dev = dev; 145 bp->b_blkno = LABELSECTOR; 146 bp->b_bcount = lp->d_secsize; 147 bp->b_flags = B_BUSY | B_READ; 148 bp->b_cylin = LABELSECTOR / lp->d_secpercyl; 149 (*strat)(bp); 150 if (biowait(bp)) { 151 msg = "I/O error"; 152 } else for (dlp = (struct disklabel *)bp->b_un.b_addr; 153 dlp <= (struct disklabel *)(bp->b_un.b_addr+DEV_BSIZE-sizeof(*dlp)); 154 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 155 if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) { 156 if (msg == NULL) 157 msg = "no disk label"; 158 } else if (dlp->d_npartitions > MAXPARTITIONS || 159 dkcksum(dlp) != 0) 160 msg = "disk label corrupted"; 161 else { 162 *lp = *dlp; 163 msg = NULL; 164 break; 165 } 166 } 167 bp->b_flags = B_INVAL | B_AGE; 168 brelse(bp); 169 return (msg); 170 } 171 172 /* 173 * Check new disk label for sensibility 174 * before setting it. 175 */ 176 setdisklabel(olp, nlp, openmask) 177 register struct disklabel *olp, *nlp; 178 u_long openmask; 179 { 180 register i; 181 register struct partition *opp, *npp; 182 183 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 184 dkcksum(nlp) != 0) 185 return (EINVAL); 186 while ((i = ffs((long)openmask)) != 0) { 187 i--; 188 openmask &= ~(1 << i); 189 if (nlp->d_npartitions <= i) 190 return (EBUSY); 191 opp = &olp->d_partitions[i]; 192 npp = &nlp->d_partitions[i]; 193 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 194 return (EBUSY); 195 /* 196 * Copy internally-set partition information 197 * if new label doesn't include it. XXX 198 */ 199 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 200 npp->p_fstype = opp->p_fstype; 201 npp->p_fsize = opp->p_fsize; 202 npp->p_frag = opp->p_frag; 203 npp->p_cpg = opp->p_cpg; 204 } 205 } 206 nlp->d_checksum = 0; 207 nlp->d_checksum = dkcksum(nlp); 208 *olp = *nlp; 209 return (0); 210 } 211 212 /* encoding of disk minor numbers, should be elsewhere... */ 213 #define dkunit(dev) (minor(dev) >> 3) 214 #define dkpart(dev) (minor(dev) & 07) 215 #define dkminor(unit, part) (((unit) << 3) | (part)) 216 217 /* 218 * Write disk label back to device after modification. 219 */ 220 writedisklabel(dev, strat, lp) 221 dev_t dev; 222 int (*strat)(); 223 register struct disklabel *lp; 224 { 225 struct buf *bp; 226 struct disklabel *dlp; 227 int labelpart; 228 int error = 0; 229 230 labelpart = dkpart(dev); 231 if (lp->d_partitions[labelpart].p_offset != 0) { 232 if (lp->d_partitions[0].p_offset != 0) 233 return (EXDEV); /* not quite right */ 234 labelpart = 0; 235 } 236 bp = geteblk((int)lp->d_secsize); 237 bp->b_dev = makedev(major(dev), dkminor(dkunit(dev), labelpart)); 238 bp->b_blkno = LABELSECTOR; 239 bp->b_bcount = lp->d_secsize; 240 bp->b_flags = B_READ; 241 (*strat)(bp); 242 if (error = biowait(bp)) 243 goto done; 244 for (dlp = (struct disklabel *)bp->b_un.b_addr; 245 dlp <= (struct disklabel *) 246 (bp->b_un.b_addr + lp->d_secsize - sizeof(*dlp)); 247 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 248 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC && 249 dkcksum(dlp) == 0) { 250 *dlp = *lp; 251 bp->b_flags = B_WRITE; 252 (*strat)(bp); 253 error = biowait(bp); 254 goto done; 255 } 256 } 257 error = ESRCH; 258 done: 259 brelse(bp); 260 return (error); 261 } 262 263 /* 264 * Compute checksum for disk label. 265 */ 266 dkcksum(lp) 267 register struct disklabel *lp; 268 { 269 register u_short *start, *end; 270 register u_short sum = 0; 271 272 start = (u_short *)lp; 273 end = (u_short *)&lp->d_partitions[lp->d_npartitions]; 274 while (start < end) 275 sum ^= *start++; 276 return (sum); 277 } 278 279 /* 280 * Disk error is the preface to plaintive error messages 281 * about failing disk transfers. It prints messages of the form 282 283 hp0g: hard error reading fsbn 12345 of 12344-12347 (hp0 bn %d cn %d tn %d sn %d) 284 285 * if the offset of the error in the transfer and a disk label 286 * are both available. blkdone should be -1 if the position of the error 287 * is unknown; the disklabel pointer may be null from drivers that have not 288 * been converted to use them. The message is printed with printf 289 * if pri is LOG_PRINTF, otherwise it uses log at the specified priority. 290 * The message should be completed (with at least a newline) with printf 291 * or addlog, respectively. There is no trailing space. 292 */ 293 diskerr(bp, dname, what, pri, blkdone, lp) 294 register struct buf *bp; 295 char *dname, *what; 296 int pri, blkdone; 297 register struct disklabel *lp; 298 { 299 int unit = dkunit(bp->b_dev), part = dkpart(bp->b_dev); 300 register int (*pr)(), sn; 301 char partname = 'a' + part; 302 extern printf(), addlog(); 303 304 if (pri != LOG_PRINTF) { 305 log(pri, ""); 306 pr = addlog; 307 } else 308 pr = printf; 309 (*pr)("%s%d%c: %s %sing fsbn ", dname, unit, partname, what, 310 bp->b_flags & B_READ ? "read" : "writ"); 311 sn = bp->b_blkno; 312 if (bp->b_bcount <= DEV_BSIZE) 313 (*pr)("%d", sn); 314 else { 315 if (blkdone >= 0) { 316 sn += blkdone; 317 (*pr)("%d of ", sn); 318 } 319 (*pr)("%d-%d", bp->b_blkno, 320 bp->b_blkno + (bp->b_bcount - 1) / DEV_BSIZE); 321 } 322 if (lp && (blkdone >= 0 || bp->b_bcount <= lp->d_secsize)) { 323 #ifdef tahoe 324 sn *= DEV_BSIZE / lp->d_secsize; /* XXX */ 325 #endif 326 sn += lp->d_partitions[part].p_offset; 327 (*pr)(" (%s%d bn %d; cn %d", dname, unit, sn, 328 sn / lp->d_secpercyl); 329 sn %= lp->d_secpercyl; 330 (*pr)(" tn %d sn %d)", sn / lp->d_nsectors, sn % lp->d_nsectors); 331 } 332 } 333