xref: /minix3/minix/servers/vfs/dmap.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc /* This file contains the table with device <-> driver mappings. It also
2*433d6423SLionel Sambuc  * contains some routines to dynamically add and/ or remove device drivers
3*433d6423SLionel Sambuc  * or change mappings.
4*433d6423SLionel Sambuc  */
5*433d6423SLionel Sambuc 
6*433d6423SLionel Sambuc #include "fs.h"
7*433d6423SLionel Sambuc #include <assert.h>
8*433d6423SLionel Sambuc #include <string.h>
9*433d6423SLionel Sambuc #include <stdlib.h>
10*433d6423SLionel Sambuc #include <ctype.h>
11*433d6423SLionel Sambuc #include <unistd.h>
12*433d6423SLionel Sambuc #include <minix/callnr.h>
13*433d6423SLionel Sambuc #include <minix/ds.h>
14*433d6423SLionel Sambuc 
15*433d6423SLionel Sambuc /* The order of the entries in the table determines the mapping between major
16*433d6423SLionel Sambuc  * device numbers and device drivers. Character and block devices
17*433d6423SLionel Sambuc  * can be intermixed at random.  The ordering determines the device numbers in
18*433d6423SLionel Sambuc  * /dev. Note that the major device numbers used in /dev are NOT the same as
19*433d6423SLionel Sambuc  * the process numbers of the device drivers. See <minix/dmap.h> for mappings.
20*433d6423SLionel Sambuc  */
21*433d6423SLionel Sambuc 
22*433d6423SLionel Sambuc struct dmap dmap[NR_DEVICES];
23*433d6423SLionel Sambuc 
24*433d6423SLionel Sambuc /*===========================================================================*
25*433d6423SLionel Sambuc  *				lock_dmap		 		     *
26*433d6423SLionel Sambuc  *===========================================================================*/
27*433d6423SLionel Sambuc void lock_dmap(struct dmap *dp)
28*433d6423SLionel Sambuc {
29*433d6423SLionel Sambuc /* Lock a driver */
30*433d6423SLionel Sambuc 	struct worker_thread *org_self;
31*433d6423SLionel Sambuc 	int r;
32*433d6423SLionel Sambuc 
33*433d6423SLionel Sambuc 	assert(dp != NULL);
34*433d6423SLionel Sambuc 	assert(dp->dmap_driver != NONE);
35*433d6423SLionel Sambuc 
36*433d6423SLionel Sambuc 	org_self = worker_suspend();
37*433d6423SLionel Sambuc 
38*433d6423SLionel Sambuc 	if ((r = mutex_lock(&dp->dmap_lock)) != 0)
39*433d6423SLionel Sambuc 		panic("unable to get a lock on dmap: %d\n", r);
40*433d6423SLionel Sambuc 
41*433d6423SLionel Sambuc 	worker_resume(org_self);
42*433d6423SLionel Sambuc }
43*433d6423SLionel Sambuc 
44*433d6423SLionel Sambuc /*===========================================================================*
45*433d6423SLionel Sambuc  *				unlock_dmap		 		     *
46*433d6423SLionel Sambuc  *===========================================================================*/
47*433d6423SLionel Sambuc void unlock_dmap(struct dmap *dp)
48*433d6423SLionel Sambuc {
49*433d6423SLionel Sambuc /* Unlock a driver */
50*433d6423SLionel Sambuc 	int r;
51*433d6423SLionel Sambuc 
52*433d6423SLionel Sambuc 	assert(dp != NULL);
53*433d6423SLionel Sambuc 
54*433d6423SLionel Sambuc 	if ((r = mutex_unlock(&dp->dmap_lock)) != 0)
55*433d6423SLionel Sambuc 		panic("unable to unlock dmap lock: %d\n", r);
56*433d6423SLionel Sambuc }
57*433d6423SLionel Sambuc 
58*433d6423SLionel Sambuc /*===========================================================================*
59*433d6423SLionel Sambuc  *				map_driver		 		     *
60*433d6423SLionel Sambuc  *===========================================================================*/
61*433d6423SLionel Sambuc static int map_driver(const char label[LABEL_MAX], devmajor_t major,
62*433d6423SLionel Sambuc 	endpoint_t proc_nr_e)
63*433d6423SLionel Sambuc {
64*433d6423SLionel Sambuc /* Add a new device driver mapping in the dmap table. If the proc_nr is set to
65*433d6423SLionel Sambuc  * NONE, we're supposed to unmap it.
66*433d6423SLionel Sambuc  */
67*433d6423SLionel Sambuc   size_t len;
68*433d6423SLionel Sambuc   struct dmap *dp;
69*433d6423SLionel Sambuc 
70*433d6423SLionel Sambuc   /* Get pointer to device entry in the dmap table. */
71*433d6423SLionel Sambuc   if (major < 0 || major >= NR_DEVICES) return(ENODEV);
72*433d6423SLionel Sambuc   dp = &dmap[major];
73*433d6423SLionel Sambuc 
74*433d6423SLionel Sambuc   /* Check if we're supposed to unmap it. */
75*433d6423SLionel Sambuc  if (proc_nr_e == NONE) {
76*433d6423SLionel Sambuc 	/* Even when a driver is now unmapped and is shortly to be mapped in
77*433d6423SLionel Sambuc 	 * due to recovery, invalidate associated filps if they're character
78*433d6423SLionel Sambuc 	 * special files. More sophisticated recovery mechanisms which would
79*433d6423SLionel Sambuc 	 * reduce the need to invalidate files are possible, but would require
80*433d6423SLionel Sambuc 	 * cooperation of the driver and more recovery framework between RS,
81*433d6423SLionel Sambuc 	 * VFS, and DS.
82*433d6423SLionel Sambuc 	 */
83*433d6423SLionel Sambuc 	invalidate_filp_by_char_major(major);
84*433d6423SLionel Sambuc 	dp->dmap_driver = NONE;
85*433d6423SLionel Sambuc 	return(OK);
86*433d6423SLionel Sambuc   }
87*433d6423SLionel Sambuc 
88*433d6423SLionel Sambuc   if (label != NULL) {
89*433d6423SLionel Sambuc 	len = strlen(label);
90*433d6423SLionel Sambuc 	if (len+1 > sizeof(dp->dmap_label)) {
91*433d6423SLionel Sambuc 		printf("VFS: map_driver: label too long: %d\n", len);
92*433d6423SLionel Sambuc 		return(EINVAL);
93*433d6423SLionel Sambuc 	}
94*433d6423SLionel Sambuc 	strlcpy(dp->dmap_label, label, sizeof(dp->dmap_label));
95*433d6423SLionel Sambuc   }
96*433d6423SLionel Sambuc 
97*433d6423SLionel Sambuc   /* Store driver I/O routines based on type of device */
98*433d6423SLionel Sambuc   dp->dmap_driver = proc_nr_e;
99*433d6423SLionel Sambuc 
100*433d6423SLionel Sambuc   return(OK);
101*433d6423SLionel Sambuc }
102*433d6423SLionel Sambuc 
103*433d6423SLionel Sambuc /*===========================================================================*
104*433d6423SLionel Sambuc  *				do_mapdriver		 		     *
105*433d6423SLionel Sambuc  *===========================================================================*/
106*433d6423SLionel Sambuc int do_mapdriver(void)
107*433d6423SLionel Sambuc {
108*433d6423SLionel Sambuc /* Create a device->driver mapping. RS will tell us which major is driven by
109*433d6423SLionel Sambuc  * this driver, what type of device it is (regular, TTY, asynchronous, clone,
110*433d6423SLionel Sambuc  * etc), and its label. This label is registered with DS, and allows us to
111*433d6423SLionel Sambuc  * retrieve the driver's endpoint.
112*433d6423SLionel Sambuc  */
113*433d6423SLionel Sambuc   int r, major, slot;
114*433d6423SLionel Sambuc   endpoint_t endpoint;
115*433d6423SLionel Sambuc   vir_bytes label_vir;
116*433d6423SLionel Sambuc   size_t label_len;
117*433d6423SLionel Sambuc   char label[LABEL_MAX];
118*433d6423SLionel Sambuc   struct fproc *rfp;
119*433d6423SLionel Sambuc 
120*433d6423SLionel Sambuc   /* Only RS can map drivers. */
121*433d6423SLionel Sambuc   if (who_e != RS_PROC_NR) return(EPERM);
122*433d6423SLionel Sambuc 
123*433d6423SLionel Sambuc   label_vir = job_m_in.m_lsys_vfs_mapdriver.label;
124*433d6423SLionel Sambuc   label_len = job_m_in.m_lsys_vfs_mapdriver.labellen;
125*433d6423SLionel Sambuc   major = job_m_in.m_lsys_vfs_mapdriver.major;
126*433d6423SLionel Sambuc 
127*433d6423SLionel Sambuc   /* Get the label */
128*433d6423SLionel Sambuc   if (label_len > sizeof(label)) { /* Can we store this label? */
129*433d6423SLionel Sambuc 	printf("VFS: do_mapdriver: label too long\n");
130*433d6423SLionel Sambuc 	return(EINVAL);
131*433d6423SLionel Sambuc   }
132*433d6423SLionel Sambuc   r = sys_vircopy(who_e, label_vir, SELF, (vir_bytes) label, label_len,
133*433d6423SLionel Sambuc 	CP_FLAG_TRY);
134*433d6423SLionel Sambuc   if (r != OK) {
135*433d6423SLionel Sambuc 	printf("VFS: do_mapdriver: sys_vircopy failed: %d\n", r);
136*433d6423SLionel Sambuc 	return(EINVAL);
137*433d6423SLionel Sambuc   }
138*433d6423SLionel Sambuc   if (label[label_len-1] != '\0') {
139*433d6423SLionel Sambuc 	printf("VFS: do_mapdriver: label not null-terminated\n");
140*433d6423SLionel Sambuc 	return(EINVAL);
141*433d6423SLionel Sambuc   }
142*433d6423SLionel Sambuc 
143*433d6423SLionel Sambuc   /* Now we know how the driver is called, fetch its endpoint */
144*433d6423SLionel Sambuc   r = ds_retrieve_label_endpt(label, &endpoint);
145*433d6423SLionel Sambuc   if (r != OK) {
146*433d6423SLionel Sambuc 	printf("VFS: do_mapdriver: label '%s' unknown\n", label);
147*433d6423SLionel Sambuc 	return(EINVAL);
148*433d6423SLionel Sambuc   }
149*433d6423SLionel Sambuc 
150*433d6423SLionel Sambuc   /* Process is a service */
151*433d6423SLionel Sambuc   if (isokendpt(endpoint, &slot) != OK) {
152*433d6423SLionel Sambuc 	printf("VFS: can't map driver to unknown endpoint %d\n", endpoint);
153*433d6423SLionel Sambuc 	return(EINVAL);
154*433d6423SLionel Sambuc   }
155*433d6423SLionel Sambuc   rfp = &fproc[slot];
156*433d6423SLionel Sambuc   rfp->fp_flags |= FP_SRV_PROC;
157*433d6423SLionel Sambuc 
158*433d6423SLionel Sambuc   /* Try to update device mapping. */
159*433d6423SLionel Sambuc   return map_driver(label, major, endpoint);
160*433d6423SLionel Sambuc }
161*433d6423SLionel Sambuc 
162*433d6423SLionel Sambuc /*===========================================================================*
163*433d6423SLionel Sambuc  *				dmap_unmap_by_endpt	 		     *
164*433d6423SLionel Sambuc  *===========================================================================*/
165*433d6423SLionel Sambuc void dmap_unmap_by_endpt(endpoint_t proc_e)
166*433d6423SLionel Sambuc {
167*433d6423SLionel Sambuc /* Lookup driver in dmap table by endpoint and unmap it */
168*433d6423SLionel Sambuc   int major, r;
169*433d6423SLionel Sambuc 
170*433d6423SLionel Sambuc   for (major = 0; major < NR_DEVICES; major++) {
171*433d6423SLionel Sambuc 	if (dmap_driver_match(proc_e, major)) {
172*433d6423SLionel Sambuc 		/* Found driver; overwrite it with a NULL entry */
173*433d6423SLionel Sambuc 		if ((r = map_driver(NULL, major, NONE)) != OK) {
174*433d6423SLionel Sambuc 			printf("VFS: unmapping driver %d for major %d failed:"
175*433d6423SLionel Sambuc 				" %d\n", proc_e, major, r);
176*433d6423SLionel Sambuc 		}
177*433d6423SLionel Sambuc 	}
178*433d6423SLionel Sambuc   }
179*433d6423SLionel Sambuc }
180*433d6423SLionel Sambuc 
181*433d6423SLionel Sambuc /*===========================================================================*
182*433d6423SLionel Sambuc  *		               map_service				     *
183*433d6423SLionel Sambuc  *===========================================================================*/
184*433d6423SLionel Sambuc int map_service(struct rprocpub *rpub)
185*433d6423SLionel Sambuc {
186*433d6423SLionel Sambuc /* Map a new service by storing its device driver properties. */
187*433d6423SLionel Sambuc   int r, slot;
188*433d6423SLionel Sambuc   struct fproc *rfp;
189*433d6423SLionel Sambuc 
190*433d6423SLionel Sambuc   if (IS_RPUB_BOOT_USR(rpub)) return(OK);
191*433d6423SLionel Sambuc 
192*433d6423SLionel Sambuc   /* Process is a service */
193*433d6423SLionel Sambuc   if (isokendpt(rpub->endpoint, &slot) != OK) {
194*433d6423SLionel Sambuc 	printf("VFS: can't map service with unknown endpoint %d\n",
195*433d6423SLionel Sambuc 		rpub->endpoint);
196*433d6423SLionel Sambuc 	return(EINVAL);
197*433d6423SLionel Sambuc   }
198*433d6423SLionel Sambuc   rfp = &fproc[slot];
199*433d6423SLionel Sambuc   rfp->fp_flags |= FP_SRV_PROC;
200*433d6423SLionel Sambuc 
201*433d6423SLionel Sambuc   /* Not a driver, nothing more to do. */
202*433d6423SLionel Sambuc   if (rpub->dev_nr == NO_DEV) return(OK);
203*433d6423SLionel Sambuc 
204*433d6423SLionel Sambuc   /* Map driver. */
205*433d6423SLionel Sambuc   r = map_driver(rpub->label, rpub->dev_nr, rpub->endpoint);
206*433d6423SLionel Sambuc   if(r != OK) return(r);
207*433d6423SLionel Sambuc 
208*433d6423SLionel Sambuc   return(OK);
209*433d6423SLionel Sambuc }
210*433d6423SLionel Sambuc 
211*433d6423SLionel Sambuc /*===========================================================================*
212*433d6423SLionel Sambuc  *				init_dmap		 		     *
213*433d6423SLionel Sambuc  *===========================================================================*/
214*433d6423SLionel Sambuc void init_dmap(void)
215*433d6423SLionel Sambuc {
216*433d6423SLionel Sambuc /* Initialize the device mapping table. */
217*433d6423SLionel Sambuc   int i;
218*433d6423SLionel Sambuc 
219*433d6423SLionel Sambuc   memset(dmap, 0, sizeof(dmap));
220*433d6423SLionel Sambuc 
221*433d6423SLionel Sambuc   for (i = 0; i < NR_DEVICES; i++) {
222*433d6423SLionel Sambuc 	dmap[i].dmap_driver = NONE;
223*433d6423SLionel Sambuc 	dmap[i].dmap_servicing = INVALID_THREAD;
224*433d6423SLionel Sambuc 	if (mutex_init(&dmap[i].dmap_lock, NULL) != 0)
225*433d6423SLionel Sambuc 		panic("unable to initialize dmap lock");
226*433d6423SLionel Sambuc   }
227*433d6423SLionel Sambuc 
228*433d6423SLionel Sambuc   /* CTTY_MAJOR is a special case, which is handled by VFS itself. */
229*433d6423SLionel Sambuc   if (map_driver("vfs", CTTY_MAJOR, CTTY_ENDPT) != OK)
230*433d6423SLionel Sambuc 	panic("map_driver(CTTY_MAJOR) failed");
231*433d6423SLionel Sambuc }
232*433d6423SLionel Sambuc 
233*433d6423SLionel Sambuc /*===========================================================================*
234*433d6423SLionel Sambuc  *				dmap_driver_match	 		     *
235*433d6423SLionel Sambuc  *===========================================================================*/
236*433d6423SLionel Sambuc int dmap_driver_match(endpoint_t proc, int major)
237*433d6423SLionel Sambuc {
238*433d6423SLionel Sambuc   if (major < 0 || major >= NR_DEVICES) return(0);
239*433d6423SLionel Sambuc   if (dmap[major].dmap_driver != NONE && dmap[major].dmap_driver == proc)
240*433d6423SLionel Sambuc 	return(1);
241*433d6423SLionel Sambuc 
242*433d6423SLionel Sambuc   return(0);
243*433d6423SLionel Sambuc }
244*433d6423SLionel Sambuc 
245*433d6423SLionel Sambuc /*===========================================================================*
246*433d6423SLionel Sambuc  *				dmap_by_major		 		     *
247*433d6423SLionel Sambuc  *===========================================================================*/
248*433d6423SLionel Sambuc struct dmap *
249*433d6423SLionel Sambuc get_dmap_by_major(int major)
250*433d6423SLionel Sambuc {
251*433d6423SLionel Sambuc 	if (major < 0 || major >= NR_DEVICES) return(NULL);
252*433d6423SLionel Sambuc 	if (dmap[major].dmap_driver == NONE) return(NULL);
253*433d6423SLionel Sambuc 	return(&dmap[major]);
254*433d6423SLionel Sambuc }
255*433d6423SLionel Sambuc 
256*433d6423SLionel Sambuc /*===========================================================================*
257*433d6423SLionel Sambuc  *				dmap_endpt_up		 		     *
258*433d6423SLionel Sambuc  *===========================================================================*/
259*433d6423SLionel Sambuc void dmap_endpt_up(endpoint_t proc_e, int is_blk)
260*433d6423SLionel Sambuc {
261*433d6423SLionel Sambuc /* A device driver with endpoint proc_e has been restarted. Go tell everyone
262*433d6423SLionel Sambuc  * that might be blocking on it that this device is 'up'.
263*433d6423SLionel Sambuc  */
264*433d6423SLionel Sambuc 
265*433d6423SLionel Sambuc   int major;
266*433d6423SLionel Sambuc   struct dmap *dp;
267*433d6423SLionel Sambuc   struct worker_thread *worker;
268*433d6423SLionel Sambuc 
269*433d6423SLionel Sambuc   if (proc_e == NONE) return;
270*433d6423SLionel Sambuc 
271*433d6423SLionel Sambuc   for (major = 0; major < NR_DEVICES; major++) {
272*433d6423SLionel Sambuc 	if ((dp = get_dmap_by_major(major)) == NULL) continue;
273*433d6423SLionel Sambuc 	if (dp->dmap_driver == proc_e) {
274*433d6423SLionel Sambuc 		if (is_blk) {
275*433d6423SLionel Sambuc 			if (dp->dmap_recovering) {
276*433d6423SLionel Sambuc 				printf("VFS: driver recovery failure for"
277*433d6423SLionel Sambuc 					" major %d\n", major);
278*433d6423SLionel Sambuc 				if (dp->dmap_servicing != INVALID_THREAD) {
279*433d6423SLionel Sambuc 					worker = worker_get(dp->dmap_servicing);
280*433d6423SLionel Sambuc 					worker_stop(worker);
281*433d6423SLionel Sambuc 				}
282*433d6423SLionel Sambuc 				dp->dmap_recovering = 0;
283*433d6423SLionel Sambuc 				continue;
284*433d6423SLionel Sambuc 			}
285*433d6423SLionel Sambuc 			dp->dmap_recovering = 1;
286*433d6423SLionel Sambuc 			bdev_up(major);
287*433d6423SLionel Sambuc 			dp->dmap_recovering = 0;
288*433d6423SLionel Sambuc 		} else {
289*433d6423SLionel Sambuc 			if (dp->dmap_servicing != INVALID_THREAD) {
290*433d6423SLionel Sambuc 				worker = worker_get(dp->dmap_servicing);
291*433d6423SLionel Sambuc 				worker_stop(worker);
292*433d6423SLionel Sambuc 			}
293*433d6423SLionel Sambuc 			invalidate_filp_by_char_major(major);
294*433d6423SLionel Sambuc 		}
295*433d6423SLionel Sambuc 	}
296*433d6423SLionel Sambuc   }
297*433d6423SLionel Sambuc }
298*433d6423SLionel Sambuc 
299*433d6423SLionel Sambuc /*===========================================================================*
300*433d6423SLionel Sambuc  *				get_dmap		 		     *
301*433d6423SLionel Sambuc  *===========================================================================*/
302*433d6423SLionel Sambuc struct dmap *get_dmap(endpoint_t proc_e)
303*433d6423SLionel Sambuc {
304*433d6423SLionel Sambuc /* See if 'proc_e' endpoint belongs to a valid dmap entry. If so, return a
305*433d6423SLionel Sambuc  * pointer */
306*433d6423SLionel Sambuc 
307*433d6423SLionel Sambuc   int major;
308*433d6423SLionel Sambuc   for (major = 0; major < NR_DEVICES; major++)
309*433d6423SLionel Sambuc 	if (dmap_driver_match(proc_e, major))
310*433d6423SLionel Sambuc 		return(&dmap[major]);
311*433d6423SLionel Sambuc 
312*433d6423SLionel Sambuc   return(NULL);
313*433d6423SLionel Sambuc }
314