xref: /minix/minix/servers/vfs/dmap.c (revision e3b8d4bb)
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  *===========================================================================*/
lock_dmap(struct dmap * dp)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  *===========================================================================*/
unlock_dmap(struct dmap * dp)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  *===========================================================================*/
map_driver(const char label[LABEL_MAX],devmajor_t major,endpoint_t proc_nr_e)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: %zu\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  *===========================================================================*/
do_mapdriver(void)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   const int *domains;
114   int r, slot, ndomains;
115   devmajor_t major;
116   endpoint_t endpoint;
117   vir_bytes label_vir;
118   size_t label_len;
119   char label[LABEL_MAX];
120   struct fproc *rfp;
121 
122   /* Only RS can map drivers. */
123   if (who_e != RS_PROC_NR) return(EPERM);
124 
125   label_vir = job_m_in.m_lsys_vfs_mapdriver.label;
126   label_len = job_m_in.m_lsys_vfs_mapdriver.labellen;
127   major = job_m_in.m_lsys_vfs_mapdriver.major;
128   ndomains = job_m_in.m_lsys_vfs_mapdriver.ndomains;
129   domains = job_m_in.m_lsys_vfs_mapdriver.domains;
130 
131   /* Get the label */
132   if (label_len > sizeof(label)) { /* Can we store this label? */
133 	printf("VFS: do_mapdriver: label too long\n");
134 	return(EINVAL);
135   }
136   r = sys_vircopy(who_e, label_vir, SELF, (vir_bytes) label, label_len,
137 	CP_FLAG_TRY);
138   if (r != OK) {
139 	printf("VFS: do_mapdriver: sys_vircopy failed: %d\n", r);
140 	return(EINVAL);
141   }
142   if (label[label_len-1] != '\0') {
143 	printf("VFS: do_mapdriver: label not null-terminated\n");
144 	return(EINVAL);
145   }
146 
147   /* Now we know how the driver is called, fetch its endpoint */
148   r = ds_retrieve_label_endpt(label, &endpoint);
149   if (r != OK) {
150 	printf("VFS: do_mapdriver: label '%s' unknown\n", label);
151 	return(EINVAL);
152   }
153 
154   /* Process is a service */
155   if (isokendpt(endpoint, &slot) != OK) {
156 	printf("VFS: can't map driver to unknown endpoint %d\n", endpoint);
157 	return(EINVAL);
158   }
159   rfp = &fproc[slot];
160   rfp->fp_flags |= FP_SRV_PROC;
161 
162   /* Try to update device mapping. */
163   if (major != NO_DEV) {
164 	if ((r = map_driver(label, major, endpoint)) != OK)
165 		return r;
166   }
167   if (ndomains != 0) {
168 	if ((r = smap_map(label, endpoint, domains, ndomains)) != OK) {
169 		if (major != NO_DEV)
170 			map_driver(NULL, major, NONE); /* undo */
171 		return r;
172 	}
173   }
174   return OK;
175 }
176 
177 /*===========================================================================*
178  *				dmap_unmap_by_endpt	 		     *
179  *===========================================================================*/
dmap_unmap_by_endpt(endpoint_t proc_e)180 void dmap_unmap_by_endpt(endpoint_t proc_e)
181 {
182 /* Lookup driver in dmap table by endpoint and unmap it */
183   devmajor_t major;
184   int r;
185 
186   for (major = 0; major < NR_DEVICES; major++) {
187 	if (dmap_driver_match(proc_e, major)) {
188 		/* Found driver; overwrite it with a NULL entry */
189 		if ((r = map_driver(NULL, major, NONE)) != OK) {
190 			printf("VFS: unmapping driver %d for major %d failed:"
191 				" %d\n", proc_e, major, r);
192 		}
193 	}
194   }
195 }
196 
197 /*===========================================================================*
198  *		               map_service				     *
199  *===========================================================================*/
map_service(struct rprocpub * rpub)200 int map_service(struct rprocpub *rpub)
201 {
202 /* Map a new service by storing its device driver properties. */
203   int r, slot;
204   struct fproc *rfp;
205 
206   if (IS_RPUB_BOOT_USR(rpub)) return(OK);
207 
208   /* Process is a service */
209   if (isokendpt(rpub->endpoint, &slot) != OK) {
210 	printf("VFS: can't map service with unknown endpoint %d\n",
211 		rpub->endpoint);
212 	return(EINVAL);
213   }
214   rfp = &fproc[slot];
215   rfp->fp_flags |= FP_SRV_PROC;
216 
217   /* Not a driver, nothing more to do. */
218   if (rpub->dev_nr == NO_DEV) return(OK);
219 
220   /* Map driver. */
221   r = map_driver(rpub->label, rpub->dev_nr, rpub->endpoint);
222   if(r != OK) return(r);
223 
224   return(OK);
225 }
226 
227 /*===========================================================================*
228  *				init_dmap		 		     *
229  *===========================================================================*/
init_dmap(void)230 void init_dmap(void)
231 {
232 /* Initialize the device mapping table. */
233   int i;
234 
235   memset(dmap, 0, sizeof(dmap));
236 
237   for (i = 0; i < NR_DEVICES; i++) {
238 	dmap[i].dmap_driver = NONE;
239 	dmap[i].dmap_servicing = INVALID_THREAD;
240 	if (mutex_init(&dmap[i].dmap_lock, NULL) != 0)
241 		panic("unable to initialize dmap lock");
242   }
243 
244   /* CTTY_MAJOR is a special case, which is handled by VFS itself. */
245   if (map_driver("vfs", CTTY_MAJOR, CTTY_ENDPT) != OK)
246 	panic("map_driver(CTTY_MAJOR) failed");
247 }
248 
249 /*===========================================================================*
250  *				dmap_driver_match	 		     *
251  *===========================================================================*/
dmap_driver_match(endpoint_t proc,devmajor_t major)252 int dmap_driver_match(endpoint_t proc, devmajor_t major)
253 {
254   if (major < 0 || major >= NR_DEVICES) return(0);
255   if (dmap[major].dmap_driver != NONE && dmap[major].dmap_driver == proc)
256 	return(1);
257 
258   return(0);
259 }
260 
261 /*===========================================================================*
262  *				dmap_by_major		 		     *
263  *===========================================================================*/
264 struct dmap *
get_dmap_by_major(devmajor_t major)265 get_dmap_by_major(devmajor_t major)
266 {
267 	if (major < 0 || major >= NR_DEVICES) return(NULL);
268 	if (dmap[major].dmap_driver == NONE) return(NULL);
269 	return(&dmap[major]);
270 }
271 
272 /*===========================================================================*
273  *				dmap_endpt_up		 		     *
274  *===========================================================================*/
dmap_endpt_up(endpoint_t proc_e,int is_blk)275 void dmap_endpt_up(endpoint_t proc_e, int is_blk)
276 {
277 /* A device driver with endpoint proc_e has been restarted. Go tell everyone
278  * that might be blocking on it that this device is 'up'.
279  */
280   devmajor_t major;
281   struct dmap *dp;
282   struct worker_thread *worker;
283 
284   if (proc_e == NONE) return;
285 
286   for (major = 0; major < NR_DEVICES; major++) {
287 	if ((dp = get_dmap_by_major(major)) == NULL) continue;
288 	if (dp->dmap_driver == proc_e) {
289 		if (is_blk) {
290 			if (dp->dmap_recovering) {
291 				printf("VFS: driver recovery failure for"
292 					" major %d\n", major);
293 				if (dp->dmap_servicing != INVALID_THREAD) {
294 					worker = worker_get(dp->dmap_servicing);
295 					worker_stop(worker);
296 				}
297 				dp->dmap_recovering = 0;
298 				continue;
299 			}
300 			dp->dmap_recovering = 1;
301 			bdev_up(major);
302 			dp->dmap_recovering = 0;
303 		} else {
304 			if (dp->dmap_servicing != INVALID_THREAD) {
305 				worker = worker_get(dp->dmap_servicing);
306 				worker_stop(worker);
307 			}
308 			invalidate_filp_by_char_major(major);
309 		}
310 	}
311   }
312 }
313 
314 /*===========================================================================*
315  *				get_dmap		 		     *
316  *===========================================================================*/
get_dmap_by_endpt(endpoint_t proc_e)317 struct dmap *get_dmap_by_endpt(endpoint_t proc_e)
318 {
319 /* See if 'proc_e' endpoint belongs to a valid dmap entry. If so, return a
320  * pointer */
321   devmajor_t major;
322 
323   for (major = 0; major < NR_DEVICES; major++)
324 	if (dmap_driver_match(proc_e, major))
325 		return(&dmap[major]);
326 
327   return(NULL);
328 }
329