opendir change: refinement
[minix.git] / servers / vfs / dmap.c
blob3afe7eeb0116432fe8be6a29d478c5e1fbf0c5b6
1 /* This file contains the table with device <-> driver mappings. It also
2 * contains some routines to dynamically add and/ or remove device drivers
3 * or change mappings.
4 */
6 #include "fs.h"
7 #include <assert.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <ctype.h>
11 #include <unistd.h>
12 #include <minix/com.h>
13 #include <minix/ds.h>
14 #include "fproc.h"
15 #include "dmap.h"
16 #include "param.h"
18 /* The order of the entries in the table determines the mapping between major
19 * device numbers and device drivers. Character and block devices
20 * can be intermixed at random. The ordering determines the device numbers in
21 * /dev. Note that the major device numbers used in /dev are NOT the same as
22 * the process numbers of the device drivers. See <minix/dmap.h> for mappings.
25 struct dmap dmap[NR_DEVICES];
27 #define DT_EMPTY { no_dev, no_dev_io, NONE, "", 0, STYLE_NDEV, NULL, NONE, \
28 0, NULL, 0}
30 /*===========================================================================*
31 * lock_dmap *
32 *===========================================================================*/
33 void lock_dmap(struct dmap *dp)
35 /* Lock a driver */
36 struct worker_thread *org_self;
37 struct fproc *org_fp;
38 int r;
40 assert(dp != NULL);
41 assert(dp->dmap_driver != NONE);
43 org_fp = fp;
44 org_self = self;
46 if ((r = mutex_lock(dp->dmap_lock_ref)) != 0)
47 panic("unable to get a lock on dmap: %d\n", r);
49 fp = org_fp;
50 self = org_self;
53 /*===========================================================================*
54 * unlock_dmap *
55 *===========================================================================*/
56 void unlock_dmap(struct dmap *dp)
58 /* Unlock a driver */
59 int r;
61 assert(dp != NULL);
63 if ((r = mutex_unlock(dp->dmap_lock_ref)) != 0)
64 panic("unable to unlock dmap lock: %d\n", r);
67 /*===========================================================================*
68 * do_mapdriver *
69 *===========================================================================*/
70 int do_mapdriver()
72 /* Create a device->driver mapping. RS will tell us which major is driven by
73 * this driver, what type of device it is (regular, TTY, asynchronous, clone,
74 * etc), and its label. This label is registered with DS, and allows us to
75 * retrieve the driver's endpoint.
77 int r, flags, major, style, slot;
78 endpoint_t endpoint;
79 vir_bytes label_vir;
80 size_t label_len;
81 char label[LABEL_MAX];
82 struct fproc *rfp;
84 /* Only RS can map drivers. */
85 if (who_e != RS_PROC_NR) return(EPERM);
87 label_vir = (vir_bytes) job_m_in.md_label;
88 label_len = (size_t) job_m_in.md_label_len;
89 major = job_m_in.md_major;
90 flags = job_m_in.md_flags;
91 style = job_m_in.md_style;
93 /* Get the label */
94 if (label_len+1 > sizeof(label)) { /* Can we store this label? */
95 printf("VFS: do_mapdriver: label too long\n");
96 return(EINVAL);
98 r = sys_vircopy(who_e, label_vir, SELF, (vir_bytes) label, label_len);
99 if (r != OK) {
100 printf("VFS: do_mapdriver: sys_vircopy failed: %d\n", r);
101 return(EINVAL);
103 label[label_len] = '\0'; /* Terminate label */
105 /* Now we know how the driver is called, fetch its endpoint */
106 r = ds_retrieve_label_endpt(label, &endpoint);
107 if (r != OK) {
108 printf("VFS: do_mapdriver: label '%s' unknown\n", label);
109 return(EINVAL);
112 /* Process is a service */
113 if (isokendpt(endpoint, &slot) != OK) {
114 printf("VFS: can't map driver to unknown endpoint %d\n", endpoint);
115 return(EINVAL);
117 rfp = &fproc[slot];
118 rfp->fp_flags |= FP_SRV_PROC;
120 /* Try to update device mapping. */
121 return map_driver(label, major, endpoint, style, flags);
124 /*===========================================================================*
125 * map_driver *
126 *===========================================================================*/
127 int map_driver(label, major, proc_nr_e, style, flags)
128 const char label[LABEL_MAX]; /* name of the driver */
129 int major; /* major number of the device */
130 endpoint_t proc_nr_e; /* process number of the driver */
131 int style; /* style of the device */
132 int flags; /* device flags */
134 /* Add a new device driver mapping in the dmap table. If the proc_nr is set to
135 * NONE, we're supposed to unmap it.
138 int slot, s;
139 size_t len;
140 struct dmap *dp;
142 /* Get pointer to device entry in the dmap table. */
143 if (major < 0 || major >= NR_DEVICES) return(ENODEV);
144 dp = &dmap[major];
146 /* Check if we're supposed to unmap it. */
147 if (proc_nr_e == NONE) {
148 /* Even when a driver is now unmapped and is shortly to be mapped in
149 * due to recovery, invalidate associated filps if they're character
150 * special files. More sophisticated recovery mechanisms which would
151 * reduce the need to invalidate files are possible, but would require
152 * cooperation of the driver and more recovery framework between RS,
153 * VFS, and DS.
155 invalidate_filp_by_char_major(major);
156 dp->dmap_opcl = no_dev;
157 dp->dmap_io = no_dev_io;
158 dp->dmap_driver = NONE;
159 dp->dmap_flags = flags;
160 dp->dmap_lock_ref = &dp->dmap_lock;
161 return(OK);
164 /* Check process number of new driver if it was alive before mapping */
165 s = isokendpt(proc_nr_e, &slot);
166 if (s != OK) {
167 /* This is not a problem only when we force this driver mapping */
168 if (! (flags & DRV_FORCED))
169 return(EINVAL);
172 if (label != NULL) {
173 len = strlen(label);
174 if (len+1 > sizeof(dp->dmap_label))
175 panic("VFS: map_driver: label too long: %d", len);
176 strlcpy(dp->dmap_label, label, LABEL_MAX);
179 /* Store driver I/O routines based on type of device */
180 switch (style) {
181 case STYLE_DEV:
182 dp->dmap_opcl = gen_opcl;
183 dp->dmap_io = gen_io;
184 break;
185 case STYLE_DEVA:
186 dp->dmap_opcl = gen_opcl;
187 dp->dmap_io = asyn_io;
188 break;
189 case STYLE_TTY:
190 dp->dmap_opcl = tty_opcl;
191 dp->dmap_io = gen_io;
192 break;
193 case STYLE_CTTY:
194 dp->dmap_opcl = ctty_opcl;
195 dp->dmap_io = ctty_io;
196 break;
197 case STYLE_CLONE:
198 dp->dmap_opcl = clone_opcl;
199 dp->dmap_io = gen_io;
200 break;
201 case STYLE_CLONE_A:
202 dp->dmap_opcl = clone_opcl;
203 dp->dmap_io = asyn_io;
204 break;
205 default:
206 return(EINVAL);
209 dp->dmap_driver = proc_nr_e;
210 dp->dmap_flags = flags;
211 dp->dmap_style = style;
213 return(OK);
216 /*===========================================================================*
217 * dmap_unmap_by_endpt *
218 *===========================================================================*/
219 void dmap_unmap_by_endpt(endpoint_t proc_e)
221 /* Lookup driver in dmap table by endpoint and unmap it */
222 int major, r;
224 for (major = 0; major < NR_DEVICES; major++) {
225 if (dmap_driver_match(proc_e, major)) {
226 /* Found driver; overwrite it with a NULL entry */
227 if ((r = map_driver(NULL, major, NONE, 0, 0)) != OK) {
228 printf("VFS: unmapping driver %d for major %d failed:"
229 " %d\n", proc_e, major, r);
235 /*===========================================================================*
236 * map_service *
237 *===========================================================================*/
238 int map_service(struct rprocpub *rpub)
240 /* Map a new service by storing its device driver properties. */
241 int r, slot;
242 struct dmap *fdp, *sdp;
243 struct fproc *rfp;
245 /* Process is a service */
246 if (isokendpt(rpub->endpoint, &slot) != OK) {
247 printf("VFS: can't map service with unknown endpoint %d\n",
248 rpub->endpoint);
249 return(EINVAL);
251 rfp = &fproc[slot];
252 rfp->fp_flags |= FP_SRV_PROC;
254 /* Not a driver, nothing more to do. */
255 if (rpub->dev_nr == NO_DEV) return(OK);
257 /* Map driver. */
258 r = map_driver(rpub->label, rpub->dev_nr, rpub->endpoint, rpub->dev_style,
259 rpub->dev_flags);
260 if(r != OK) return(r);
262 /* If driver has two major numbers associated, also map the other one. */
263 if(rpub->dev_style2 != STYLE_NDEV) {
264 r = map_driver(rpub->label, rpub->dev_nr+1, rpub->endpoint,
265 rpub->dev_style2, rpub->dev_flags);
266 if(r != OK) return(r);
268 /* To ensure that future dmap lock attempts always lock the same driver
269 * regardless of major number, refer the second dmap lock reference
270 * to the first dmap entry.
272 fdp = get_dmap_by_major(rpub->dev_nr);
273 sdp = get_dmap_by_major(rpub->dev_nr+1);
274 assert(fdp != NULL);
275 assert(sdp != NULL);
276 assert(fdp != sdp);
277 sdp->dmap_lock_ref = &fdp->dmap_lock;
280 return(OK);
283 /*===========================================================================*
284 * init_dmap *
285 *===========================================================================*/
286 void init_dmap()
288 /* Initialize the table with empty device <-> driver mappings. */
289 int i;
290 struct dmap dmap_default = DT_EMPTY;
292 for (i = 0; i < NR_DEVICES; i++)
293 dmap[i] = dmap_default;
296 /*===========================================================================*
297 * init_dmap_locks *
298 *===========================================================================*/
299 void init_dmap_locks()
301 int i;
303 for (i = 0; i < NR_DEVICES; i++) {
304 if (mutex_init(&dmap[i].dmap_lock, NULL) != 0)
305 panic("unable to initialize dmap lock");
306 dmap[i].dmap_lock_ref = &dmap[i].dmap_lock;
310 /*===========================================================================*
311 * dmap_driver_match *
312 *===========================================================================*/
313 int dmap_driver_match(endpoint_t proc, int major)
315 if (major < 0 || major >= NR_DEVICES) return(0);
316 if (dmap[major].dmap_driver != NONE && dmap[major].dmap_driver == proc)
317 return(1);
319 return(0);
322 /*===========================================================================*
323 * dmap_by_major *
324 *===========================================================================*/
325 struct dmap *
326 get_dmap_by_major(int major)
328 if (major < 0 || major >= NR_DEVICES) return(NULL);
329 if (dmap[major].dmap_driver == NONE) return(NULL);
330 return(&dmap[major]);
333 /*===========================================================================*
334 * dmap_endpt_up *
335 *===========================================================================*/
336 void dmap_endpt_up(endpoint_t proc_e, int is_blk)
338 /* A device driver with endpoint proc_e has been restarted. Go tell everyone
339 * that might be blocking on it that this device is 'up'.
342 int major;
343 struct dmap *dp;
344 struct worker_thread *worker;
346 if (proc_e == NONE) return;
348 for (major = 0; major < NR_DEVICES; major++) {
349 if ((dp = get_dmap_by_major(major)) == NULL) continue;
350 if (dp->dmap_driver == proc_e) {
351 if (is_blk) {
352 if (dp->dmap_recovering) {
353 printf("VFS: driver recovery failure for"
354 " major %d\n", major);
355 if (dp->dmap_servicing != NONE) {
356 worker = worker_get(dp->dmap_servicing);
357 worker_stop(worker);
359 dp->dmap_recovering = 0;
360 continue;
362 dp->dmap_recovering = 1;
363 bdev_up(major);
364 dp->dmap_recovering = 0;
365 } else {
366 if (dp->dmap_servicing != NONE) {
367 worker = worker_get(dp->dmap_servicing);
368 worker_stop(worker);
370 cdev_up(major);
376 /*===========================================================================*
377 * get_dmap *
378 *===========================================================================*/
379 struct dmap *get_dmap(endpoint_t proc_e)
381 /* See if 'proc_e' endpoint belongs to a valid dmap entry. If so, return a
382 * pointer */
384 int major;
385 for (major = 0; major < NR_DEVICES; major++)
386 if (dmap_driver_match(proc_e, major))
387 return(&dmap[major]);
389 return(NULL);