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