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