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