xref: /minix3/minix/servers/vfs/dmap.c (revision 3c8950cce94bbea7236b2d07ae8e59f07e4432c5)
1433d6423SLionel Sambuc /* This file contains the table with device <-> driver mappings. It also
2433d6423SLionel Sambuc  * contains some routines to dynamically add and/ or remove device drivers
3433d6423SLionel Sambuc  * or change mappings.
4433d6423SLionel Sambuc  */
5433d6423SLionel Sambuc 
6433d6423SLionel Sambuc #include "fs.h"
7433d6423SLionel Sambuc #include <assert.h>
8433d6423SLionel Sambuc #include <string.h>
9433d6423SLionel Sambuc #include <stdlib.h>
10433d6423SLionel Sambuc #include <ctype.h>
11433d6423SLionel Sambuc #include <unistd.h>
12433d6423SLionel Sambuc #include <minix/callnr.h>
13433d6423SLionel Sambuc #include <minix/ds.h>
14433d6423SLionel Sambuc 
15433d6423SLionel Sambuc /* The order of the entries in the table determines the mapping between major
16433d6423SLionel Sambuc  * device numbers and device drivers. Character and block devices
17433d6423SLionel Sambuc  * can be intermixed at random.  The ordering determines the device numbers in
18433d6423SLionel Sambuc  * /dev. Note that the major device numbers used in /dev are NOT the same as
19433d6423SLionel Sambuc  * the process numbers of the device drivers. See <minix/dmap.h> for mappings.
20433d6423SLionel Sambuc  */
21433d6423SLionel Sambuc 
22433d6423SLionel Sambuc struct dmap dmap[NR_DEVICES];
23433d6423SLionel Sambuc 
24433d6423SLionel Sambuc /*===========================================================================*
25433d6423SLionel Sambuc  *				lock_dmap		 		     *
26433d6423SLionel Sambuc  *===========================================================================*/
27433d6423SLionel Sambuc void lock_dmap(struct dmap *dp)
28433d6423SLionel Sambuc {
29433d6423SLionel Sambuc /* Lock a driver */
30433d6423SLionel Sambuc 	struct worker_thread *org_self;
31433d6423SLionel Sambuc 	int r;
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc 	assert(dp != NULL);
34433d6423SLionel Sambuc 	assert(dp->dmap_driver != NONE);
35433d6423SLionel Sambuc 
36433d6423SLionel Sambuc 	org_self = worker_suspend();
37433d6423SLionel Sambuc 
38433d6423SLionel Sambuc 	if ((r = mutex_lock(&dp->dmap_lock)) != 0)
39433d6423SLionel Sambuc 		panic("unable to get a lock on dmap: %d\n", r);
40433d6423SLionel Sambuc 
41433d6423SLionel Sambuc 	worker_resume(org_self);
42433d6423SLionel Sambuc }
43433d6423SLionel Sambuc 
44433d6423SLionel Sambuc /*===========================================================================*
45433d6423SLionel Sambuc  *				unlock_dmap		 		     *
46433d6423SLionel Sambuc  *===========================================================================*/
47433d6423SLionel Sambuc void unlock_dmap(struct dmap *dp)
48433d6423SLionel Sambuc {
49433d6423SLionel Sambuc /* Unlock a driver */
50433d6423SLionel Sambuc 	int r;
51433d6423SLionel Sambuc 
52433d6423SLionel Sambuc 	assert(dp != NULL);
53433d6423SLionel Sambuc 
54433d6423SLionel Sambuc 	if ((r = mutex_unlock(&dp->dmap_lock)) != 0)
55433d6423SLionel Sambuc 		panic("unable to unlock dmap lock: %d\n", r);
56433d6423SLionel Sambuc }
57433d6423SLionel Sambuc 
58433d6423SLionel Sambuc /*===========================================================================*
59433d6423SLionel Sambuc  *				map_driver		 		     *
60433d6423SLionel Sambuc  *===========================================================================*/
61433d6423SLionel Sambuc static int map_driver(const char label[LABEL_MAX], devmajor_t major,
62433d6423SLionel Sambuc 	endpoint_t proc_nr_e)
63433d6423SLionel Sambuc {
64433d6423SLionel Sambuc /* Add a new device driver mapping in the dmap table. If the proc_nr is set to
65433d6423SLionel Sambuc  * NONE, we're supposed to unmap it.
66433d6423SLionel Sambuc  */
67433d6423SLionel Sambuc   size_t len;
68433d6423SLionel Sambuc   struct dmap *dp;
69433d6423SLionel Sambuc 
70433d6423SLionel Sambuc   /* Get pointer to device entry in the dmap table. */
71433d6423SLionel Sambuc   if (major < 0 || major >= NR_DEVICES) return(ENODEV);
72433d6423SLionel Sambuc   dp = &dmap[major];
73433d6423SLionel Sambuc 
74433d6423SLionel Sambuc   /* Check if we're supposed to unmap it. */
75433d6423SLionel Sambuc  if (proc_nr_e == NONE) {
76433d6423SLionel Sambuc 	/* Even when a driver is now unmapped and is shortly to be mapped in
77433d6423SLionel Sambuc 	 * due to recovery, invalidate associated filps if they're character
78433d6423SLionel Sambuc 	 * special files. More sophisticated recovery mechanisms which would
79433d6423SLionel Sambuc 	 * reduce the need to invalidate files are possible, but would require
80433d6423SLionel Sambuc 	 * cooperation of the driver and more recovery framework between RS,
81433d6423SLionel Sambuc 	 * VFS, and DS.
82433d6423SLionel Sambuc 	 */
83433d6423SLionel Sambuc 	invalidate_filp_by_char_major(major);
84433d6423SLionel Sambuc 	dp->dmap_driver = NONE;
85433d6423SLionel Sambuc 	return(OK);
86433d6423SLionel Sambuc   }
87433d6423SLionel Sambuc 
88433d6423SLionel Sambuc   if (label != NULL) {
89433d6423SLionel Sambuc 	len = strlen(label);
90433d6423SLionel Sambuc 	if (len+1 > sizeof(dp->dmap_label)) {
91*3c8950ccSBen Gras 		printf("VFS: map_driver: label too long: %zu\n", len);
92433d6423SLionel Sambuc 		return(EINVAL);
93433d6423SLionel Sambuc 	}
94433d6423SLionel Sambuc 	strlcpy(dp->dmap_label, label, sizeof(dp->dmap_label));
95433d6423SLionel Sambuc   }
96433d6423SLionel Sambuc 
97433d6423SLionel Sambuc   /* Store driver I/O routines based on type of device */
98433d6423SLionel Sambuc   dp->dmap_driver = proc_nr_e;
99433d6423SLionel Sambuc 
100433d6423SLionel Sambuc   return(OK);
101433d6423SLionel Sambuc }
102433d6423SLionel Sambuc 
103433d6423SLionel Sambuc /*===========================================================================*
104433d6423SLionel Sambuc  *				do_mapdriver		 		     *
105433d6423SLionel Sambuc  *===========================================================================*/
106433d6423SLionel Sambuc int do_mapdriver(void)
107433d6423SLionel Sambuc {
108433d6423SLionel Sambuc /* Create a device->driver mapping. RS will tell us which major is driven by
109433d6423SLionel Sambuc  * this driver, what type of device it is (regular, TTY, asynchronous, clone,
110433d6423SLionel Sambuc  * etc), and its label. This label is registered with DS, and allows us to
111433d6423SLionel Sambuc  * retrieve the driver's endpoint.
112433d6423SLionel Sambuc  */
1133b468884SDavid van Moolenbroek   int r, slot;
1143b468884SDavid van Moolenbroek   devmajor_t major;
115433d6423SLionel Sambuc   endpoint_t endpoint;
116433d6423SLionel Sambuc   vir_bytes label_vir;
117433d6423SLionel Sambuc   size_t label_len;
118433d6423SLionel Sambuc   char label[LABEL_MAX];
119433d6423SLionel Sambuc   struct fproc *rfp;
120433d6423SLionel Sambuc 
121433d6423SLionel Sambuc   /* Only RS can map drivers. */
122433d6423SLionel Sambuc   if (who_e != RS_PROC_NR) return(EPERM);
123433d6423SLionel Sambuc 
124433d6423SLionel Sambuc   label_vir = job_m_in.m_lsys_vfs_mapdriver.label;
125433d6423SLionel Sambuc   label_len = job_m_in.m_lsys_vfs_mapdriver.labellen;
126433d6423SLionel Sambuc   major = job_m_in.m_lsys_vfs_mapdriver.major;
127433d6423SLionel Sambuc 
128433d6423SLionel Sambuc   /* Get the label */
129433d6423SLionel Sambuc   if (label_len > sizeof(label)) { /* Can we store this label? */
130433d6423SLionel Sambuc 	printf("VFS: do_mapdriver: label too long\n");
131433d6423SLionel Sambuc 	return(EINVAL);
132433d6423SLionel Sambuc   }
133433d6423SLionel Sambuc   r = sys_vircopy(who_e, label_vir, SELF, (vir_bytes) label, label_len,
134433d6423SLionel Sambuc 	CP_FLAG_TRY);
135433d6423SLionel Sambuc   if (r != OK) {
136433d6423SLionel Sambuc 	printf("VFS: do_mapdriver: sys_vircopy failed: %d\n", r);
137433d6423SLionel Sambuc 	return(EINVAL);
138433d6423SLionel Sambuc   }
139433d6423SLionel Sambuc   if (label[label_len-1] != '\0') {
140433d6423SLionel Sambuc 	printf("VFS: do_mapdriver: label not null-terminated\n");
141433d6423SLionel Sambuc 	return(EINVAL);
142433d6423SLionel Sambuc   }
143433d6423SLionel Sambuc 
144433d6423SLionel Sambuc   /* Now we know how the driver is called, fetch its endpoint */
145433d6423SLionel Sambuc   r = ds_retrieve_label_endpt(label, &endpoint);
146433d6423SLionel Sambuc   if (r != OK) {
147433d6423SLionel Sambuc 	printf("VFS: do_mapdriver: label '%s' unknown\n", label);
148433d6423SLionel Sambuc 	return(EINVAL);
149433d6423SLionel Sambuc   }
150433d6423SLionel Sambuc 
151433d6423SLionel Sambuc   /* Process is a service */
152433d6423SLionel Sambuc   if (isokendpt(endpoint, &slot) != OK) {
153433d6423SLionel Sambuc 	printf("VFS: can't map driver to unknown endpoint %d\n", endpoint);
154433d6423SLionel Sambuc 	return(EINVAL);
155433d6423SLionel Sambuc   }
156433d6423SLionel Sambuc   rfp = &fproc[slot];
157433d6423SLionel Sambuc   rfp->fp_flags |= FP_SRV_PROC;
158433d6423SLionel Sambuc 
159433d6423SLionel Sambuc   /* Try to update device mapping. */
160433d6423SLionel Sambuc   return map_driver(label, major, endpoint);
161433d6423SLionel Sambuc }
162433d6423SLionel Sambuc 
163433d6423SLionel Sambuc /*===========================================================================*
164433d6423SLionel Sambuc  *				dmap_unmap_by_endpt	 		     *
165433d6423SLionel Sambuc  *===========================================================================*/
166433d6423SLionel Sambuc void dmap_unmap_by_endpt(endpoint_t proc_e)
167433d6423SLionel Sambuc {
168433d6423SLionel Sambuc /* Lookup driver in dmap table by endpoint and unmap it */
1693b468884SDavid van Moolenbroek   devmajor_t major;
1703b468884SDavid van Moolenbroek   int r;
171433d6423SLionel Sambuc 
172433d6423SLionel Sambuc   for (major = 0; major < NR_DEVICES; major++) {
173433d6423SLionel Sambuc 	if (dmap_driver_match(proc_e, major)) {
174433d6423SLionel Sambuc 		/* Found driver; overwrite it with a NULL entry */
175433d6423SLionel Sambuc 		if ((r = map_driver(NULL, major, NONE)) != OK) {
176433d6423SLionel Sambuc 			printf("VFS: unmapping driver %d for major %d failed:"
177433d6423SLionel Sambuc 				" %d\n", proc_e, major, r);
178433d6423SLionel Sambuc 		}
179433d6423SLionel Sambuc 	}
180433d6423SLionel Sambuc   }
181433d6423SLionel Sambuc }
182433d6423SLionel Sambuc 
183433d6423SLionel Sambuc /*===========================================================================*
184433d6423SLionel Sambuc  *		               map_service				     *
185433d6423SLionel Sambuc  *===========================================================================*/
186433d6423SLionel Sambuc int map_service(struct rprocpub *rpub)
187433d6423SLionel Sambuc {
188433d6423SLionel Sambuc /* Map a new service by storing its device driver properties. */
189433d6423SLionel Sambuc   int r, slot;
190433d6423SLionel Sambuc   struct fproc *rfp;
191433d6423SLionel Sambuc 
192433d6423SLionel Sambuc   if (IS_RPUB_BOOT_USR(rpub)) return(OK);
193433d6423SLionel Sambuc 
194433d6423SLionel Sambuc   /* Process is a service */
195433d6423SLionel Sambuc   if (isokendpt(rpub->endpoint, &slot) != OK) {
196433d6423SLionel Sambuc 	printf("VFS: can't map service with unknown endpoint %d\n",
197433d6423SLionel Sambuc 		rpub->endpoint);
198433d6423SLionel Sambuc 	return(EINVAL);
199433d6423SLionel Sambuc   }
200433d6423SLionel Sambuc   rfp = &fproc[slot];
201433d6423SLionel Sambuc   rfp->fp_flags |= FP_SRV_PROC;
202433d6423SLionel Sambuc 
203433d6423SLionel Sambuc   /* Not a driver, nothing more to do. */
204433d6423SLionel Sambuc   if (rpub->dev_nr == NO_DEV) return(OK);
205433d6423SLionel Sambuc 
206433d6423SLionel Sambuc   /* Map driver. */
207433d6423SLionel Sambuc   r = map_driver(rpub->label, rpub->dev_nr, rpub->endpoint);
208433d6423SLionel Sambuc   if(r != OK) return(r);
209433d6423SLionel Sambuc 
210433d6423SLionel Sambuc   return(OK);
211433d6423SLionel Sambuc }
212433d6423SLionel Sambuc 
213433d6423SLionel Sambuc /*===========================================================================*
214433d6423SLionel Sambuc  *				init_dmap		 		     *
215433d6423SLionel Sambuc  *===========================================================================*/
216433d6423SLionel Sambuc void init_dmap(void)
217433d6423SLionel Sambuc {
218433d6423SLionel Sambuc /* Initialize the device mapping table. */
219433d6423SLionel Sambuc   int i;
220433d6423SLionel Sambuc 
221433d6423SLionel Sambuc   memset(dmap, 0, sizeof(dmap));
222433d6423SLionel Sambuc 
223433d6423SLionel Sambuc   for (i = 0; i < NR_DEVICES; i++) {
224433d6423SLionel Sambuc 	dmap[i].dmap_driver = NONE;
225433d6423SLionel Sambuc 	dmap[i].dmap_servicing = INVALID_THREAD;
226433d6423SLionel Sambuc 	if (mutex_init(&dmap[i].dmap_lock, NULL) != 0)
227433d6423SLionel Sambuc 		panic("unable to initialize dmap lock");
228433d6423SLionel Sambuc   }
229433d6423SLionel Sambuc 
230433d6423SLionel Sambuc   /* CTTY_MAJOR is a special case, which is handled by VFS itself. */
231433d6423SLionel Sambuc   if (map_driver("vfs", CTTY_MAJOR, CTTY_ENDPT) != OK)
232433d6423SLionel Sambuc 	panic("map_driver(CTTY_MAJOR) failed");
233433d6423SLionel Sambuc }
234433d6423SLionel Sambuc 
235433d6423SLionel Sambuc /*===========================================================================*
236433d6423SLionel Sambuc  *				dmap_driver_match	 		     *
237433d6423SLionel Sambuc  *===========================================================================*/
2383b468884SDavid van Moolenbroek int dmap_driver_match(endpoint_t proc, devmajor_t major)
239433d6423SLionel Sambuc {
240433d6423SLionel Sambuc   if (major < 0 || major >= NR_DEVICES) return(0);
241433d6423SLionel Sambuc   if (dmap[major].dmap_driver != NONE && dmap[major].dmap_driver == proc)
242433d6423SLionel Sambuc 	return(1);
243433d6423SLionel Sambuc 
244433d6423SLionel Sambuc   return(0);
245433d6423SLionel Sambuc }
246433d6423SLionel Sambuc 
247433d6423SLionel Sambuc /*===========================================================================*
248433d6423SLionel Sambuc  *				dmap_by_major		 		     *
249433d6423SLionel Sambuc  *===========================================================================*/
250433d6423SLionel Sambuc struct dmap *
2513b468884SDavid van Moolenbroek get_dmap_by_major(devmajor_t major)
252433d6423SLionel Sambuc {
253433d6423SLionel Sambuc 	if (major < 0 || major >= NR_DEVICES) return(NULL);
254433d6423SLionel Sambuc 	if (dmap[major].dmap_driver == NONE) return(NULL);
255433d6423SLionel Sambuc 	return(&dmap[major]);
256433d6423SLionel Sambuc }
257433d6423SLionel Sambuc 
258433d6423SLionel Sambuc /*===========================================================================*
259433d6423SLionel Sambuc  *				dmap_endpt_up		 		     *
260433d6423SLionel Sambuc  *===========================================================================*/
261433d6423SLionel Sambuc void dmap_endpt_up(endpoint_t proc_e, int is_blk)
262433d6423SLionel Sambuc {
263433d6423SLionel Sambuc /* A device driver with endpoint proc_e has been restarted. Go tell everyone
264433d6423SLionel Sambuc  * that might be blocking on it that this device is 'up'.
265433d6423SLionel Sambuc  */
2663b468884SDavid van Moolenbroek   devmajor_t major;
267433d6423SLionel Sambuc   struct dmap *dp;
268433d6423SLionel Sambuc   struct worker_thread *worker;
269433d6423SLionel Sambuc 
270433d6423SLionel Sambuc   if (proc_e == NONE) return;
271433d6423SLionel Sambuc 
272433d6423SLionel Sambuc   for (major = 0; major < NR_DEVICES; major++) {
273433d6423SLionel Sambuc 	if ((dp = get_dmap_by_major(major)) == NULL) continue;
274433d6423SLionel Sambuc 	if (dp->dmap_driver == proc_e) {
275433d6423SLionel Sambuc 		if (is_blk) {
276433d6423SLionel Sambuc 			if (dp->dmap_recovering) {
277433d6423SLionel Sambuc 				printf("VFS: driver recovery failure for"
278433d6423SLionel Sambuc 					" major %d\n", major);
279433d6423SLionel Sambuc 				if (dp->dmap_servicing != INVALID_THREAD) {
280433d6423SLionel Sambuc 					worker = worker_get(dp->dmap_servicing);
281433d6423SLionel Sambuc 					worker_stop(worker);
282433d6423SLionel Sambuc 				}
283433d6423SLionel Sambuc 				dp->dmap_recovering = 0;
284433d6423SLionel Sambuc 				continue;
285433d6423SLionel Sambuc 			}
286433d6423SLionel Sambuc 			dp->dmap_recovering = 1;
287433d6423SLionel Sambuc 			bdev_up(major);
288433d6423SLionel Sambuc 			dp->dmap_recovering = 0;
289433d6423SLionel Sambuc 		} else {
290433d6423SLionel Sambuc 			if (dp->dmap_servicing != INVALID_THREAD) {
291433d6423SLionel Sambuc 				worker = worker_get(dp->dmap_servicing);
292433d6423SLionel Sambuc 				worker_stop(worker);
293433d6423SLionel Sambuc 			}
294433d6423SLionel Sambuc 			invalidate_filp_by_char_major(major);
295433d6423SLionel Sambuc 		}
296433d6423SLionel Sambuc 	}
297433d6423SLionel Sambuc   }
298433d6423SLionel Sambuc }
299433d6423SLionel Sambuc 
300433d6423SLionel Sambuc /*===========================================================================*
301433d6423SLionel Sambuc  *				get_dmap		 		     *
302433d6423SLionel Sambuc  *===========================================================================*/
303433d6423SLionel Sambuc struct dmap *get_dmap(endpoint_t proc_e)
304433d6423SLionel Sambuc {
305433d6423SLionel Sambuc /* See if 'proc_e' endpoint belongs to a valid dmap entry. If so, return a
306433d6423SLionel Sambuc  * pointer */
3073b468884SDavid van Moolenbroek   devmajor_t major;
308433d6423SLionel Sambuc 
309433d6423SLionel Sambuc   for (major = 0; major < NR_DEVICES; major++)
310433d6423SLionel Sambuc 	if (dmap_driver_match(proc_e, major))
311433d6423SLionel Sambuc 		return(&dmap[major]);
312433d6423SLionel Sambuc 
313433d6423SLionel Sambuc   return(NULL);
314433d6423SLionel Sambuc }
315