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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/conf.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/modctl.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 
35 #include <sys/ib/clients/iser/iser.h>
36 
37 /*
38  * iser.c
39  *    DDI and core routines for Solaris iSER implementation.
40  */
41 
42 iser_state_t	*iser_state = NULL;	/* global state */
43 ddi_taskq_t	*iser_taskq = NULL;	/* global taskq */
44 
45 /* set B_TRUE for console logging */
46 boolean_t iser_logging = B_FALSE;
47 
48 /* Driver functions */
49 static int iser_attach(dev_info_t *, ddi_attach_cmd_t);
50 static int iser_detach(dev_info_t *, ddi_detach_cmd_t);
51 static int iser_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
52 static int iser_open(dev_t *, int, int, cred_t *);
53 static int iser_close(dev_t, int, int, cred_t *);
54 static int iser_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
55 /* static int iser_close(dev_t, int, int, cred_t *); */
56 
57 /* Char/Block operations */
58 static struct cb_ops	iser_cb_ops = {
59 	iser_open,		/* open */
60 	iser_close,		/* close */
61 	nodev,			/* strategy */
62 	nodev,			/* print */
63 	nodev,			/* dump */
64 	nodev,			/* read */
65 	nodev,			/* write */
66 	iser_ioctl,		/* ioctl */
67 	nodev,			/* devmap */
68 	nodev,			/* mmap */
69 	nodev,			/* segmap */
70 	nochpoll,		/* poll */
71 	ddi_prop_op,		/* prop_op */
72 	NULL,			/* stream */
73 	D_MP,			/* cb_flag */
74 	CB_REV,			/* rev */
75 	nodev,			/* int (*cb_aread)() */
76 	nodev,			/* int (*cb_awrite)() */
77 };
78 
79 /* Device operations */
80 static struct dev_ops iser_ops = {
81 	DEVO_REV,		/* devo_rev, */
82 	0,			/* refcnt  */
83 	iser_getinfo,		/* getinfo */
84 	nulldev,		/* identify */
85 	nulldev,		/* probe */
86 	iser_attach,		/* attach */
87 	iser_detach,		/* detach */
88 	nodev,			/* reset */
89 	&iser_cb_ops,		/* cb_ops */
90 	NULL,			/* bus ops */
91 	NULL,			/* power */
92 	ddi_quiesce_not_needed	/* quiesce */
93 };
94 
95 /* Module Driver Info */
96 #define	ISER_NAME_VERSION	"iSCSI Extensions for RDMA"
97 static struct modldrv iser_modldrv = {
98 	&mod_driverops,
99 	ISER_NAME_VERSION,
100 	&iser_ops,
101 };
102 
103 /* Module Linkage */
104 static struct modlinkage iser_modlinkage = {
105 	MODREV_1,
106 	&iser_modldrv,
107 	NULL
108 };
109 
110 /*
111  * _init()
112  */
113 int
_init(void)114 _init(void)
115 {
116 	int	status;
117 
118 	iser_state = kmem_zalloc(sizeof (iser_state_t), KM_SLEEP);
119 	status = mod_install(&iser_modlinkage);
120 	if (status != DDI_SUCCESS) {
121 		kmem_free(iser_state, sizeof (iser_state_t));
122 	}
123 
124 	return (status);
125 }
126 
127 /*
128  * _info()
129  */
130 int
_info(struct modinfo * modinfop)131 _info(struct modinfo *modinfop)
132 {
133 	return (mod_info(&iser_modlinkage, modinfop));
134 }
135 
136 /*
137  * _fini()
138  */
139 int
_fini(void)140 _fini(void)
141 {
142 	int status;
143 
144 	status = mod_remove(&iser_modlinkage);
145 	if (status != DDI_SUCCESS) {
146 		return (status);
147 	}
148 	kmem_free(iser_state, sizeof (iser_state_t));
149 
150 	return (DDI_SUCCESS);
151 }
152 
153 /*
154  * iser_attach()
155  */
156 static int
iser_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)157 iser_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
158 {
159 	int		instance;
160 	int		status;
161 
162 	switch (cmd) {
163 	case DDI_ATTACH:
164 		ISER_LOG(CE_CONT, "iser_attach: DDI_ATTACH");
165 		instance = ddi_get_instance(dip);
166 
167 		iser_state->is_dip = dip;
168 		iser_state->is_instance = instance;
169 
170 		/* Initialize the open refcnt and it's lock */
171 		iser_state->is_open_refcnt = 0;
172 		mutex_init(&iser_state->is_refcnt_lock, NULL, MUTEX_DRIVER,
173 		    NULL);
174 
175 		iser_taskq = ddi_taskq_create(dip, "iser_taskq",
176 		    ISER_TASKQ_NTHREADS, TASKQ_DEFAULTPRI, 0);
177 
178 		if (iser_taskq == NULL) {
179 			ISER_LOG(CE_CONT, "%s%d: failed to create taskq",
180 			    "iser", instance);
181 			mutex_destroy(&iser_state->is_refcnt_lock);
182 			return (DDI_FAILURE);
183 		}
184 
185 		/* initialize iSER as IB service */
186 		status = iser_ib_init();
187 		if (status != DDI_SUCCESS) {
188 			ddi_taskq_destroy(iser_taskq);
189 			mutex_destroy(&iser_state->is_refcnt_lock);
190 			ISER_LOG(CE_CONT, "%s%d: failed to initialize IB",
191 			    "iser", instance);
192 			return (DDI_FAILURE);
193 		}
194 
195 		status = ddi_create_minor_node(
196 		    dip, ddi_get_name(dip), S_IFCHR, instance,
197 		    DDI_PSEUDO, 0);
198 		if (status != DDI_SUCCESS) {
199 			(void) iser_ib_fini();
200 			ddi_taskq_destroy(iser_taskq);
201 			mutex_destroy(&iser_state->is_refcnt_lock);
202 			ISER_LOG(CE_CONT, "%s%d: failed ddi_create_minor_node",
203 			    "iser", instance);
204 			return (DDI_FAILURE);
205 		}
206 
207 		ddi_report_dev(dip);
208 
209 		return (DDI_SUCCESS);
210 
211 	case DDI_RESUME:
212 		ISER_LOG(CE_CONT, "iser_detach: DDI_RESUME unsupported");
213 		return (DDI_FAILURE);
214 
215 	default:
216 		ISER_LOG(CE_CONT, "%s%d: unknown cmd in attach (0x%x)", "iser",
217 		    instance, cmd);
218 		return (DDI_FAILURE);
219 	}
220 }
221 
222 /*
223  * iser_detach()
224  */
225 static int
iser_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)226 iser_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
227 {
228 	mutex_enter(&iser_state->is_refcnt_lock);
229 	if (iser_state->is_open_refcnt > 0) {
230 		mutex_exit(&iser_state->is_refcnt_lock);
231 		return (DDI_FAILURE);
232 	}
233 	mutex_exit(&iser_state->is_refcnt_lock);
234 	mutex_destroy(&iser_state->is_refcnt_lock);
235 
236 	switch (cmd) {
237 	case DDI_DETACH:
238 		ISER_LOG(CE_CONT, "iser_detach: DDI_DETACH");
239 
240 		if (iser_ib_fini() != DDI_SUCCESS) {
241 			ISER_LOG(CE_CONT, "iser_ib_fini failed");
242 			return (DDI_FAILURE);
243 		}
244 
245 		if (iser_taskq != NULL) {
246 			ddi_taskq_destroy(iser_taskq);
247 			iser_taskq = NULL;
248 		}
249 		ddi_remove_minor_node(dip, NULL);
250 
251 		return (DDI_SUCCESS);
252 
253 	case DDI_SUSPEND:
254 		ISER_LOG(CE_CONT, "iser_detach: DDI_SUSPEND unsupported");
255 		return (DDI_FAILURE);
256 
257 	default:
258 		ISER_LOG(CE_CONT, "iser: unknown cmd in detach (0x%x)", cmd);
259 		return (DDI_FAILURE);
260 	}
261 }
262 
263 /*
264  * iser_getinfo()
265  */
266 /* ARGSUSED */
267 static int
iser_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)268 iser_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
269 {
270 	switch (cmd) {
271 	case DDI_INFO_DEVT2DEVINFO:
272 		*result = (void *)iser_state->is_dip;
273 		return (DDI_SUCCESS);
274 
275 	case DDI_INFO_DEVT2INSTANCE:
276 		*result = NULL;
277 		return (DDI_SUCCESS);
278 
279 	default:
280 		return (DDI_FAILURE);
281 	}
282 
283 }
284 
285 /*
286  * iser_open()
287  */
288 /* ARGSUSED */
289 static int
iser_open(dev_t * devp,int flag,int otyp,cred_t * credp)290 iser_open(dev_t *devp, int flag, int otyp, cred_t *credp)
291 {
292 	minor_t		instance;
293 	int		status;
294 
295 	instance = getminor(*devp);
296 
297 	/* Register the transport with IDM */
298 	status = iser_idm_register();
299 	if (status != DDI_SUCCESS) {
300 		ISER_LOG(CE_CONT, "%s%d: failed to register with IDM",
301 		    "iser", instance);
302 		return (ENXIO);
303 	}
304 
305 	/* Increment our open refcnt */
306 	mutex_enter(&iser_state->is_refcnt_lock);
307 	iser_state->is_open_refcnt++;
308 	mutex_exit(&iser_state->is_refcnt_lock);
309 
310 	return (DDI_SUCCESS);
311 }
312 
313 /*
314  * iser_close()
315  */
316 /* ARGSUSED */
317 static int
iser_close(dev_t devp,int flag,int otyp,cred_t * credp)318 iser_close(dev_t devp, int flag, int otyp, cred_t *credp)
319 {
320 	ASSERT(iser_state->is_open_refcnt != 0);
321 
322 	mutex_enter(&iser_state->is_refcnt_lock);
323 	iser_state->is_open_refcnt--;
324 	mutex_exit(&iser_state->is_refcnt_lock);
325 
326 	return (DDI_SUCCESS);
327 }
328 
329 iser_status_t
iser_register_service(idm_svc_t * idm_svc)330 iser_register_service(idm_svc_t *idm_svc)
331 {
332 
333 	return (iser_ib_register_service(idm_svc));
334 }
335 
336 iser_status_t
iser_bind_service(idm_svc_t * idm_svc)337 iser_bind_service(idm_svc_t *idm_svc)
338 {
339 
340 	return (iser_ib_bind_service(idm_svc));
341 }
342 
343 void
iser_unbind_service(idm_svc_t * idm_svc)344 iser_unbind_service(idm_svc_t *idm_svc)
345 {
346 
347 	iser_ib_unbind_service(idm_svc);
348 }
349 
350 void
iser_deregister_service(idm_svc_t * idm_svc)351 iser_deregister_service(idm_svc_t *idm_svc)
352 {
353 
354 	iser_ib_deregister_service(idm_svc);
355 }
356 
357 /*
358  * iser_path_exists
359  * This function takes in a pair of endpoints and determines if an iSER path
360  * exists between the two. The actual path information (required for creating
361  * a RC channel) is not returned, instead a boolean value indicating if a path
362  * exists is returned.
363  *
364  * To use an implicit source, a value of NULL is allowed for laddr.
365  */
366 boolean_t
iser_path_exists(idm_sockaddr_t * laddr,idm_sockaddr_t * raddr)367 iser_path_exists(idm_sockaddr_t *laddr, idm_sockaddr_t *raddr)
368 {
369 
370 	ibt_ip_addr_t		remote_ip, local_ip;
371 	ibt_path_info_t		path;
372 	int			status;
373 
374 	iser_ib_conv_sockaddr2ibtaddr(raddr, &remote_ip);
375 	iser_ib_conv_sockaddr2ibtaddr(laddr, &local_ip);
376 
377 	status = iser_ib_get_paths(&local_ip, &remote_ip, &path, NULL);
378 
379 	return ((status == IBT_SUCCESS) ? B_TRUE : B_FALSE);
380 }
381 
382 /*
383  * iser_channel_alloc
384  * This function allocates a reliable communication channel between the
385  * given endpoints.
386  */
387 iser_chan_t *
iser_channel_alloc(idm_sockaddr_t * laddr,idm_sockaddr_t * raddr)388 iser_channel_alloc(idm_sockaddr_t *laddr, idm_sockaddr_t *raddr)
389 {
390 	ibt_ip_addr_t		remote_ip, local_ip;
391 
392 	iser_ib_conv_sockaddr2ibtaddr(raddr, &remote_ip);
393 	iser_ib_conv_sockaddr2ibtaddr(laddr, &local_ip);
394 
395 	return (iser_ib_alloc_channel_pathlookup(&local_ip, &remote_ip));
396 }
397 
398 /*
399  * iser_channel_open
400  * This function opens the already allocated communication channel between the
401  * two endpoints.
402  */
403 iser_status_t
iser_channel_open(iser_chan_t * chan)404 iser_channel_open(iser_chan_t *chan)
405 {
406 	return (iser_ib_open_rc_channel(chan));
407 }
408 
409 /*
410  * iser_channel_close
411  * This function closes the already opened communication channel between the
412  * two endpoints.
413  */
414 void
iser_channel_close(iser_chan_t * chan)415 iser_channel_close(iser_chan_t *chan)
416 {
417 	iser_ib_close_rc_channel(chan);
418 }
419 
420 /*
421  * iser_channel_free
422  * This function frees the channel between the given endpoints
423  */
424 void
iser_channel_free(iser_chan_t * chan)425 iser_channel_free(iser_chan_t *chan)
426 {
427 	iser_ib_free_rc_channel(chan);
428 }
429 
430 /* ARGSUSED */
431 static int
iser_ioctl(dev_t devp,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)432 iser_ioctl(dev_t devp, int cmd, intptr_t arg, int mode, cred_t *credp,
433     int *rvalp)
434 {
435 	return (DDI_SUCCESS);
436 }
437