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