xref: /freebsd/sys/kern/kern_physio.c (revision c48d17750f93a0c73afe5ac7d5ec2ce0093a4407)
126f9a767SRodney W. Grimes /*
226f9a767SRodney W. Grimes  * Copyright (c) 1994 John S. Dyson
326f9a767SRodney W. Grimes  * All rights reserved.
4df8bae1dSRodney W. Grimes  *
5df8bae1dSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
6df8bae1dSRodney W. Grimes  * modification, are permitted provided that the following conditions
7df8bae1dSRodney W. Grimes  * are met:
8df8bae1dSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
926f9a767SRodney W. Grimes  *    notice immediately at the beginning of the file, without modification,
1026f9a767SRodney W. Grimes  *    this list of conditions, and the following disclaimer.
11df8bae1dSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
12df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
13df8bae1dSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
1426f9a767SRodney W. Grimes  * 3. Absolutely no warranty of function or purpose is made by the author
1526f9a767SRodney W. Grimes  *    John S. Dyson.
1626f9a767SRodney W. Grimes  * 4. Modifications may be freely made to this file if the above conditions
1726f9a767SRodney W. Grimes  *    are met.
183c4dd356SDavid Greenman  *
19c48d1775SPoul-Henning Kamp  * $Id: kern_physio.c,v 1.32 1999/05/06 20:00:25 phk Exp $
20df8bae1dSRodney W. Grimes  */
21df8bae1dSRodney W. Grimes 
22df8bae1dSRodney W. Grimes #include <sys/param.h>
23df8bae1dSRodney W. Grimes #include <sys/systm.h>
24df8bae1dSRodney W. Grimes #include <sys/buf.h>
25df8bae1dSRodney W. Grimes #include <sys/conf.h>
26df8bae1dSRodney W. Grimes #include <sys/proc.h>
2708637435SBruce Evans #include <sys/uio.h>
2808637435SBruce Evans 
2926f9a767SRodney W. Grimes #include <vm/vm.h>
30efeaf95aSDavid Greenman #include <vm/vm_extern.h>
31df8bae1dSRodney W. Grimes 
3298d93822SBruce Evans static void	physwakeup __P((struct buf *bp));
3350ce7ff4SJohn Dyson static struct buf * phygetvpbuf(dev_t dev, int resid);
3426f9a767SRodney W. Grimes 
3526f9a767SRodney W. Grimes int
36c48d1775SPoul-Henning Kamp physread(dev_t dev, struct uio *uio, int ioflag)
37c48d1775SPoul-Henning Kamp {
38c48d1775SPoul-Henning Kamp 	return(physio(cdevsw[major(dev)]->d_strategy, NULL, dev, 1, minphys, uio));
39c48d1775SPoul-Henning Kamp }
40c48d1775SPoul-Henning Kamp 
41c48d1775SPoul-Henning Kamp int
42c48d1775SPoul-Henning Kamp physwrite(dev_t dev, struct uio *uio, int ioflag)
43c48d1775SPoul-Henning Kamp {
44c48d1775SPoul-Henning Kamp 	return(physio(cdevsw[major(dev)]->d_strategy, NULL, dev, 0, minphys, uio));
45c48d1775SPoul-Henning Kamp }
46c48d1775SPoul-Henning Kamp 
47c48d1775SPoul-Henning Kamp int
4826f9a767SRodney W. Grimes physio(strategy, bp, dev, rw, minp, uio)
49b5e8ce9fSBruce Evans 	d_strategy_t *strategy;
5026f9a767SRodney W. Grimes 	struct buf *bp;
5126f9a767SRodney W. Grimes 	dev_t dev;
5226f9a767SRodney W. Grimes 	int rw;
5398d93822SBruce Evans 	u_int (*minp) __P((struct buf *bp));
5426f9a767SRodney W. Grimes 	struct uio *uio;
55df8bae1dSRodney W. Grimes {
5626f9a767SRodney W. Grimes 	int i;
5726f9a767SRodney W. Grimes 	int bufflags = rw?B_READ:0;
5826f9a767SRodney W. Grimes 	int error;
5926f9a767SRodney W. Grimes 	int spl;
6016f62314SDavid Greenman 	caddr_t sa;
6116f62314SDavid Greenman 	int bp_alloc = (bp == 0);
6216f62314SDavid Greenman 	struct buf *bpa;
63df8bae1dSRodney W. Grimes 
64df8bae1dSRodney W. Grimes 	/*
6557dc5948SPeter Wemm 	 * Keep the process UPAGES from being swapped. (XXX for performance?)
66df8bae1dSRodney W. Grimes 	 */
6757dc5948SPeter Wemm 	PHOLD(curproc);
6826f9a767SRodney W. Grimes 
6926f9a767SRodney W. Grimes 	/* create and build a buffer header for a transfer */
7050ce7ff4SJohn Dyson 	bpa = (struct buf *)phygetvpbuf(dev, uio->uio_resid);
7116f62314SDavid Greenman 	if (!bp_alloc) {
7226f9a767SRodney W. Grimes 		spl = splbio();
7326f9a767SRodney W. Grimes 		while (bp->b_flags & B_BUSY) {
7426f9a767SRodney W. Grimes 			bp->b_flags |= B_WANTED;
7526f9a767SRodney W. Grimes 			tsleep((caddr_t)bp, PRIBIO, "physbw", 0);
7626f9a767SRodney W. Grimes 		}
7726f9a767SRodney W. Grimes 		bp->b_flags |= B_BUSY;
7826f9a767SRodney W. Grimes 		splx(spl);
7916f62314SDavid Greenman 	} else {
8016f62314SDavid Greenman 		bp = bpa;
8126f9a767SRodney W. Grimes 	}
8226f9a767SRodney W. Grimes 
8316f62314SDavid Greenman 	/*
8416f62314SDavid Greenman 	 * get a copy of the kva from the physical buffer
8516f62314SDavid Greenman 	 */
8616f62314SDavid Greenman 	sa = bpa->b_data;
8726f9a767SRodney W. Grimes 	error = bp->b_error = 0;
8826f9a767SRodney W. Grimes 
8926f9a767SRodney W. Grimes 	for(i=0;i<uio->uio_iovcnt;i++) {
9026f9a767SRodney W. Grimes 		while( uio->uio_iov[i].iov_len) {
9126f9a767SRodney W. Grimes 
9250ce7ff4SJohn Dyson 			bp->b_dev = dev;
9326f9a767SRodney W. Grimes 			bp->b_bcount = uio->uio_iov[i].iov_len;
944b83b27fSJohn Dyson 			bp->b_flags = B_BUSY | B_PHYS | B_CALL | bufflags;
954b83b27fSJohn Dyson 			bp->b_iodone = physwakeup;
964b83b27fSJohn Dyson 			bp->b_data = uio->uio_iov[i].iov_base;
97a481f200SDavid Greenman 			bp->b_bcount = minp( bp);
98a481f200SDavid Greenman 			if( minp != minphys)
99a481f200SDavid Greenman 				bp->b_bcount = minphys( bp);
10026f9a767SRodney W. Grimes 			bp->b_bufsize = bp->b_bcount;
10116f62314SDavid Greenman 			/*
10216f62314SDavid Greenman 			 * pass in the kva from the physical buffer
10316f62314SDavid Greenman 			 * for the temporary kernel mapping.
10416f62314SDavid Greenman 			 */
10516f62314SDavid Greenman 			bp->b_saveaddr = sa;
10626f9a767SRodney W. Grimes 			bp->b_blkno = btodb(uio->uio_offset);
107e620a1cbSSøren Schmidt 			bp->b_offset = uio->uio_offset;
10826f9a767SRodney W. Grimes 
1096884d2aaSPeter Wemm 			if (uio->uio_segflg == UIO_USERSPACE) {
11026f9a767SRodney W. Grimes 				if (rw && !useracc(bp->b_data, bp->b_bufsize, B_WRITE)) {
11126f9a767SRodney W. Grimes 					error = EFAULT;
11226f9a767SRodney W. Grimes 					goto doerror;
11326f9a767SRodney W. Grimes 				}
11426f9a767SRodney W. Grimes 				if (!rw && !useracc(bp->b_data, bp->b_bufsize, B_READ)) {
11526f9a767SRodney W. Grimes 					error = EFAULT;
11626f9a767SRodney W. Grimes 					goto doerror;
11726f9a767SRodney W. Grimes 				}
11826f9a767SRodney W. Grimes 
1196884d2aaSPeter Wemm 				/* bring buffer into kernel space */
12026f9a767SRodney W. Grimes 				vmapbuf(bp);
1216884d2aaSPeter Wemm 			}
12226f9a767SRodney W. Grimes 
12326f9a767SRodney W. Grimes 			/* perform transfer */
12426f9a767SRodney W. Grimes 			(*strategy)(bp);
12526f9a767SRodney W. Grimes 
12626f9a767SRodney W. Grimes 			spl = splbio();
12726f9a767SRodney W. Grimes 			while ((bp->b_flags & B_DONE) == 0)
12826f9a767SRodney W. Grimes 				tsleep((caddr_t)bp, PRIBIO, "physstr", 0);
12926f9a767SRodney W. Grimes 			splx(spl);
13026f9a767SRodney W. Grimes 
1316884d2aaSPeter Wemm 			/* release mapping into kernel space */
1326884d2aaSPeter Wemm 			if (uio->uio_segflg == UIO_USERSPACE)
13326f9a767SRodney W. Grimes 				vunmapbuf(bp);
13426f9a767SRodney W. Grimes 
13526f9a767SRodney W. Grimes 			/*
13626f9a767SRodney W. Grimes 			 * update the uio data
13726f9a767SRodney W. Grimes 			 */
13826f9a767SRodney W. Grimes 			{
13926f9a767SRodney W. Grimes 				int iolen = bp->b_bcount - bp->b_resid;
1407d7bb69dSDavid Greenman 
1417d7bb69dSDavid Greenman 				if (iolen == 0 && !(bp->b_flags & B_ERROR))
1427d7bb69dSDavid Greenman 					goto doerror;	/* EOF */
14326f9a767SRodney W. Grimes 				uio->uio_iov[i].iov_len -= iolen;
14426f9a767SRodney W. Grimes 				uio->uio_iov[i].iov_base += iolen;
14526f9a767SRodney W. Grimes 				uio->uio_resid -= iolen;
14626f9a767SRodney W. Grimes 				uio->uio_offset += iolen;
14726f9a767SRodney W. Grimes 			}
14826f9a767SRodney W. Grimes 
14926f9a767SRodney W. Grimes 			/*
15026f9a767SRodney W. Grimes 			 * check for an error
15126f9a767SRodney W. Grimes 			 */
15226f9a767SRodney W. Grimes 			if( bp->b_flags & B_ERROR) {
15326f9a767SRodney W. Grimes 				error = bp->b_error;
15426f9a767SRodney W. Grimes 				goto doerror;
15526f9a767SRodney W. Grimes 			}
15626f9a767SRodney W. Grimes 		}
15726f9a767SRodney W. Grimes 	}
15826f9a767SRodney W. Grimes 
15926f9a767SRodney W. Grimes 
16026f9a767SRodney W. Grimes doerror:
1611c7c3c6aSMatthew Dillon 	relpbuf(bpa, NULL);
16216f62314SDavid Greenman 	if (!bp_alloc) {
16326f9a767SRodney W. Grimes 		bp->b_flags &= ~(B_BUSY|B_PHYS);
16426f9a767SRodney W. Grimes 		if( bp->b_flags & B_WANTED) {
16526f9a767SRodney W. Grimes 			bp->b_flags &= ~B_WANTED;
16626f9a767SRodney W. Grimes 			wakeup((caddr_t)bp);
16726f9a767SRodney W. Grimes 		}
16826f9a767SRodney W. Grimes 	}
16926f9a767SRodney W. Grimes 	/*
17057dc5948SPeter Wemm 	 * Allow the process UPAGES to be swapped again.
17126f9a767SRodney W. Grimes 	 */
17257dc5948SPeter Wemm 	PRELE(curproc);
17326f9a767SRodney W. Grimes 
17426f9a767SRodney W. Grimes 	return (error);
175df8bae1dSRodney W. Grimes }
176df8bae1dSRodney W. Grimes 
177df8bae1dSRodney W. Grimes u_int
178eb776aeaSBruce Evans minphys(bp)
179eb776aeaSBruce Evans 	struct buf *bp;
180df8bae1dSRodney W. Grimes {
18150ce7ff4SJohn Dyson 	u_int maxphys = DFLTPHYS;
182f7ea2f55SJulian Elischer 	struct cdevsw *bdsw;
18350ce7ff4SJohn Dyson 
184f7ea2f55SJulian Elischer 	bdsw = cdevsw[major(bp->b_dev)];
18550ce7ff4SJohn Dyson 
18650ce7ff4SJohn Dyson 	if (bdsw && bdsw->d_maxio) {
18750ce7ff4SJohn Dyson 		maxphys = bdsw->d_maxio;
18850ce7ff4SJohn Dyson 	}
189aec0bcdfSJohn Dyson 	if (bp->b_kvasize && (bp->b_kvasize < maxphys))
19050ce7ff4SJohn Dyson 		maxphys = bp->b_kvasize;
191df8bae1dSRodney W. Grimes 
1924b83b27fSJohn Dyson 	if(((vm_offset_t) bp->b_data) & PAGE_MASK) {
19350ce7ff4SJohn Dyson 		maxphys -= PAGE_SIZE;
1944b83b27fSJohn Dyson 	}
1954b83b27fSJohn Dyson 
1964b83b27fSJohn Dyson 	if( bp->b_bcount > maxphys) {
1974b83b27fSJohn Dyson 		bp->b_bcount = maxphys;
19826f9a767SRodney W. Grimes 	}
19950ce7ff4SJohn Dyson 
20026f9a767SRodney W. Grimes 	return bp->b_bcount;
201df8bae1dSRodney W. Grimes }
202df8bae1dSRodney W. Grimes 
20350ce7ff4SJohn Dyson struct buf *
20450ce7ff4SJohn Dyson phygetvpbuf(dev_t dev, int resid)
20550ce7ff4SJohn Dyson {
206f7ea2f55SJulian Elischer 	struct cdevsw *bdsw;
20750ce7ff4SJohn Dyson 	int maxio;
20850ce7ff4SJohn Dyson 
209f7ea2f55SJulian Elischer 	bdsw = cdevsw[major(dev)];
210f7ea2f55SJulian Elischer 	if ((bdsw == NULL) || (bdsw->d_bmaj == -1))
2111c7c3c6aSMatthew Dillon 		return getpbuf(NULL);
21250ce7ff4SJohn Dyson 
21350ce7ff4SJohn Dyson 	maxio = bdsw->d_maxio;
21450ce7ff4SJohn Dyson 	if (resid > maxio)
21550ce7ff4SJohn Dyson 		resid = maxio;
21650ce7ff4SJohn Dyson 
2171c7c3c6aSMatthew Dillon 	return getpbuf(NULL);
21850ce7ff4SJohn Dyson }
21950ce7ff4SJohn Dyson 
22026f9a767SRodney W. Grimes static void
22126f9a767SRodney W. Grimes physwakeup(bp)
22226f9a767SRodney W. Grimes 	struct buf *bp;
22326f9a767SRodney W. Grimes {
22426f9a767SRodney W. Grimes 	wakeup((caddr_t) bp);
22526f9a767SRodney W. Grimes 	bp->b_flags &= ~B_CALL;
226df8bae1dSRodney W. Grimes }
227