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 } 437