Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / kern / subr_disk.c
1 /*
2  * ----------------------------------------------------------------------------
3  * "THE BEER-WARE LICENSE" (Revision 42):
4  * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5  * can do whatever you want with this stuff. If we meet some day, and you think
6  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7  * ----------------------------------------------------------------------------
8  *
9  * $FreeBSD: src/sys/kern/subr_disk.c,v 1.20.2.6 2001/10/05 07:14:57 peter Exp $
10  *
11  */
12
13 #include <sys/param.h>
14 #include <sys/systm.h>
15 #include <sys/kernel.h>
16 #include <sys/sysctl.h>
17 #include <sys/buf.h>
18 #include <sys/conf.h>
19 #include <sys/disk.h>
20 #include <sys/malloc.h>
21 #include <sys/sysctl.h>
22 #include <machine/md_var.h>
23 #include <sys/ctype.h>
24
25 static MALLOC_DEFINE(M_DISK, "disk", "disk data");
26
27 static d_strategy_t diskstrategy;
28 static d_open_t diskopen;
29 static d_close_t diskclose; 
30 static d_ioctl_t diskioctl;
31 static d_psize_t diskpsize;
32
33 static LIST_HEAD(, disk) disklist = LIST_HEAD_INITIALIZER(&disklist);
34
35 static void
36 inherit_raw(dev_t pdev, dev_t dev)
37 {
38         dev->si_disk = pdev->si_disk;
39         dev->si_drv1 = pdev->si_drv1;
40         dev->si_drv2 = pdev->si_drv2;
41         dev->si_iosize_max = pdev->si_iosize_max;
42         dev->si_bsize_phys = pdev->si_bsize_phys;
43         dev->si_bsize_best = pdev->si_bsize_best;
44 }
45
46 dev_t
47 disk_create(int unit, struct disk *dp, int flags, struct cdevsw *cdevsw, struct cdevsw *proto)
48 {
49         dev_t dev;
50
51         bzero(dp, sizeof(*dp));
52
53         dev = makedev(cdevsw->d_maj, 0);        
54         if (!devsw(dev)) {
55                 *proto = *cdevsw;
56                 proto->d_open = diskopen;
57                 proto->d_close = diskclose;
58                 proto->d_ioctl = diskioctl;
59                 proto->d_strategy = diskstrategy;
60                 proto->d_psize = diskpsize;
61                 cdevsw_add(proto);
62         }
63
64         if (bootverbose)
65                 printf("Creating DISK %s%d\n", cdevsw->d_name, unit);
66         dev = make_dev(proto, dkmakeminor(unit, WHOLE_DISK_SLICE, RAW_PART),
67             UID_ROOT, GID_OPERATOR, 0640, "%s%d", cdevsw->d_name, unit);
68
69         dev->si_disk = dp;
70         dp->d_dev = dev;
71         dp->d_dsflags = flags;
72         dp->d_devsw = cdevsw;
73         LIST_INSERT_HEAD(&disklist, dp, d_list);
74         return (dev);
75 }
76
77 int
78 disk_dumpcheck(dev_t dev, u_int *count, u_int *blkno, u_int *secsize)
79 {
80         struct disk *dp;
81         struct disklabel *dl;
82         u_int boff;
83
84         dp = dev->si_disk;
85         if (!dp)
86                 return (ENXIO);
87         if (!dp->d_slice)
88                 return (ENXIO);
89         dl = dsgetlabel(dev, dp->d_slice);
90         if (!dl)
91                 return (ENXIO);
92         *count = Maxmem * (PAGE_SIZE / dl->d_secsize);
93         if (dumplo <= LABELSECTOR || 
94             (dumplo + *count > dl->d_partitions[dkpart(dev)].p_size))
95                 return (EINVAL);
96         boff = dl->d_partitions[dkpart(dev)].p_offset +
97             dp->d_slice->dss_slices[dkslice(dev)].ds_offset;
98         *blkno = boff + dumplo;
99         *secsize = dl->d_secsize;
100         return (0);
101         
102 }
103
104 void 
105 disk_invalidate (struct disk *disk)
106 {
107         if (disk->d_slice)
108                 dsgone(&disk->d_slice);
109 }
110
111 void
112 disk_destroy(dev_t dev)
113 {
114         LIST_REMOVE(dev->si_disk, d_list);
115         bzero(dev->si_disk, sizeof(*dev->si_disk));
116         dev->si_disk = NULL;
117         destroy_dev(dev);
118         return;
119 }
120
121 struct disk *
122 disk_enumerate(struct disk *disk)
123 {
124         if (!disk)
125                 return (LIST_FIRST(&disklist));
126         else
127                 return (LIST_NEXT(disk, d_list));
128 }
129
130 static int
131 sysctl_disks(SYSCTL_HANDLER_ARGS)
132 {
133         struct disk *disk;
134         int error, first;
135
136         disk = NULL;
137         first = 1;
138
139         while ((disk = disk_enumerate(disk))) {
140                 if (!first) {
141                         error = SYSCTL_OUT(req, " ", 1);
142                         if (error)
143                                 return error;
144                 } else {
145                         first = 0;
146                 }
147                 error = SYSCTL_OUT(req, disk->d_dev->si_name, strlen(disk->d_dev->si_name));
148                 if (error)
149                         return error;
150         }
151         error = SYSCTL_OUT(req, "", 1);
152         return error;
153 }
154  
155 SYSCTL_PROC(_kern, OID_AUTO, disks, CTLTYPE_STRING | CTLFLAG_RD, 0, NULL, 
156     sysctl_disks, "A", "names of available disks");
157
158 /*
159  * The cdevsw functions
160  */
161
162 static int
163 diskopen(dev_t dev, int oflags, int devtype, struct proc *p)
164 {
165         dev_t pdev;
166         struct disk *dp;
167         int error;
168
169         error = 0;
170         pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
171
172         dp = pdev->si_disk;
173         if (!dp)
174                 return (ENXIO);
175
176         while (dp->d_flags & DISKFLAG_LOCK) {
177                 dp->d_flags |= DISKFLAG_WANTED;
178                 error = tsleep(dp, PRIBIO | PCATCH, "diskopen", hz);
179                 if (error)
180                         return (error);
181         }
182         dp->d_flags |= DISKFLAG_LOCK;
183
184         if (!dsisopen(dp->d_slice)) {
185                 if (!pdev->si_iosize_max)
186                         pdev->si_iosize_max = dev->si_iosize_max;
187                 error = dp->d_devsw->d_open(pdev, oflags, devtype, p);
188         }
189
190         /* Inherit properties from the whole/raw dev_t */
191         inherit_raw(pdev, dev);
192
193         if (error)
194                 goto out;
195         
196         error = dsopen(dev, devtype, dp->d_dsflags, &dp->d_slice, &dp->d_label);
197
198         if (!dsisopen(dp->d_slice)) 
199                 dp->d_devsw->d_close(pdev, oflags, devtype, p);
200 out:    
201         dp->d_flags &= ~DISKFLAG_LOCK;
202         if (dp->d_flags & DISKFLAG_WANTED) {
203                 dp->d_flags &= ~DISKFLAG_WANTED;
204                 wakeup(dp);
205         }
206         
207         return(error);
208 }
209
210 static int
211 diskclose(dev_t dev, int fflag, int devtype, struct proc *p)
212 {
213         struct disk *dp;
214         int error;
215         dev_t pdev;
216
217         error = 0;
218         pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
219         dp = pdev->si_disk;
220         if (!dp)
221                 return (ENXIO);
222         dsclose(dev, devtype, dp->d_slice);
223         if (!dsisopen(dp->d_slice))
224                 error = dp->d_devsw->d_close(dp->d_dev, fflag, devtype, p);
225         return (error);
226 }
227
228 static void
229 diskstrategy(struct buf *bp)
230 {
231         dev_t pdev;
232         struct disk *dp;
233
234         pdev = dkmodpart(dkmodslice(bp->b_dev, WHOLE_DISK_SLICE), RAW_PART);
235         dp = pdev->si_disk;
236         if (dp != bp->b_dev->si_disk)
237                 inherit_raw(pdev, bp->b_dev);
238
239         if (!dp) {
240                 bp->b_error = ENXIO;
241                 bp->b_flags |= B_ERROR;
242                 biodone(bp);
243                 return;
244         }
245
246         if (dscheck(bp, dp->d_slice) <= 0) {
247                 biodone(bp);
248                 return;
249         }
250
251         dp->d_devsw->d_strategy(bp);
252         return;
253         
254 }
255
256 static int
257 diskioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
258 {
259         struct disk *dp;
260         int error;
261         dev_t pdev;
262
263         pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
264         dp = pdev->si_disk;
265         if (!dp)
266                 return (ENXIO);
267         error = dsioctl(dev, cmd, data, fflag, &dp->d_slice);
268         if (error == ENOIOCTL)
269                 error = dp->d_devsw->d_ioctl(dev, cmd, data, fflag, p);
270         return (error);
271 }
272
273 static int
274 diskpsize(dev_t dev)
275 {
276         struct disk *dp;
277         dev_t pdev;
278
279         pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
280         dp = pdev->si_disk;
281         if (!dp)
282                 return (-1);
283         if (dp != dev->si_disk) {
284                 dev->si_drv1 = pdev->si_drv1;
285                 dev->si_drv2 = pdev->si_drv2;
286                 /* XXX: don't set bp->b_dev->si_disk (?) */
287         }
288         return (dssize(dev, &dp->d_slice));
289 }
290
291 SYSCTL_DECL(_debug_sizeof);
292
293 SYSCTL_INT(_debug_sizeof, OID_AUTO, disklabel, CTLFLAG_RD, 
294     0, sizeof(struct disklabel), "sizeof(struct disklabel)");
295
296 SYSCTL_INT(_debug_sizeof, OID_AUTO, diskslices, CTLFLAG_RD, 
297     0, sizeof(struct diskslices), "sizeof(struct diskslices)");
298
299 SYSCTL_INT(_debug_sizeof, OID_AUTO, disk, CTLFLAG_RD, 
300     0, sizeof(struct disk), "sizeof(struct disk)");