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 27 #include <sys/types.h> 28 #include <sys/file.h> 29 #include <sys/errno.h> 30 #include <sys/open.h> 31 #include <sys/cred.h> 32 #include <sys/conf.h> 33 #include <sys/modctl.h> 34 #include <sys/stat.h> 35 #include <sys/ddi.h> 36 #include <sys/sunddi.h> 37 #include <sys/policy.h> 38 #include <sys/pool.h> 39 #include <sys/pool_impl.h> 40 41 /* 42 * The kernel pools subsystem is accessed and manipulated through the pool 43 * device, which has two minor nodes /dev/pool, and /dev/poolctl. User 44 * processes can comminicate with pools through ioctls on these devices. 45 * 46 * The poolctl device (POOL_CTL_PARENT) can be used to modify and take 47 * snapshot of the current configuration. Only one process on the system 48 * can have it open at any given time. This device is also used to enable 49 * or disable pools. If pools are disabled, the pool driver can be unloaded 50 * and completely removed from the system. 51 * 52 * The pool "info" device (POOL_INFO_PARENT) can only be used to obtain 53 * snapshots of the current configuration and change/query pool bindings. 54 * While some reconfiguration transaction via the poolctl device is in 55 * progress, all processes using this "info" device will be provided with 56 * the snapshot taken at the beginning of that transaction. 57 */ 58 59 #define POOL_CTL_PARENT 0 60 #define POOL_INFO_PARENT 1 61 62 static dev_info_t *pool_devi; /* pool device information */ 63 static int pool_openctl; /* poolctl device is already open */ 64 65 /*ARGSUSED*/ 66 static int 67 pool_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 68 { 69 int error = DDI_FAILURE; 70 71 switch (infocmd) { 72 case DDI_INFO_DEVT2DEVINFO: 73 *result = pool_devi; 74 error = DDI_SUCCESS; 75 break; 76 case DDI_INFO_DEVT2INSTANCE: 77 /* 78 * All dev_t's map to the same, single instance. 79 */ 80 *result = NULL; 81 error = DDI_SUCCESS; 82 break; 83 default: 84 break; 85 } 86 return (error); 87 } 88 89 static int 90 pool_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 91 { 92 int ret = DDI_SUCCESS; 93 94 switch (cmd) { 95 case DDI_DETACH: 96 pool_lock(); 97 if (pool_state == POOL_ENABLED) { 98 ret = DDI_FAILURE; 99 pool_unlock(); 100 break; 101 } 102 ddi_remove_minor_node(devi, NULL); 103 pool_devi = NULL; 104 pool_unlock(); 105 break; 106 default: 107 ret = DDI_FAILURE; 108 } 109 return (ret); 110 } 111 112 static int 113 pool_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 114 { 115 switch (cmd) { 116 case DDI_ATTACH: 117 if (pool_devi != NULL) 118 return (DDI_FAILURE); 119 if (ddi_create_minor_node(devi, "poolctl", S_IFCHR, 120 POOL_CTL_PARENT, DDI_PSEUDO, 0) == DDI_FAILURE || 121 ddi_create_minor_node(devi, "pool", S_IFCHR, 122 POOL_INFO_PARENT, DDI_PSEUDO, 0) == DDI_FAILURE) { 123 ddi_remove_minor_node(devi, NULL); 124 return (DDI_FAILURE); 125 } 126 pool_devi = devi; 127 ddi_report_dev(devi); 128 break; 129 case DDI_RESUME: 130 break; 131 default: 132 return (DDI_FAILURE); 133 } 134 return (DDI_SUCCESS); 135 136 } 137 138 /* 139 * There is only one instance of the pool control device, poolctl, 140 * and multiple instances of the pool info device, pool. 141 */ 142 /*ARGSUSED*/ 143 static int 144 pool_open(dev_t *devp, int flag, int otype, cred_t *credp) 145 { 146 minor_t minor = getminor(*devp); 147 148 if (otype != OTYP_CHR) 149 return (EINVAL); 150 151 switch (minor) { 152 case POOL_CTL_PARENT: 153 if (secpolicy_pool(CRED()) != 0) 154 return (EPERM); 155 if (pool_lock_intr() != 0) 156 return (EINTR); 157 if (pool_openctl == 1) { 158 pool_unlock(); 159 return (EBUSY); 160 } 161 pool_openctl = 1; 162 pool_unlock(); 163 break; 164 case POOL_INFO_PARENT: 165 break; 166 default: 167 return (ENXIO); 168 } 169 return (0); 170 } 171 172 /*ARGSUSED*/ 173 static int 174 pool_close(dev_t dev, int flag, int otype, cred_t *credp) 175 { 176 if (otype != OTYP_CHR) 177 return (EINVAL); 178 if (getminor(dev) == 0) { 179 /* 180 * We could be closing the poolctl device without finishing 181 * the commit transaction first, so do that now. 182 */ 183 pool_lock(); 184 (void) pool_commit(0); /* cannot fail since arg is 0 */ 185 pool_openctl = 0; 186 pool_unlock(); 187 } 188 return (0); 189 } 190 191 /* 192 * Main pool interface. 193 */ 194 /* ARGSUSED4 */ 195 static int 196 pool_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 197 int *rvalp) 198 { 199 pool_xtransfer_t xtransfer; 200 pool_transfer_t transfer; 201 pool_destroy_t destroy; 202 pool_propget_t propget; 203 pool_propput_t propput; 204 pool_proprm_t proprm; 205 pool_status_t status; 206 pool_dissoc_t dissoc; 207 pool_create_t create; 208 pool_assoc_t assoc; 209 pool_bindq_t bindq; 210 pool_query_t query; 211 pool_bind_t bind; 212 #ifdef _MULTI_DATAMODEL 213 pool_xtransfer32_t xtransfer32; 214 pool_propput32_t propput32; 215 pool_propget32_t propget32; 216 pool_proprm32_t proprm32; 217 pool_query32_t query32; 218 #endif /* _MULTI_DATAMODEL */ 219 char *kbuf = NULL; 220 size_t kbufsz = 0; 221 int snapshot = 0; 222 char *prop_name; 223 size_t size = 0; 224 nvlist_t *list; 225 nvpair_t *pair; 226 char *listbuf; 227 minor_t minor; 228 uint_t model; 229 id_t *id_buf; 230 int ret = 0; 231 232 model = ddi_model_convert_from(mode & FMODELS); 233 minor = getminor(dev); 234 235 /* 236 * Check basic permissions first. 237 */ 238 switch (cmd) { 239 case POOL_STATUS: 240 case POOL_CREATE: 241 case POOL_ASSOC: 242 case POOL_DISSOC: 243 case POOL_DESTROY: 244 case POOL_TRANSFER: 245 case POOL_XTRANSFER: 246 case POOL_PROPPUT: 247 case POOL_PROPRM: 248 case POOL_COMMIT: 249 if (minor != POOL_CTL_PARENT) 250 return (EINVAL); 251 /*FALLTHROUGH*/ 252 case POOL_BIND: 253 if (secpolicy_pool(CRED()) != 0) 254 return (EPERM); 255 break; 256 } 257 258 switch (cmd) { 259 case POOL_STATUS: 260 if (ddi_copyin((void *)arg, &status, 261 sizeof (pool_status_t), mode) != 0) 262 return (EFAULT); 263 if (pool_lock_intr() != 0) 264 return (EINTR); 265 ret = pool_status(status.ps_io_state); 266 pool_unlock(); 267 break; 268 case POOL_STATUSQ: 269 /* 270 * No need to grab pool_lock() to look at the current state. 271 */ 272 status.ps_io_state = pool_state; 273 if (ddi_copyout(&status, (void *)arg, 274 sizeof (pool_status_t), mode) != 0) 275 return (EFAULT); 276 break; 277 case POOL_QUERY: 278 switch (model) { 279 #ifdef _MULTI_DATAMODEL 280 case DDI_MODEL_ILP32: 281 if (ddi_copyin((void *)arg, &query32, 282 sizeof (pool_query32_t), mode) != 0) 283 return (EFAULT); 284 query.pq_io_bufsize = query32.pq_io_bufsize; 285 query.pq_io_buf = (char *)(uintptr_t)query32.pq_io_buf; 286 break; 287 #endif /* _MULTI_DATAMODEL */ 288 default: 289 case DDI_MODEL_NONE: 290 if (ddi_copyin((void *)arg, &query, 291 sizeof (pool_query_t), mode) != 0) 292 return (EFAULT); 293 } 294 if (pool_lock_intr() != 0) 295 return (EINTR); 296 if (pool_state == POOL_DISABLED) { 297 pool_unlock(); 298 return (ENOTACTIVE); 299 } 300 if (minor != 0 && pool_buf != NULL) { 301 /* 302 * Return last snapshot if some 303 * transaction is still in progress 304 */ 305 if (kbufsz != 0 && pool_bufsz > kbufsz) { 306 pool_unlock(); 307 return (ENOMEM); 308 } 309 kbuf = pool_buf; 310 kbufsz = size = pool_bufsz; 311 snapshot = 1; 312 } else if (query.pq_io_bufsize != 0) { 313 kbufsz = query.pq_io_bufsize; 314 kbuf = kmem_alloc(kbufsz, KM_NOSLEEP); 315 if (kbuf == NULL) { 316 pool_unlock(); 317 return (ENOMEM); 318 } 319 ret = pool_pack_conf(kbuf, kbufsz, &size); 320 } else { 321 ret = pool_pack_conf(NULL, 0, &size); 322 } 323 if (ret == 0) { 324 switch (model) { 325 #ifdef _MULTI_DATAMODEL 326 case DDI_MODEL_ILP32: 327 query32.pq_io_bufsize = size; 328 if (ddi_copyout((caddr_t)&query32, (void *)arg, 329 sizeof (pool_query32_t), mode) != 0) 330 ret = EFAULT; 331 break; 332 #endif /* _MULTI_DATAMODEL */ 333 default: 334 case DDI_MODEL_NONE: 335 query.pq_io_bufsize = size; 336 if (ddi_copyout(&query, (void *)arg, 337 sizeof (pool_query_t), mode) != 0) 338 ret = EFAULT; 339 } 340 if (ret == 0 && query.pq_io_buf != NULL && 341 ddi_copyout(kbuf, query.pq_io_buf, size, mode) != 0) 342 ret = EFAULT; 343 } 344 pool_unlock(); 345 if (snapshot == 0) 346 kmem_free(kbuf, kbufsz); 347 break; 348 case POOL_CREATE: 349 if (ddi_copyin((void *)arg, 350 &create, sizeof (pool_create_t), mode) != 0) 351 return (EFAULT); 352 if (pool_lock_intr() != 0) 353 return (EINTR); 354 ret = pool_create(create.pc_o_type, 355 create.pc_o_sub_type, &create.pc_i_id); 356 pool_unlock(); 357 if (ret == 0 && ddi_copyout(&create, (void *)arg, 358 sizeof (pool_create_t), mode) != 0) 359 ret = EFAULT; 360 break; 361 case POOL_ASSOC: 362 if (ddi_copyin((void *)arg, &assoc, 363 sizeof (pool_assoc_t), mode) != 0) 364 return (EFAULT); 365 if (pool_lock_intr() != 0) 366 return (EINTR); 367 ret = pool_assoc(assoc.pa_o_pool_id, 368 assoc.pa_o_id_type, assoc.pa_o_res_id); 369 pool_unlock(); 370 break; 371 case POOL_DISSOC: 372 if (ddi_copyin((void *)arg, &dissoc, 373 sizeof (pool_dissoc_t), mode) != 0) 374 return (EFAULT); 375 if (pool_lock_intr() != 0) 376 return (EINTR); 377 ret = pool_dissoc(dissoc.pd_o_pool_id, dissoc.pd_o_id_type); 378 pool_unlock(); 379 break; 380 case POOL_DESTROY: 381 if (ddi_copyin((void *)arg, &destroy, 382 sizeof (pool_destroy_t), mode) != 0) 383 return (EFAULT); 384 if (pool_lock_intr() != 0) 385 return (EINTR); 386 ret = pool_destroy(destroy.pd_o_type, destroy.pd_o_sub_type, 387 destroy.pd_o_id); 388 pool_unlock(); 389 break; 390 case POOL_TRANSFER: 391 if (ddi_copyin((void *)arg, &transfer, 392 sizeof (pool_transfer_t), mode) != 0) 393 return (EFAULT); 394 if (pool_lock_intr() != 0) 395 return (EINTR); 396 ret = pool_transfer(transfer.pt_o_id_type, transfer.pt_o_src_id, 397 transfer.pt_o_tgt_id, transfer.pt_o_qty); 398 pool_unlock(); 399 break; 400 case POOL_XTRANSFER: 401 switch (model) { 402 #ifdef _MULTI_DATAMODEL 403 case DDI_MODEL_ILP32: 404 if (ddi_copyin((void *)arg, &xtransfer32, 405 sizeof (pool_xtransfer32_t), mode) != 0) 406 return (EFAULT); 407 xtransfer.px_o_id_type = xtransfer32.px_o_id_type; 408 xtransfer.px_o_src_id = xtransfer32.px_o_src_id; 409 xtransfer.px_o_tgt_id = xtransfer32.px_o_tgt_id; 410 xtransfer.px_o_complist_size = 411 xtransfer32.px_o_complist_size; 412 xtransfer.px_o_comp_list = 413 (id_t *)(uintptr_t)xtransfer32.px_o_comp_list; 414 break; 415 #endif /* _MULTI_DATAMODEL */ 416 default: 417 case DDI_MODEL_NONE: 418 if (ddi_copyin((void *)arg, &xtransfer, 419 sizeof (pool_xtransfer_t), mode) != 0) 420 return (EFAULT); 421 } 422 /* 423 * Copy in IDs to transfer from the userland 424 */ 425 if (xtransfer.px_o_complist_size > POOL_IDLIST_SIZE) 426 return (EINVAL); 427 id_buf = kmem_alloc(xtransfer.px_o_complist_size * 428 sizeof (id_t), KM_SLEEP); 429 if (ddi_copyin((void *)xtransfer.px_o_comp_list, id_buf, 430 xtransfer.px_o_complist_size * sizeof (id_t), mode) != 0) { 431 kmem_free(id_buf, xtransfer.px_o_complist_size * 432 sizeof (id_t)); 433 return (EFAULT); 434 } 435 if (pool_lock_intr() != 0) { 436 kmem_free(id_buf, xtransfer.px_o_complist_size * 437 sizeof (id_t)); 438 return (EINTR); 439 } 440 ret = pool_xtransfer(xtransfer.px_o_id_type, 441 xtransfer.px_o_src_id, xtransfer.px_o_tgt_id, 442 xtransfer.px_o_complist_size, id_buf); 443 pool_unlock(); 444 kmem_free(id_buf, xtransfer.px_o_complist_size * 445 sizeof (id_t)); 446 break; 447 case POOL_BIND: 448 if (ddi_copyin((void *)arg, &bind, 449 sizeof (pool_bind_t), mode) != 0) 450 return (EFAULT); 451 if (pool_lock_intr() != 0) 452 return (EINTR); 453 ret = pool_bind(bind.pb_o_pool_id, bind.pb_o_id_type, 454 bind.pb_o_id); 455 pool_unlock(); 456 break; 457 case POOL_BINDQ: 458 if (ddi_copyin((void *)arg, &bindq, 459 sizeof (pool_bindq_t), mode) != 0) { 460 return (EFAULT); 461 } 462 if (pool_lock_intr() != 0) 463 return (EINTR); 464 if ((ret = pool_query_binding(bindq.pb_o_id_type, 465 bindq.pb_o_id, &bindq.pb_i_id)) == 0 && 466 ddi_copyout(&bindq, (void *)arg, 467 sizeof (pool_bindq_t), mode) != 0) 468 ret = EFAULT; 469 pool_unlock(); 470 break; 471 case POOL_PROPGET: 472 switch (model) { 473 #ifdef _MULTI_DATAMODEL 474 case DDI_MODEL_ILP32: 475 if (ddi_copyin((void *)arg, &propget32, 476 sizeof (pool_propget32_t), mode) != 0) 477 return (EFAULT); 478 propget.pp_o_id = propget32.pp_o_id; 479 propget.pp_o_id_type = propget32.pp_o_id_type; 480 propget.pp_o_id_subtype = propget32.pp_o_id_subtype; 481 propget.pp_o_prop_name = 482 (char *)(uintptr_t)propget32.pp_o_prop_name; 483 propget.pp_o_prop_name_size = 484 propget32.pp_o_prop_name_size; 485 propget.pp_i_buf = 486 (char *)(uintptr_t)propget32.pp_i_buf; 487 propget.pp_i_bufsize = propget32.pp_i_bufsize; 488 break; 489 #endif /* _MULTI_DATAMODEL */ 490 default: 491 case DDI_MODEL_NONE: 492 if (ddi_copyin((void *)arg, &propget, 493 sizeof (pool_propget_t), mode) != 0) 494 return (EFAULT); 495 } 496 if (propget.pp_o_prop_name_size + 1 > POOL_PROPNAME_SIZE) 497 return (EINVAL); 498 prop_name = kmem_alloc(propget.pp_o_prop_name_size + 1, 499 KM_SLEEP); 500 if (ddi_copyin(propget.pp_o_prop_name, prop_name, 501 propget.pp_o_prop_name_size + 1, mode) != 0) { 502 kmem_free(prop_name, propget.pp_o_prop_name_size + 1); 503 return (EFAULT); 504 } 505 list = NULL; 506 if (pool_lock_intr() != 0) { 507 kmem_free(prop_name, propget.pp_o_prop_name_size + 1); 508 return (EINTR); 509 } 510 ret = pool_propget(prop_name, propget.pp_o_id_type, 511 propget.pp_o_id_subtype, propget.pp_o_id, &list); 512 pool_unlock(); 513 kmem_free(prop_name, propget.pp_o_prop_name_size + 1); 514 if (ret != 0) 515 return (ret); 516 ret = nvlist_pack(list, &kbuf, &kbufsz, NV_ENCODE_NATIVE, 0); 517 if (ret != 0) { 518 nvlist_free(list); 519 return (ret); 520 } 521 switch (model) { 522 #ifdef _MULTI_DATAMODEL 523 case DDI_MODEL_ILP32: 524 propget32.pp_i_bufsize = kbufsz; 525 if (ddi_copyout((caddr_t)&propget32, (void *)arg, 526 sizeof (pool_propget32_t), mode) != 0) 527 ret = EFAULT; 528 break; 529 #endif /* _MULTI_DATAMODEL */ 530 default: 531 case DDI_MODEL_NONE: 532 if (ddi_copyout(&propget, (void *)arg, 533 sizeof (pool_propget_t), mode) != 0) 534 ret = EFAULT; 535 } 536 if (ret == 0) { 537 if (propget.pp_i_buf == NULL) { 538 ret = 0; 539 } else if (propget.pp_i_bufsize >= kbufsz) { 540 if (ddi_copyout(kbuf, propget.pp_i_buf, 541 kbufsz, mode) != 0) 542 ret = EFAULT; 543 } else { 544 ret = ENOMEM; 545 } 546 } 547 kmem_free(kbuf, kbufsz); 548 nvlist_free(list); 549 break; 550 case POOL_PROPPUT: 551 switch (model) { 552 #ifdef _MULTI_DATAMODEL 553 case DDI_MODEL_ILP32: 554 if (ddi_copyin((void *)arg, &propput32, 555 sizeof (pool_propput32_t), mode) != 0) 556 return (EFAULT); 557 propput.pp_o_id_type = propput32.pp_o_id_type; 558 propput.pp_o_id_sub_type = propput32.pp_o_id_sub_type; 559 propput.pp_o_id = propput32.pp_o_id; 560 propput.pp_o_bufsize = propput32.pp_o_bufsize; 561 propput.pp_o_buf = 562 (char *)(uintptr_t)propput32.pp_o_buf; 563 break; 564 #endif /* _MULTI_DATAMODEL */ 565 default: 566 case DDI_MODEL_NONE: 567 if (ddi_copyin((void *)arg, &propput, 568 sizeof (pool_propput_t), mode) != 0) 569 return (EFAULT); 570 } 571 if (propput.pp_o_bufsize > POOL_PROPBUF_SIZE) 572 return (EINVAL); 573 listbuf = kmem_alloc(propput.pp_o_bufsize, KM_SLEEP); 574 if (ddi_copyin(propput.pp_o_buf, listbuf, 575 propput.pp_o_bufsize, mode) != 0) { 576 kmem_free(listbuf, propput.pp_o_bufsize); 577 return (EFAULT); 578 } 579 if (nvlist_unpack(listbuf, propput.pp_o_bufsize, 580 &list, KM_SLEEP) != 0) { 581 kmem_free(listbuf, propput.pp_o_bufsize); 582 return (EFAULT); 583 } 584 if (pool_lock_intr() != 0) { 585 nvlist_free(list); 586 kmem_free(listbuf, propput.pp_o_bufsize); 587 return (EINTR); 588 } 589 /* 590 * Extract the nvpair from the list. The list may 591 * contain multiple properties. 592 */ 593 for (pair = nvlist_next_nvpair(list, NULL); pair != NULL; 594 pair = nvlist_next_nvpair(list, pair)) { 595 if ((ret = pool_propput(propput.pp_o_id_type, 596 propput.pp_o_id_sub_type, 597 propput.pp_o_id, pair)) != 0) 598 break; 599 } 600 pool_unlock(); 601 nvlist_free(list); 602 kmem_free(listbuf, propput.pp_o_bufsize); 603 break; 604 case POOL_PROPRM: 605 switch (model) { 606 #ifdef _MULTI_DATAMODEL 607 case DDI_MODEL_ILP32: 608 if (ddi_copyin((void *)arg, &proprm32, 609 sizeof (pool_proprm32_t), mode) != 0) 610 return (EFAULT); 611 proprm.pp_o_id_type = proprm32.pp_o_id_type; 612 proprm.pp_o_id_sub_type = proprm32.pp_o_id_sub_type; 613 proprm.pp_o_id = proprm32.pp_o_id; 614 proprm.pp_o_prop_name_size = 615 proprm32.pp_o_prop_name_size; 616 proprm.pp_o_prop_name = 617 (void *)(uintptr_t)proprm32.pp_o_prop_name; 618 break; 619 #endif /* _MULTI_DATAMODEL */ 620 default: 621 case DDI_MODEL_NONE: 622 if (ddi_copyin((void *)arg, &proprm, 623 sizeof (pool_proprm_t), mode) != 0) 624 return (EFAULT); 625 } 626 if (proprm.pp_o_prop_name_size + 1 > POOL_PROPNAME_SIZE) 627 return (EINVAL); 628 prop_name = kmem_alloc(proprm.pp_o_prop_name_size + 1, 629 KM_SLEEP); 630 if (ddi_copyin(proprm.pp_o_prop_name, prop_name, 631 proprm.pp_o_prop_name_size + 1, mode) != 0) { 632 kmem_free(prop_name, proprm.pp_o_prop_name_size + 1); 633 return (EFAULT); 634 } 635 if (pool_lock_intr() != 0) { 636 kmem_free(prop_name, proprm.pp_o_prop_name_size + 1); 637 return (EINTR); 638 } 639 ret = pool_proprm(proprm.pp_o_id_type, 640 proprm.pp_o_id_sub_type, proprm.pp_o_id, prop_name); 641 pool_unlock(); 642 kmem_free(prop_name, proprm.pp_o_prop_name_size + 1); 643 break; 644 case POOL_COMMIT: 645 if (pool_lock_intr() != 0) 646 return (EINTR); 647 ret = pool_commit((int)arg); 648 pool_unlock(); 649 break; 650 default: 651 return (EINVAL); 652 } 653 return (ret); 654 } 655 656 static struct cb_ops pool_cb_ops = { 657 pool_open, /* open */ 658 pool_close, /* close */ 659 nodev, /* strategy */ 660 nodev, /* print */ 661 nodev, /* dump */ 662 nodev, /* read */ 663 nodev, /* write */ 664 pool_ioctl, /* ioctl */ 665 nodev, /* devmap */ 666 nodev, /* mmap */ 667 nodev, /* segmap */ 668 nochpoll, /* poll */ 669 nodev, /* cb_prop_op */ 670 (struct streamtab *)0, /* streamtab */ 671 D_NEW | D_MP /* driver compatibility flags */ 672 }; 673 674 static struct dev_ops pool_ops = { 675 DEVO_REV, /* devo_rev */ 676 0, /* refcnt */ 677 pool_info, /* info */ 678 nulldev, /* identify */ 679 nulldev, /* probe */ 680 pool_attach, /* attach */ 681 pool_detach, /* detach */ 682 nodev, /* reset */ 683 &pool_cb_ops, /* cb_ops */ 684 (struct bus_ops *)NULL, /* bus_ops */ 685 nulldev, /* power */ 686 ddi_quiesce_not_needed, /* quiesce */ 687 }; 688 689 /* 690 * Module linkage information for the kernel 691 */ 692 static struct modldrv modldrv = { 693 &mod_driverops, /* this one is a pseudo driver */ 694 "pool driver", 695 &pool_ops 696 }; 697 698 static struct modlinkage modlinkage = { 699 MODREV_1, 700 &modldrv, 701 NULL 702 }; 703 704 int 705 _init(void) 706 { 707 return (mod_install(&modlinkage)); 708 } 709 710 int 711 _fini(void) 712 { 713 return (mod_remove(&modlinkage)); 714 } 715 716 int 717 _info(struct modinfo *modinfop) 718 { 719 return (mod_info(&modlinkage, modinfop)); 720 } 721