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, NULL } 107 }; 108 109 /* 110 * _init() 111 */ 112 int 113 _init(void) 114 { 115 int status; 116 117 iser_state = kmem_zalloc(sizeof (iser_state_t), KM_SLEEP); 118 status = mod_install(&iser_modlinkage); 119 if (status != DDI_SUCCESS) { 120 kmem_free(iser_state, sizeof (iser_state_t)); 121 } 122 123 return (status); 124 } 125 126 /* 127 * _info() 128 */ 129 int 130 _info(struct modinfo *modinfop) 131 { 132 return (mod_info(&iser_modlinkage, modinfop)); 133 } 134 135 /* 136 * _fini() 137 */ 138 int 139 _fini(void) 140 { 141 int status; 142 143 status = mod_remove(&iser_modlinkage); 144 if (status != DDI_SUCCESS) { 145 return (status); 146 } 147 kmem_free(iser_state, sizeof (iser_state_t)); 148 149 return (DDI_SUCCESS); 150 } 151 152 /* 153 * iser_attach() 154 */ 155 static int 156 iser_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 157 { 158 int instance; 159 int status; 160 161 switch (cmd) { 162 case DDI_ATTACH: 163 ISER_LOG(CE_CONT, "iser_attach: DDI_ATTACH"); 164 instance = ddi_get_instance(dip); 165 166 iser_state->is_dip = dip; 167 iser_state->is_instance = instance; 168 169 /* Initialize the open refcnt and it's lock */ 170 iser_state->is_open_refcnt = 0; 171 mutex_init(&iser_state->is_refcnt_lock, NULL, MUTEX_DRIVER, 172 NULL); 173 174 iser_taskq = ddi_taskq_create(dip, "iser_taskq", 175 ISER_TASKQ_NTHREADS, TASKQ_DEFAULTPRI, 0); 176 177 if (iser_taskq == NULL) { 178 ISER_LOG(CE_CONT, "%s%d: failed to create taskq", 179 "iser", instance); 180 mutex_destroy(&iser_state->is_refcnt_lock); 181 return (DDI_FAILURE); 182 } 183 184 /* initialize iSER as IB service */ 185 status = iser_ib_init(); 186 if (status != DDI_SUCCESS) { 187 ddi_taskq_destroy(iser_taskq); 188 mutex_destroy(&iser_state->is_refcnt_lock); 189 ISER_LOG(CE_CONT, "%s%d: failed to initialize IB", 190 "iser", instance); 191 return (DDI_FAILURE); 192 } 193 194 status = ddi_create_minor_node( 195 dip, ddi_get_name(dip), S_IFCHR, instance, 196 DDI_PSEUDO, 0); 197 if (status != DDI_SUCCESS) { 198 (void) iser_ib_fini(); 199 ddi_taskq_destroy(iser_taskq); 200 mutex_destroy(&iser_state->is_refcnt_lock); 201 ISER_LOG(CE_CONT, "%s%d: failed ddi_create_minor_node", 202 "iser", instance); 203 return (DDI_FAILURE); 204 } 205 206 ddi_report_dev(dip); 207 208 return (DDI_SUCCESS); 209 210 case DDI_RESUME: 211 ISER_LOG(CE_CONT, "iser_detach: DDI_RESUME unsupported"); 212 return (DDI_FAILURE); 213 214 default: 215 ISER_LOG(CE_CONT, "%s%d: unknown cmd in attach (0x%x)", "iser", 216 instance, cmd); 217 return (DDI_FAILURE); 218 } 219 } 220 221 /* 222 * iser_detach() 223 */ 224 static int 225 iser_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 226 { 227 mutex_enter(&iser_state->is_refcnt_lock); 228 if (iser_state->is_open_refcnt > 0) { 229 mutex_exit(&iser_state->is_refcnt_lock); 230 return (DDI_FAILURE); 231 } 232 mutex_exit(&iser_state->is_refcnt_lock); 233 mutex_destroy(&iser_state->is_refcnt_lock); 234 235 switch (cmd) { 236 case DDI_DETACH: 237 ISER_LOG(CE_CONT, "iser_detach: DDI_DETACH"); 238 239 if (iser_ib_fini() != DDI_SUCCESS) { 240 ISER_LOG(CE_CONT, "iser_ib_fini failed"); 241 return (DDI_FAILURE); 242 } 243 244 if (iser_taskq != NULL) { 245 ddi_taskq_destroy(iser_taskq); 246 iser_taskq = NULL; 247 } 248 ddi_remove_minor_node(dip, NULL); 249 250 return (DDI_SUCCESS); 251 252 case DDI_SUSPEND: 253 ISER_LOG(CE_CONT, "iser_detach: DDI_SUSPEND unsupported"); 254 return (DDI_FAILURE); 255 256 default: 257 ISER_LOG(CE_CONT, "iser: unknown cmd in detach (0x%x)", cmd); 258 return (DDI_FAILURE); 259 } 260 } 261 262 /* 263 * iser_getinfo() 264 */ 265 /* ARGSUSED */ 266 static int 267 iser_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 268 { 269 switch (cmd) { 270 case DDI_INFO_DEVT2DEVINFO: 271 *result = (void *)iser_state->is_dip; 272 return (DDI_SUCCESS); 273 274 case DDI_INFO_DEVT2INSTANCE: 275 *result = NULL; 276 return (DDI_SUCCESS); 277 278 default: 279 return (DDI_FAILURE); 280 } 281 282 } 283 284 /* 285 * iser_open() 286 */ 287 /* ARGSUSED */ 288 static int 289 iser_open(dev_t *devp, int flag, int otyp, cred_t *credp) 290 { 291 minor_t instance; 292 int status; 293 294 instance = getminor(*devp); 295 296 /* Register the transport with IDM */ 297 status = iser_idm_register(); 298 if (status != DDI_SUCCESS) { 299 ISER_LOG(CE_CONT, "%s%d: failed to register with IDM", 300 "iser", instance); 301 return (ENXIO); 302 } 303 304 /* Increment our open refcnt */ 305 mutex_enter(&iser_state->is_refcnt_lock); 306 iser_state->is_open_refcnt++; 307 mutex_exit(&iser_state->is_refcnt_lock); 308 309 return (DDI_SUCCESS); 310 } 311 312 /* 313 * iser_close() 314 */ 315 /* ARGSUSED */ 316 static int 317 iser_close(dev_t devp, int flag, int otyp, cred_t *credp) 318 { 319 ASSERT(iser_state->is_open_refcnt != 0); 320 321 mutex_enter(&iser_state->is_refcnt_lock); 322 iser_state->is_open_refcnt--; 323 mutex_exit(&iser_state->is_refcnt_lock); 324 325 return (DDI_SUCCESS); 326 } 327 328 iser_status_t 329 iser_register_service(idm_svc_t *idm_svc) 330 { 331 332 return (iser_ib_register_service(idm_svc)); 333 } 334 335 iser_status_t 336 iser_bind_service(idm_svc_t *idm_svc) 337 { 338 339 return (iser_ib_bind_service(idm_svc)); 340 } 341 342 void 343 iser_unbind_service(idm_svc_t *idm_svc) 344 { 345 346 iser_ib_unbind_service(idm_svc); 347 } 348 349 void 350 iser_deregister_service(idm_svc_t *idm_svc) 351 { 352 353 iser_ib_deregister_service(idm_svc); 354 } 355 356 /* 357 * iser_path_exists 358 * This function takes in a pair of endpoints and determines if an iSER path 359 * exists between the two. The actual path information (required for creating 360 * a RC channel) is not returned, instead a boolean value indicating if a path 361 * exists is returned. 362 * 363 * To use an implicit source, a value of NULL is allowed for laddr. 364 */ 365 boolean_t 366 iser_path_exists(idm_sockaddr_t *laddr, idm_sockaddr_t *raddr) 367 { 368 369 ibt_ip_addr_t remote_ip, local_ip; 370 ibt_path_info_t path; 371 int status; 372 373 iser_ib_conv_sockaddr2ibtaddr(raddr, &remote_ip); 374 iser_ib_conv_sockaddr2ibtaddr(laddr, &local_ip); 375 376 status = iser_ib_get_paths(&local_ip, &remote_ip, &path, NULL); 377 378 return ((status == IBT_SUCCESS) ? B_TRUE : B_FALSE); 379 } 380 381 /* 382 * iser_channel_alloc 383 * This function allocates a reliable communication channel between the 384 * given endpoints. 385 */ 386 iser_chan_t * 387 iser_channel_alloc(idm_sockaddr_t *laddr, idm_sockaddr_t *raddr) 388 { 389 ibt_ip_addr_t remote_ip, local_ip; 390 391 iser_ib_conv_sockaddr2ibtaddr(raddr, &remote_ip); 392 iser_ib_conv_sockaddr2ibtaddr(laddr, &local_ip); 393 394 return (iser_ib_alloc_channel_pathlookup(&local_ip, &remote_ip)); 395 } 396 397 /* 398 * iser_channel_open 399 * This function opens the already allocated communication channel between the 400 * two endpoints. 401 */ 402 iser_status_t 403 iser_channel_open(iser_chan_t *chan) 404 { 405 return (iser_ib_open_rc_channel(chan)); 406 } 407 408 /* 409 * iser_channel_close 410 * This function closes the already opened communication channel between the 411 * two endpoints. 412 */ 413 void 414 iser_channel_close(iser_chan_t *chan) 415 { 416 iser_ib_close_rc_channel(chan); 417 } 418 419 /* 420 * iser_channel_free 421 * This function frees the channel between the given endpoints 422 */ 423 void 424 iser_channel_free(iser_chan_t *chan) 425 { 426 iser_ib_free_rc_channel(chan); 427 } 428 429 /* ARGSUSED */ 430 static int 431 iser_ioctl(dev_t devp, int cmd, intptr_t arg, int mode, cred_t *credp, 432 int *rvalp) 433 { 434 return (DDI_SUCCESS); 435 }