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