* Copyright (c) 1982, 1986, 1988 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)ufs_disksubr.c 7.13 (Berkeley) %G%
* Seek sort for disks. We depend on the driver
* which calls us using b_resid as the current cylinder number.
* The argument dp structure holds a b_actf activity chain pointer
* on which we keep two queues, sorted in ascending cylinder order.
* The first queue holds those requests which are positioned after
* the current cylinder (in the first request); the second holds
* requests which came in after their cylinder number was passed.
* Thus we implement a one way scan, retracting after reaching the
* end of the drive to the first request on the second queue,
* at which time it becomes the first queue.
* A one-way scan is natural because of the way UNIX read-ahead
register struct buf
*dp
, *bp
;
* If nothing on the activity queue, then
* we become the only thing.
* If we lie after the first (currently active)
* request, then we must locate the second request list
* and add ourselves to it.
if (bp
->b_cylin
< ap
->b_cylin
) {
* Check for an ``inversion'' in the
* normally ascending cylinder numbers,
* indicating the start of the second request list.
if (ap
->av_forw
->b_cylin
< ap
->b_cylin
) {
* Search the second request list
* for the first request at a larger
* cylinder number. We go before that;
* if there is no such request, we go at end.
if (bp
->b_cylin
< ap
->av_forw
->b_cylin
)
if (bp
->b_cylin
== ap
->av_forw
->b_cylin
&&
bp
->b_blkno
< ap
->av_forw
->b_blkno
)
goto insert
; /* after last */
* No inversions... we will go after the last, and
* be the first request in the second request list.
* Request is at/after the current request...
* sort in the first request list.
* We want to go after the current request
* if there is an inversion after it (i.e. it is
* the end of the first request list), or if
* the next request is a larger cylinder than our request.
if (ap
->av_forw
->b_cylin
< ap
->b_cylin
||
bp
->b_cylin
< ap
->av_forw
->b_cylin
||
(bp
->b_cylin
== ap
->av_forw
->b_cylin
&&
bp
->b_blkno
< ap
->av_forw
->b_blkno
))
* Neither a second list nor a larger
* request... we go at the end of the first list,
* which is the same as the end of the whole schebang.
bp
->av_forw
= ap
->av_forw
;
* Attempt to read a disk label from a device
* using the indicated stategy routine.
* The label must be partly set up before this:
* secpercyl and anything required in the strategy routine
* (e.g., sector size) must be filled in before calling us.
* Returns null on success and an error string on failure.
readdisklabel(dev
, strat
, lp
)
register struct disklabel
*lp
;
if (lp
->d_secperunit
== 0)
lp
->d_secperunit
= 0x1fffffff;
if (lp
->d_partitions
[0].p_size
== 0)
lp
->d_partitions
[0].p_size
= 0x1fffffff;
lp
->d_partitions
[0].p_offset
= 0;
bp
= geteblk((int)lp
->d_secsize
);
bp
->b_blkno
= LABELSECTOR
;
bp
->b_bcount
= lp
->d_secsize
;
bp
->b_flags
= B_BUSY
| B_READ
;
bp
->b_cylin
= LABELSECTOR
/ lp
->d_secpercyl
;
} else for (dlp
= (struct disklabel
*)bp
->b_un
.b_addr
;
dlp
<= (struct disklabel
*)(bp
->b_un
.b_addr
+DEV_BSIZE
-sizeof(*dlp
));
dlp
= (struct disklabel
*)((char *)dlp
+ sizeof(long))) {
if (dlp
->d_magic
!= DISKMAGIC
|| dlp
->d_magic2
!= DISKMAGIC
) {
} else if (dlp
->d_npartitions
> MAXPARTITIONS
||
msg
= "disk label corrupted";
bp
->b_flags
= B_INVAL
| B_AGE
;
* Check new disk label for sensibility
setdisklabel(olp
, nlp
, openmask
)
register struct disklabel
*olp
, *nlp
;
register struct partition
*opp
, *npp
;
if (nlp
->d_magic
!= DISKMAGIC
|| nlp
->d_magic2
!= DISKMAGIC
||
while ((i
= ffs((long)openmask
)) != 0) {
if (nlp
->d_npartitions
<= i
)
opp
= &olp
->d_partitions
[i
];
npp
= &nlp
->d_partitions
[i
];
if (npp
->p_offset
!= opp
->p_offset
|| npp
->p_size
< opp
->p_size
)
* Copy internally-set partition information
* if new label doesn't include it. XXX
if (npp
->p_fstype
== FS_UNUSED
&& opp
->p_fstype
!= FS_UNUSED
) {
npp
->p_fstype
= opp
->p_fstype
;
npp
->p_fsize
= opp
->p_fsize
;
npp
->p_frag
= opp
->p_frag
;
nlp
->d_checksum
= dkcksum(nlp
);
/* encoding of disk minor numbers, should be elsewhere... */
#define dkunit(dev) (minor(dev) >> 3)
#define dkpart(dev) (minor(dev) & 07)
#define dkminor(unit, part) (((unit) << 3) | (part))
* Write disk label back to device after modification.
writedisklabel(dev
, strat
, lp
)
register struct disklabel
*lp
;
if (lp
->d_partitions
[labelpart
].p_offset
!= 0) {
if (lp
->d_partitions
[0].p_offset
!= 0)
return (EXDEV
); /* not quite right */
bp
= geteblk((int)lp
->d_secsize
);
bp
->b_dev
= makedev(major(dev
), dkminor(dkunit(dev
), labelpart
));
bp
->b_blkno
= LABELSECTOR
;
bp
->b_bcount
= lp
->d_secsize
;
for (dlp
= (struct disklabel
*)bp
->b_un
.b_addr
;
dlp
<= (struct disklabel
*)
(bp
->b_un
.b_addr
+ lp
->d_secsize
- sizeof(*dlp
));
dlp
= (struct disklabel
*)((char *)dlp
+ sizeof(long))) {
if (dlp
->d_magic
== DISKMAGIC
&& dlp
->d_magic2
== DISKMAGIC
&&
* Compute checksum for disk label.
register struct disklabel
*lp
;
register u_short
*start
, *end
;
register u_short sum
= 0;
end
= (u_short
*)&lp
->d_partitions
[lp
->d_npartitions
];
* Disk error is the preface to plaintive error messages
* about failing disk transfers. It prints messages of the form
hp0g: hard error reading fsbn 12345 of 12344-12347 (hp0 bn %d cn %d tn %d sn %d)
* if the offset of the error in the transfer and a disk label
* are both available. blkdone should be -1 if the position of the error
* is unknown; the disklabel pointer may be null from drivers that have not
* been converted to use them. The message is printed with printf
* if pri is LOG_PRINTF, otherwise it uses log at the specified priority.
* The message should be completed (with at least a newline) with printf
* or addlog, respectively. There is no trailing space.
diskerr(bp
, dname
, what
, pri
, blkdone
, lp
)
register struct disklabel
*lp
;
int unit
= dkunit(bp
->b_dev
), part
= dkpart(bp
->b_dev
);
register int (*pr
)(), sn
;
char partname
= 'a' + part
;
extern printf(), addlog();
(*pr
)("%s%d%c: %s %sing fsbn ", dname
, unit
, partname
, what
,
bp
->b_flags
& B_READ
? "read" : "writ");
if (bp
->b_bcount
<= DEV_BSIZE
)
(*pr
)("%d-%d", bp
->b_blkno
,
bp
->b_blkno
+ (bp
->b_bcount
- 1) / DEV_BSIZE
);
if (lp
&& (blkdone
>= 0 || bp
->b_bcount
<= lp
->d_secsize
)) {
sn
*= DEV_BSIZE
/ lp
->d_secsize
; /* XXX */
sn
+= lp
->d_partitions
[part
].p_offset
;
(*pr
)(" (%s%d bn %d; cn %d", dname
, unit
, sn
,
(*pr
)(" tn %d sn %d)", sn
/ lp
->d_nsectors
, sn
% lp
->d_nsectors
);