xref: /illumos-gate/usr/src/uts/common/io/rsm/rsmops.c (revision 355b4669e025ff377602b6fc7caaf30dbc218371)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/modctl.h>
31 #include <sys/stat.h>
32 #include <sys/proc.h>
33 #include <sys/ddi.h>
34 #include <sys/sunddi.h>
35 #include <sys/kmem.h>
36 #include <sys/file.h>
37 #include <sys/rsm/rsm_common.h>
38 #include <sys/rsm/rsmpi.h>
39 #include <sys/rsm/rsmpi_driver.h>
40 
41 /* lint -w2 */
42 static struct modlmisc modlmisc = {
43 	&mod_miscops, "RSMOPS module %I%",
44 };
45 
46 static struct modlinkage modlinkage = {
47 	MODREV_1, (void *)&modlmisc, NULL
48 };
49 
50 static kmutex_t rsmops_lock;
51 
52 static rsmops_drv_t *rsmops_drv_head = NULL;
53 
54 static int rsmops_threads_started = 0;
55 
56 int
57 _init(void)
58 {
59 	int	err;
60 
61 	mutex_init(&rsmops_lock, NULL, MUTEX_DEFAULT, NULL);
62 
63 	if ((err = mod_install(&modlinkage)) != 0)
64 		mutex_destroy(&rsmops_lock);
65 
66 	return (err);
67 }
68 
69 int
70 _fini(void)
71 {
72 	int	err;
73 
74 	mutex_enter(&rsmops_lock);
75 	if (rsmops_drv_head) {
76 		/* Somebody is still registered with us - we cannot unload */
77 		mutex_exit(&rsmops_lock);
78 		return (EBUSY);
79 	}
80 	if (rsmops_threads_started) {
81 		/*
82 		 * Some threads have been started.  We do not have any
83 		 * well-supported way of checking whether they have all
84 		 * exited.  For now, fail attempt to unload if we have
85 		 * ever started any threads.  This is overkill, but ...
86 		 */
87 		mutex_exit(&rsmops_lock);
88 		return (EBUSY);
89 	}
90 	mutex_exit(&rsmops_lock);
91 
92 	if ((err = mod_remove(&modlinkage)) == 0)
93 		mutex_destroy(&rsmops_lock);
94 	return (err);
95 }
96 
97 int
98 _info(struct modinfo *modinfop)
99 {
100 	return (mod_info(&modlinkage, modinfop));
101 }
102 
103 static void
104 rsmops_thread_entry(rsmops_drv_t *p_drv)
105 {
106 	/* p_drv->ctrl_cnt has already been increased by the time we get here */
107 	ASSERT(p_drv->drv.rsm_thread_entry_pt);
108 
109 	/* call the driver with the thread */
110 	(*(p_drv->drv.rsm_thread_entry_pt))(p_drv->drv.drv_name);
111 
112 	/* thread has returned */
113 	mutex_enter(&rsmops_lock);
114 	p_drv->ctrl_cnt--;
115 	mutex_exit(&rsmops_lock);
116 }
117 
118 /* This is expected to be called from the driver's init function */
119 int
120 rsm_register_driver(rsmops_registry_t *p_registry)
121 {
122 	rsmops_drv_t **pp_tail;
123 	rsmops_drv_t *p;
124 
125 	if (p_registry->rsm_version > RSM_VERSION) {
126 		/* The driver is up-rev than me.  Fail attempt to register */
127 		return (RSMERR_BAD_DRIVER_VERSION);
128 	}
129 
130 	/*
131 	 * RSM_VERSION: Since this is the first version, there cannot be any
132 	 * down-rev drivers - this will be an issue in the future
133 	 */
134 	if (p_registry->rsm_version != RSM_VERSION)
135 		return (RSMERR_BAD_DRIVER_VERSION);
136 
137 	mutex_enter(&rsmops_lock);
138 	/* First, search that this driver is not already registered */
139 	pp_tail = &rsmops_drv_head;
140 	while (*pp_tail) {
141 		if (strcmp((*pp_tail)->drv.drv_name, p_registry->drv_name)
142 		    == 0) {
143 			mutex_exit(&rsmops_lock);
144 			return (RSMERR_DRIVER_NAME_IN_USE);
145 		}
146 		pp_tail = &((*pp_tail)->next);
147 	}
148 
149 	p = kmem_alloc(sizeof (rsmops_drv_t), KM_SLEEP);
150 	p->drv = *p_registry;	/* copy entire rsmops_registry_t structure */
151 	p->next = NULL;
152 	p->ctrl_cnt = 0;
153 	p->ctrl_head = NULL;
154 
155 	if (p->drv.rsm_thread_entry_pt) {
156 		/* thread entry point is defined - we need to create a thread */
157 		extern  pri_t   minclsyspri;
158 
159 		p->ctrl_cnt++;	/* bump up the count right now */
160 		p->thread_id = thread_create(NULL, 0, rsmops_thread_entry,
161 		    p, 0, &p0, TS_RUN, minclsyspri);
162 		rsmops_threads_started++;
163 	} else
164 		p->thread_id = NULL;
165 
166 	*pp_tail = p;
167 	mutex_exit(&rsmops_lock);
168 	return (RSM_SUCCESS);
169 }
170 
171 /*
172  * This is expected to be called from the driver's fini function
173  * if this function returns EBUSY, the driver is supposed to fail
174  * its own fini operation
175  */
176 int
177 rsm_unregister_driver(rsmops_registry_t *p_registry)
178 {
179 	rsmops_drv_t **pp_tail;
180 	rsmops_drv_t *p;
181 
182 	mutex_enter(&rsmops_lock);
183 
184 	/* Search for the driver */
185 	pp_tail = &rsmops_drv_head;
186 	while (*pp_tail) {
187 		if (strcmp((*pp_tail)->drv.drv_name, p_registry->drv_name)) {
188 			pp_tail = &((*pp_tail)->next);
189 			continue;
190 		}
191 		/* check ref count - if somebody is using it, return EBUSY */
192 		if ((*pp_tail)->ctrl_cnt) {
193 			mutex_exit(&rsmops_lock);
194 			return (RSMERR_CTLRS_REGISTERED);
195 		}
196 		/* Nobody is using it - we can allow the unregister to happen */
197 		p = *pp_tail;
198 
199 		/* Stomp the guy out of our linked list */
200 		*pp_tail = (*pp_tail)->next;
201 
202 		/* release the memory */
203 		kmem_free(p, sizeof (rsmops_drv_t));
204 
205 		mutex_exit(&rsmops_lock);
206 		return (RSM_SUCCESS);
207 	}
208 
209 	/* Could not find the guy */
210 	mutex_exit(&rsmops_lock);
211 	return (RSMERR_DRIVER_NOT_REGISTERED);
212 }
213 
214 /* Should be called holding the rsmops_lock mutex */
215 static rsmops_drv_t *
216 find_rsmpi_driver(const char *name)
217 {
218 	rsmops_drv_t *p_rsmops_list;
219 
220 	ASSERT(MUTEX_HELD(&rsmops_lock));
221 	/* the name is of the form "sci", "wci" etc */
222 
223 	for (p_rsmops_list = rsmops_drv_head; p_rsmops_list != NULL;
224 	    p_rsmops_list = p_rsmops_list->next) {
225 
226 		if (strcmp(name, p_rsmops_list->drv.drv_name) == 0) {
227 			return (p_rsmops_list);
228 		}
229 	}
230 	return (NULL);
231 }
232 
233 
234 /* Should be called holding the rsmops_lock mutex */
235 static rsmops_ctrl_t *
236 find_rsmpi_controller(const char *name, uint_t number)
237 {
238 	rsmops_drv_t *p_drv;
239 	rsmops_ctrl_t *p;
240 
241 	ASSERT(MUTEX_HELD(&rsmops_lock));
242 
243 	if ((p_drv = find_rsmpi_driver(name)) == NULL)
244 		return (NULL);
245 
246 	for (p = p_drv->ctrl_head; p != NULL; p = p->next) {
247 		ASSERT(p->p_drv == p_drv);
248 		if (p->number == number)
249 			return (p);
250 	}
251 	return (NULL);
252 }
253 
254 /* Should be called holding the rsmops_lock mutex */
255 static rsmops_ctrl_t *
256 find_rsmpi_controller_handle(rsm_controller_handle_t cntlr_handle)
257 {
258 	rsmops_drv_t *p_drv;
259 	rsmops_ctrl_t *p;
260 
261 	ASSERT(MUTEX_HELD(&rsmops_lock));
262 
263 	for (p_drv = rsmops_drv_head; p_drv != NULL; p_drv = p_drv->next) {
264 		for (p = p_drv->ctrl_head; p != NULL; p = p->next) {
265 			if (p->handle == cntlr_handle)
266 				return (p);
267 		}
268 	}
269 
270 	return (NULL);
271 }
272 
273 static vnode_t *
274 rsmops_device_open(const char *major_name, const minor_t minor_num);
275 
276 int
277 rsm_get_controller(const char *name, uint_t number,
278     rsm_controller_object_t *controller, uint_t version)
279 {
280 	rsmops_ctrl_t *p_ctrl;
281 	rsmops_drv_t *p_drv;
282 	vnode_t *vp;
283 	int error;
284 	int (*rsm_get_controller_handler)
285 	    (const char *name, uint_t number,
286 	    rsm_controller_object_t *pcontroller, uint_t version);
287 
288 	mutex_enter(&rsmops_lock);
289 
290 	/* check if the controller is already registered */
291 	if ((p_ctrl = find_rsmpi_controller(name, number)) == NULL) {
292 		/*
293 		 * controller is not registered.  We should try to load it
294 		 * First check if the driver is registered
295 		 */
296 		if ((p_drv = find_rsmpi_driver(name)) == NULL) {
297 			/* Cannot find the driver.  Try to load him */
298 			mutex_exit(&rsmops_lock);
299 			if ((error = modload("drv", (char *)name)) == -1) {
300 				return (RSMERR_CTLR_NOT_PRESENT);
301 			}
302 			mutex_enter(&rsmops_lock);
303 			if ((p_drv = find_rsmpi_driver(name)) == NULL) {
304 				mutex_exit(&rsmops_lock);
305 				/*
306 				 * Cannot find yet - maybe the driver we loaded
307 				 * was not a RSMPI driver at all.  We'll just
308 				 * fail this call.
309 				 */
310 				return (RSMERR_CTLR_NOT_PRESENT);
311 			}
312 		}
313 		ASSERT(p_drv);
314 		p_ctrl = find_rsmpi_controller(name, number);
315 		if (p_ctrl == NULL) {
316 			/*
317 			 * controller is not registered.
318 			 * try to do a VOP_OPEN to force it to get registered
319 			 */
320 			mutex_exit(&rsmops_lock);
321 			vp = rsmops_device_open(name, number);
322 			mutex_enter(&rsmops_lock);
323 			if (vp != NULL) {
324 				(void) VOP_CLOSE(vp, FREAD|FWRITE, 0, 0,
325 				    CRED());
326 				VN_RELE(vp);
327 			}
328 			p_ctrl = find_rsmpi_controller(name, number);
329 			if (p_ctrl == NULL) {
330 				mutex_exit(&rsmops_lock);
331 				return (RSMERR_CTLR_NOT_PRESENT);
332 			}
333 		}
334 		ASSERT(p_ctrl);
335 	} else {
336 		p_drv = p_ctrl->p_drv;
337 	}
338 	ASSERT(p_drv);
339 	ASSERT(p_drv == p_ctrl->p_drv);
340 
341 	rsm_get_controller_handler = p_drv->drv.rsm_get_controller_handler;
342 	/*
343 	 * Increase the refcnt right now, so that attempts to deregister
344 	 * while we are using this entry will fail
345 	 */
346 	p_ctrl->refcnt++;
347 	mutex_exit(&rsmops_lock);
348 
349 	error = (*rsm_get_controller_handler)(name, number, controller,
350 	    version);
351 	if (error != RSM_SUCCESS) {
352 		/* We failed - drop the refcnt back */
353 		mutex_enter(&rsmops_lock);
354 		/*
355 		 * Even though we had released the global lock, we can
356 		 * guarantee that p_ctrl is still meaningful (and has not
357 		 * been deregistered, freed whatever) because we were holding
358 		 * refcnt on it.  So, it is okay to just use p_ctrl here
359 		 * after re-acquiring the global lock
360 		 */
361 		p_ctrl->refcnt--;
362 		mutex_exit(&rsmops_lock);
363 	} else {
364 		/*
365 		 * Initialize the controller handle field
366 		 */
367 		mutex_enter(&rsmops_lock);
368 		if ((p_ctrl = find_rsmpi_controller(name, number)) == NULL) {
369 			mutex_exit(&rsmops_lock);
370 			return (RSMERR_CTLR_NOT_PRESENT);
371 		}
372 
373 		p_ctrl->handle = controller->handle;
374 		mutex_exit(&rsmops_lock);
375 	}
376 	return (error);
377 }
378 
379 int
380 rsm_release_controller(const char *name, uint_t number,
381     rsm_controller_object_t *controller)
382 {
383 	rsmops_ctrl_t *p_ctrl;
384 	rsmops_drv_t *p_drv;
385 	int error;
386 	int (*releaser)(const char *name, uint_t number,
387 	    rsm_controller_object_t *controller);
388 
389 	mutex_enter(&rsmops_lock);
390 
391 	if ((p_ctrl = find_rsmpi_controller(name, number)) == NULL) {
392 		mutex_exit(&rsmops_lock);
393 		return (RSMERR_CTLR_NOT_PRESENT);
394 	}
395 	p_drv = find_rsmpi_driver(name);
396 	ASSERT(p_drv);	/* If we found controller, there MUST be a driver */
397 
398 	/* Found the appropriate driver.  Forward the call to it */
399 	releaser = p_drv->drv.rsm_release_controller_handler;
400 	mutex_exit(&rsmops_lock);
401 
402 	error = (*releaser)(name, number, controller);
403 	if (error == RSM_SUCCESS) {
404 		mutex_enter(&rsmops_lock);
405 		p_ctrl->refcnt--;
406 		mutex_exit(&rsmops_lock);
407 	}
408 	return (error);
409 }
410 
411 /* This is expected to be called from the driver's attach function */
412 int
413 rsm_register_controller(const char *name, uint_t number,
414     rsm_controller_attr_t *attrp)
415 {
416 	rsmops_drv_t *p_drv;
417 	rsmops_ctrl_t *p_ctrl;
418 
419 	if (strlen(name) > MAX_DRVNAME)
420 		return (RSMERR_NAME_TOO_LONG);
421 
422 	mutex_enter(&rsmops_lock);
423 
424 	/* Check if the driver is registered with us */
425 	p_drv = find_rsmpi_driver(name);
426 	if (p_drv == NULL) {
427 		/*
428 		 * Hey! Driver is not registered, but we are getting a
429 		 * controller ??
430 		 */
431 		mutex_exit(&rsmops_lock);
432 		return (RSMERR_DRIVER_NOT_REGISTERED);
433 	}
434 
435 	/* Check if the controller is already registered with us */
436 	p_ctrl = find_rsmpi_controller(name, number);
437 	if (p_ctrl) {
438 		/* already registered */
439 		mutex_exit(&rsmops_lock);
440 		return (RSMERR_CTLR_ALREADY_REGISTERED);
441 	}
442 
443 	/* WAIT: sanity check - verify that the dip matches up to name,number */
444 
445 	p_ctrl = kmem_alloc(sizeof (rsmops_ctrl_t), KM_SLEEP);
446 
447 	/* bump up controller count on the driver */
448 	p_drv->ctrl_cnt++;
449 
450 	p_ctrl->p_drv = p_drv;	/* setup the back pointer */
451 	p_ctrl->number = number;
452 	p_ctrl->refcnt = 0;
453 	p_ctrl->attrp = attrp;
454 	p_ctrl->handle = NULL;
455 
456 	/* Now link to head of list */
457 	p_ctrl->next = p_drv->ctrl_head;
458 	p_drv->ctrl_head = p_ctrl;
459 
460 	mutex_exit(&rsmops_lock);
461 
462 	return (RSM_SUCCESS);
463 }
464 
465 /*
466  * This is expected to be called from the driver's detach function
467  * if this function returns EBUSY, the driver is supposed to fail
468  * his own detach operation
469  */
470 int
471 rsm_unregister_controller(const char *name, uint_t number)
472 {
473 	rsmops_drv_t *p_drv;
474 	rsmops_ctrl_t **p_prev;
475 	rsmops_ctrl_t *found;
476 
477 	mutex_enter(&rsmops_lock);
478 
479 	/* Check if the driver is registered with us */
480 	p_drv = find_rsmpi_driver(name);
481 	if (p_drv == NULL) {
482 		/* Hey!  Driver is not registered */
483 		mutex_exit(&rsmops_lock);
484 		return (RSMERR_DRIVER_NOT_REGISTERED);
485 	}
486 
487 	/* Search for the controller in the list */
488 	for (p_prev = &p_drv->ctrl_head; *p_prev; p_prev = &((*p_prev)->next)) {
489 		if ((*p_prev)->number == number) {
490 			/* Found the controller.  Check if it is busy */
491 			found = *p_prev;
492 
493 			if (found->refcnt) {
494 				/* Controller is busy -  handles outstanding */
495 				mutex_exit(&rsmops_lock);
496 				return (RSMERR_CTLR_IN_USE);
497 			}
498 			/* unlink it out */
499 			*p_prev = found->next;
500 			/* bump down controller count on the driver */
501 			p_drv->ctrl_cnt--;
502 
503 			mutex_exit(&rsmops_lock);
504 			kmem_free(found, sizeof (rsmops_ctrl_t));
505 			return (RSM_SUCCESS);
506 		}
507 	}
508 	mutex_exit(&rsmops_lock);
509 	/* Could not find the right controller */
510 	return (RSMERR_CTLR_NOT_REGISTERED);
511 }
512 
513 
514 /*
515  * This opens and closes the appropriate device with minor number -
516  * hopefully, it will cause the driver to attach and register a controller
517  * with us
518  */
519 static vnode_t *
520 rsmops_device_open(const char *major_name, const minor_t minor_num)
521 {
522 	major_t maj;
523 	vnode_t *vp;
524 	int ret;
525 
526 	if (minor_num == (minor_t)-1) {
527 		return (NULL);
528 	}
529 
530 	maj = ddi_name_to_major((char *)major_name);
531 	if (maj == (major_t)-1) {
532 		return (NULL);
533 	}
534 
535 	vp = makespecvp(makedevice(maj, minor_num), VCHR);
536 
537 	ret = VOP_OPEN(&vp, FREAD|FWRITE, CRED());
538 	if (ret == 0) {
539 		return (vp);
540 	} else {
541 		VN_RELE(vp);
542 		return (NULL);
543 	}
544 }
545 
546 /*
547  * Attributes for controller identified by the handle are returned
548  * via *attrp. Modifications of attributes is prohibited by client!
549  */
550 int
551 rsm_get_controller_attr(rsm_controller_handle_t handle,
552     rsm_controller_attr_t **attrp)
553 {
554 
555 	rsmops_ctrl_t *p_ctrl;
556 
557 	if (handle == NULL)
558 		return (RSMERR_BAD_CTLR_HNDL);
559 
560 	mutex_enter(&rsmops_lock);
561 
562 	/* find controller */
563 	if ((p_ctrl = find_rsmpi_controller_handle(handle)) == NULL) {
564 		/* can't supply attributes for invalid controller */
565 		mutex_exit(&rsmops_lock);
566 		return (RSMERR_BAD_CTLR_HNDL);
567 	}
568 	*attrp =  p_ctrl->attrp;
569 	mutex_exit(&rsmops_lock);
570 
571 	return (RSM_SUCCESS);
572 }
573