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