|
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 |
|
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 |
|
131 _info(struct modinfo *modinfop) |
|
132 { |
|
133 return (mod_info(&iser_modlinkage, modinfop)); |
|
134 } |
|
135 |
|
136 /* |
|
137 * _fini() |
|
138 */ |
|
139 int |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
337 iser_bind_service(idm_svc_t *idm_svc) |
|
338 { |
|
339 |
|
340 return (iser_ib_bind_service(idm_svc)); |
|
341 } |
|
342 |
|
343 void |
|
344 iser_unbind_service(idm_svc_t *idm_svc) |
|
345 { |
|
346 |
|
347 iser_ib_unbind_service(idm_svc); |
|
348 } |
|
349 |
|
350 void |
|
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 |
|
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 * |
|
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_rc_channel(&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 |
|
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 |
|
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 |
|
425 iser_channel_free(iser_chan_t *chan) |
|
426 { |
|
427 iser_ib_free_rc_channel(chan); |
|
428 } |
|
429 |
|
430 /* ARGSUSED */ |
|
431 static int |
|
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 } |