11991Sheppo /* 21991Sheppo * CDDL HEADER START 31991Sheppo * 41991Sheppo * The contents of this file are subject to the terms of the 51991Sheppo * Common Development and Distribution License (the "License"). 61991Sheppo * You may not use this file except in compliance with the License. 71991Sheppo * 81991Sheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91991Sheppo * or http://www.opensolaris.org/os/licensing. 101991Sheppo * See the License for the specific language governing permissions 111991Sheppo * and limitations under the License. 121991Sheppo * 131991Sheppo * When distributing Covered Code, include this CDDL HEADER in each 141991Sheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151991Sheppo * If applicable, add the following below this CDDL HEADER, with the 161991Sheppo * fields enclosed by brackets "[]" replaced with your own identifying 171991Sheppo * information: Portions Copyright [yyyy] [name of copyright owner] 181991Sheppo * 191991Sheppo * CDDL HEADER END 201991Sheppo */ 211991Sheppo 221991Sheppo /* 233401Snarayan * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 241991Sheppo * Use is subject to license terms. 251991Sheppo */ 261991Sheppo 271991Sheppo #pragma ident "%Z%%M% %I% %E% SMI" 281991Sheppo 291991Sheppo /* 301991Sheppo * Virtual disk server 311991Sheppo */ 321991Sheppo 331991Sheppo 341991Sheppo #include <sys/types.h> 351991Sheppo #include <sys/conf.h> 362531Snarayan #include <sys/crc32.h> 371991Sheppo #include <sys/ddi.h> 381991Sheppo #include <sys/dkio.h> 391991Sheppo #include <sys/file.h> 40*5365Slm66018 #include <sys/fs/hsfs_isospec.h> 411991Sheppo #include <sys/mdeg.h> 421991Sheppo #include <sys/modhash.h> 431991Sheppo #include <sys/note.h> 441991Sheppo #include <sys/pathname.h> 454838Slm66018 #include <sys/sdt.h> 461991Sheppo #include <sys/sunddi.h> 471991Sheppo #include <sys/sunldi.h> 481991Sheppo #include <sys/sysmacros.h> 491991Sheppo #include <sys/vio_common.h> 50*5365Slm66018 #include <sys/vio_util.h> 511991Sheppo #include <sys/vdsk_mailbox.h> 521991Sheppo #include <sys/vdsk_common.h> 531991Sheppo #include <sys/vtoc.h> 543401Snarayan #include <sys/vfs.h> 553401Snarayan #include <sys/stat.h> 564696Sachartre #include <sys/scsi/impl/uscsi.h> 573782Sachartre #include <vm/seg_map.h> 581991Sheppo 591991Sheppo /* Virtual disk server initialization flags */ 602336Snarayan #define VDS_LDI 0x01 612336Snarayan #define VDS_MDEG 0x02 621991Sheppo 631991Sheppo /* Virtual disk server tunable parameters */ 643401Snarayan #define VDS_RETRIES 5 653401Snarayan #define VDS_LDC_DELAY 1000 /* 1 msecs */ 663401Snarayan #define VDS_DEV_DELAY 10000000 /* 10 secs */ 671991Sheppo #define VDS_NCHAINS 32 681991Sheppo 691991Sheppo /* Identification parameters for MD, synthetic dkio(7i) structures, etc. */ 701991Sheppo #define VDS_NAME "virtual-disk-server" 711991Sheppo 721991Sheppo #define VD_NAME "vd" 731991Sheppo #define VD_VOLUME_NAME "vdisk" 741991Sheppo #define VD_ASCIILABEL "Virtual Disk" 751991Sheppo 761991Sheppo #define VD_CHANNEL_ENDPOINT "channel-endpoint" 771991Sheppo #define VD_ID_PROP "id" 781991Sheppo #define VD_BLOCK_DEVICE_PROP "vds-block-device" 795081Sachartre #define VD_BLOCK_DEVICE_OPTS "vds-block-device-opts" 803297Ssb155480 #define VD_REG_PROP "reg" 811991Sheppo 821991Sheppo /* Virtual disk initialization flags */ 833401Snarayan #define VD_DISK_READY 0x01 843401Snarayan #define VD_LOCKING 0x02 853401Snarayan #define VD_LDC 0x04 863401Snarayan #define VD_DRING 0x08 873401Snarayan #define VD_SID 0x10 883401Snarayan #define VD_SEQ_NUM 0x20 895081Sachartre #define VD_SETUP_ERROR 0x40 901991Sheppo 914076Sachartre /* Flags for writing to a vdisk which is a file */ 924076Sachartre #define VD_FILE_WRITE_FLAGS SM_ASYNC 934076Sachartre 944696Sachartre /* Number of backup labels */ 954696Sachartre #define VD_FILE_NUM_BACKUP 5 964696Sachartre 974696Sachartre /* Timeout for SCSI I/O */ 984696Sachartre #define VD_SCSI_RDWR_TIMEOUT 30 /* 30 secs */ 994696Sachartre 1001991Sheppo /* 1011991Sheppo * By Solaris convention, slice/partition 2 represents the entire disk; 1021991Sheppo * unfortunately, this convention does not appear to be codified. 1031991Sheppo */ 1041991Sheppo #define VD_ENTIRE_DISK_SLICE 2 1051991Sheppo 1061991Sheppo /* Return a cpp token as a string */ 1071991Sheppo #define STRINGIZE(token) #token 1081991Sheppo 1091991Sheppo /* 1101991Sheppo * Print a message prefixed with the current function name to the message log 1111991Sheppo * (and optionally to the console for verbose boots); these macros use cpp's 1121991Sheppo * concatenation of string literals and C99 variable-length-argument-list 1131991Sheppo * macros 1141991Sheppo */ 1151991Sheppo #define PRN(...) _PRN("?%s(): "__VA_ARGS__, "") 1161991Sheppo #define _PRN(format, ...) \ 1171991Sheppo cmn_err(CE_CONT, format"%s", __func__, __VA_ARGS__) 1181991Sheppo 1191991Sheppo /* Return a pointer to the "i"th vdisk dring element */ 1201991Sheppo #define VD_DRING_ELEM(i) ((vd_dring_entry_t *)(void *) \ 1211991Sheppo (vd->dring + (i)*vd->descriptor_size)) 1221991Sheppo 1231991Sheppo /* Return the virtual disk client's type as a string (for use in messages) */ 1241991Sheppo #define VD_CLIENT(vd) \ 1251991Sheppo (((vd)->xfer_mode == VIO_DESC_MODE) ? "in-band client" : \ 1261991Sheppo (((vd)->xfer_mode == VIO_DRING_MODE) ? "dring client" : \ 1271991Sheppo (((vd)->xfer_mode == 0) ? "null client" : \ 1281991Sheppo "unsupported client"))) 1291991Sheppo 1303782Sachartre /* Read disk label from a disk on file */ 1313782Sachartre #define VD_FILE_LABEL_READ(vd, labelp) \ 1324696Sachartre vd_file_rw(vd, VD_SLICE_NONE, VD_OP_BREAD, (caddr_t)labelp, \ 1333782Sachartre 0, sizeof (struct dk_label)) 1343782Sachartre 1353782Sachartre /* Write disk label to a disk on file */ 1363782Sachartre #define VD_FILE_LABEL_WRITE(vd, labelp) \ 1374696Sachartre vd_file_rw(vd, VD_SLICE_NONE, VD_OP_BWRITE, (caddr_t)labelp, \ 1383782Sachartre 0, sizeof (struct dk_label)) 1393782Sachartre 1403297Ssb155480 /* 1413297Ssb155480 * Specification of an MD node passed to the MDEG to filter any 1423297Ssb155480 * 'vport' nodes that do not belong to the specified node. This 1433297Ssb155480 * template is copied for each vds instance and filled in with 1443297Ssb155480 * the appropriate 'cfg-handle' value before being passed to the MDEG. 1453297Ssb155480 */ 1463297Ssb155480 static mdeg_prop_spec_t vds_prop_template[] = { 1473297Ssb155480 { MDET_PROP_STR, "name", VDS_NAME }, 1483297Ssb155480 { MDET_PROP_VAL, "cfg-handle", NULL }, 1493297Ssb155480 { MDET_LIST_END, NULL, NULL } 1503297Ssb155480 }; 1513297Ssb155480 1523297Ssb155480 #define VDS_SET_MDEG_PROP_INST(specp, val) (specp)[1].ps_val = (val); 1533297Ssb155480 1543297Ssb155480 /* 1553297Ssb155480 * Matching criteria passed to the MDEG to register interest 1563297Ssb155480 * in changes to 'virtual-device-port' nodes identified by their 1573297Ssb155480 * 'id' property. 1583297Ssb155480 */ 1593297Ssb155480 static md_prop_match_t vd_prop_match[] = { 1603297Ssb155480 { MDET_PROP_VAL, VD_ID_PROP }, 1613297Ssb155480 { MDET_LIST_END, NULL } 1623297Ssb155480 }; 1633297Ssb155480 1643297Ssb155480 static mdeg_node_match_t vd_match = {"virtual-device-port", 1653297Ssb155480 vd_prop_match}; 1663297Ssb155480 1675081Sachartre /* 1685081Sachartre * Options for the VD_BLOCK_DEVICE_OPTS property. 1695081Sachartre */ 1705081Sachartre #define VD_OPT_RDONLY 0x1 /* read-only */ 1715081Sachartre #define VD_OPT_SLICE 0x2 /* single slice */ 1725081Sachartre #define VD_OPT_EXCLUSIVE 0x4 /* exclusive access */ 1735081Sachartre 1745081Sachartre #define VD_OPTION_NLEN 128 1755081Sachartre 1765081Sachartre typedef struct vd_option { 1775081Sachartre char vdo_name[VD_OPTION_NLEN]; 1785081Sachartre uint64_t vdo_value; 1795081Sachartre } vd_option_t; 1805081Sachartre 1815081Sachartre vd_option_t vd_bdev_options[] = { 1825081Sachartre { "ro", VD_OPT_RDONLY }, 1835081Sachartre { "slice", VD_OPT_SLICE }, 1845081Sachartre { "excl", VD_OPT_EXCLUSIVE } 1855081Sachartre }; 1865081Sachartre 1871991Sheppo /* Debugging macros */ 1881991Sheppo #ifdef DEBUG 1892793Slm66018 1902793Slm66018 static int vd_msglevel = 0; 1912793Slm66018 1921991Sheppo #define PR0 if (vd_msglevel > 0) PRN 1931991Sheppo #define PR1 if (vd_msglevel > 1) PRN 1941991Sheppo #define PR2 if (vd_msglevel > 2) PRN 1951991Sheppo 1961991Sheppo #define VD_DUMP_DRING_ELEM(elem) \ 1973401Snarayan PR0("dst:%x op:%x st:%u nb:%lx addr:%lx ncook:%u\n", \ 1981991Sheppo elem->hdr.dstate, \ 1991991Sheppo elem->payload.operation, \ 2001991Sheppo elem->payload.status, \ 2011991Sheppo elem->payload.nbytes, \ 2021991Sheppo elem->payload.addr, \ 2031991Sheppo elem->payload.ncookies); 2041991Sheppo 2052793Slm66018 char * 2062793Slm66018 vd_decode_state(int state) 2072793Slm66018 { 2082793Slm66018 char *str; 2092793Slm66018 2102793Slm66018 #define CASE_STATE(_s) case _s: str = #_s; break; 2112793Slm66018 2122793Slm66018 switch (state) { 2132793Slm66018 CASE_STATE(VD_STATE_INIT) 2142793Slm66018 CASE_STATE(VD_STATE_VER) 2152793Slm66018 CASE_STATE(VD_STATE_ATTR) 2162793Slm66018 CASE_STATE(VD_STATE_DRING) 2172793Slm66018 CASE_STATE(VD_STATE_RDX) 2182793Slm66018 CASE_STATE(VD_STATE_DATA) 2192793Slm66018 default: str = "unknown"; break; 2202793Slm66018 } 2212793Slm66018 2222793Slm66018 #undef CASE_STATE 2232793Slm66018 2242793Slm66018 return (str); 2252793Slm66018 } 2262793Slm66018 2272793Slm66018 void 2282793Slm66018 vd_decode_tag(vio_msg_t *msg) 2292793Slm66018 { 2302793Slm66018 char *tstr, *sstr, *estr; 2312793Slm66018 2322793Slm66018 #define CASE_TYPE(_s) case _s: tstr = #_s; break; 2332793Slm66018 2342793Slm66018 switch (msg->tag.vio_msgtype) { 2352793Slm66018 CASE_TYPE(VIO_TYPE_CTRL) 2362793Slm66018 CASE_TYPE(VIO_TYPE_DATA) 2372793Slm66018 CASE_TYPE(VIO_TYPE_ERR) 2382793Slm66018 default: tstr = "unknown"; break; 2392793Slm66018 } 2402793Slm66018 2412793Slm66018 #undef CASE_TYPE 2422793Slm66018 2432793Slm66018 #define CASE_SUBTYPE(_s) case _s: sstr = #_s; break; 2442793Slm66018 2452793Slm66018 switch (msg->tag.vio_subtype) { 2462793Slm66018 CASE_SUBTYPE(VIO_SUBTYPE_INFO) 2472793Slm66018 CASE_SUBTYPE(VIO_SUBTYPE_ACK) 2482793Slm66018 CASE_SUBTYPE(VIO_SUBTYPE_NACK) 2492793Slm66018 default: sstr = "unknown"; break; 2502793Slm66018 } 2512793Slm66018 2522793Slm66018 #undef CASE_SUBTYPE 2532793Slm66018 2542793Slm66018 #define CASE_ENV(_s) case _s: estr = #_s; break; 2552793Slm66018 2562793Slm66018 switch (msg->tag.vio_subtype_env) { 2572793Slm66018 CASE_ENV(VIO_VER_INFO) 2582793Slm66018 CASE_ENV(VIO_ATTR_INFO) 2592793Slm66018 CASE_ENV(VIO_DRING_REG) 2602793Slm66018 CASE_ENV(VIO_DRING_UNREG) 2612793Slm66018 CASE_ENV(VIO_RDX) 2622793Slm66018 CASE_ENV(VIO_PKT_DATA) 2632793Slm66018 CASE_ENV(VIO_DESC_DATA) 2642793Slm66018 CASE_ENV(VIO_DRING_DATA) 2652793Slm66018 default: estr = "unknown"; break; 2662793Slm66018 } 2672793Slm66018 2682793Slm66018 #undef CASE_ENV 2692793Slm66018 2702793Slm66018 PR1("(%x/%x/%x) message : (%s/%s/%s)", 2712793Slm66018 msg->tag.vio_msgtype, msg->tag.vio_subtype, 2722793Slm66018 msg->tag.vio_subtype_env, tstr, sstr, estr); 2732793Slm66018 } 2742793Slm66018 2751991Sheppo #else /* !DEBUG */ 2762793Slm66018 2771991Sheppo #define PR0(...) 2781991Sheppo #define PR1(...) 2791991Sheppo #define PR2(...) 2801991Sheppo 2811991Sheppo #define VD_DUMP_DRING_ELEM(elem) 2821991Sheppo 2832793Slm66018 #define vd_decode_state(_s) (NULL) 2842793Slm66018 #define vd_decode_tag(_s) (NULL) 2852793Slm66018 2861991Sheppo #endif /* DEBUG */ 2871991Sheppo 2881991Sheppo 2892336Snarayan /* 2902336Snarayan * Soft state structure for a vds instance 2912336Snarayan */ 2921991Sheppo typedef struct vds { 2931991Sheppo uint_t initialized; /* driver inst initialization flags */ 2941991Sheppo dev_info_t *dip; /* driver inst devinfo pointer */ 2951991Sheppo ldi_ident_t ldi_ident; /* driver's identifier for LDI */ 2961991Sheppo mod_hash_t *vd_table; /* table of virtual disks served */ 2973297Ssb155480 mdeg_node_spec_t *ispecp; /* mdeg node specification */ 2981991Sheppo mdeg_handle_t mdeg; /* handle for MDEG operations */ 2991991Sheppo } vds_t; 3001991Sheppo 3012336Snarayan /* 3022336Snarayan * Types of descriptor-processing tasks 3032336Snarayan */ 3042336Snarayan typedef enum vd_task_type { 3052336Snarayan VD_NONFINAL_RANGE_TASK, /* task for intermediate descriptor in range */ 3062336Snarayan VD_FINAL_RANGE_TASK, /* task for last in a range of descriptors */ 3072336Snarayan } vd_task_type_t; 3082336Snarayan 3092336Snarayan /* 3102336Snarayan * Structure describing the task for processing a descriptor 3112336Snarayan */ 3122336Snarayan typedef struct vd_task { 3132336Snarayan struct vd *vd; /* vd instance task is for */ 3142336Snarayan vd_task_type_t type; /* type of descriptor task */ 3152336Snarayan int index; /* dring elem index for task */ 3162336Snarayan vio_msg_t *msg; /* VIO message task is for */ 3172336Snarayan size_t msglen; /* length of message content */ 3182336Snarayan vd_dring_payload_t *request; /* request task will perform */ 3192336Snarayan struct buf buf; /* buf(9s) for I/O request */ 3202531Snarayan ldc_mem_handle_t mhdl; /* task memory handle */ 3214838Slm66018 int status; /* status of processing task */ 3224838Slm66018 int (*completef)(struct vd_task *task); /* completion func ptr */ 3232336Snarayan } vd_task_t; 3242336Snarayan 3252336Snarayan /* 3262336Snarayan * Soft state structure for a virtual disk instance 3272336Snarayan */ 3281991Sheppo typedef struct vd { 3291991Sheppo uint_t initialized; /* vdisk initialization flags */ 330*5365Slm66018 uint64_t operations; /* bitmask of VD_OPs exported */ 331*5365Slm66018 vio_ver_t version; /* ver negotiated with client */ 3321991Sheppo vds_t *vds; /* server for this vdisk */ 3332336Snarayan ddi_taskq_t *startq; /* queue for I/O start tasks */ 3342336Snarayan ddi_taskq_t *completionq; /* queue for completion tasks */ 3351991Sheppo ldi_handle_t ldi_handle[V_NUMPAR]; /* LDI slice handles */ 3363401Snarayan char device_path[MAXPATHLEN + 1]; /* vdisk device */ 3371991Sheppo dev_t dev[V_NUMPAR]; /* dev numbers for slices */ 3385081Sachartre int open_flags; /* open flags */ 3392410Slm66018 uint_t nslices; /* number of slices */ 3401991Sheppo size_t vdisk_size; /* number of blocks in vdisk */ 341*5365Slm66018 size_t vdisk_block_size; /* size of each vdisk block */ 3421991Sheppo vd_disk_type_t vdisk_type; /* slice or entire disk */ 3432531Snarayan vd_disk_label_t vdisk_label; /* EFI or VTOC label */ 344*5365Slm66018 vd_media_t vdisk_media; /* media type of backing dev. */ 345*5365Slm66018 boolean_t is_atapi_dev; /* Is this an IDE CD-ROM dev? */ 3462410Slm66018 ushort_t max_xfer_sz; /* max xfer size in DEV_BSIZE */ 347*5365Slm66018 size_t block_size; /* blk size of actual device */ 3481991Sheppo boolean_t pseudo; /* underlying pseudo dev */ 349*5365Slm66018 boolean_t file; /* is vDisk backed by a file? */ 3503401Snarayan vnode_t *file_vnode; /* file vnode */ 3513401Snarayan size_t file_size; /* file size */ 3524696Sachartre ddi_devid_t file_devid; /* devid for disk image */ 3532531Snarayan struct dk_efi dk_efi; /* synthetic for slice type */ 3541991Sheppo struct dk_geom dk_geom; /* synthetic for slice type */ 355*5365Slm66018 struct dk_minfo dk_minfo; /* synthetic for slice type */ 3561991Sheppo struct vtoc vtoc; /* synthetic for slice type */ 3571991Sheppo ldc_status_t ldc_state; /* LDC connection state */ 3581991Sheppo ldc_handle_t ldc_handle; /* handle for LDC comm */ 3591991Sheppo size_t max_msglen; /* largest LDC message len */ 3601991Sheppo vd_state_t state; /* client handshake state */ 3611991Sheppo uint8_t xfer_mode; /* transfer mode with client */ 3621991Sheppo uint32_t sid; /* client's session ID */ 3631991Sheppo uint64_t seq_num; /* message sequence number */ 3641991Sheppo uint64_t dring_ident; /* identifier of dring */ 3651991Sheppo ldc_dring_handle_t dring_handle; /* handle for dring ops */ 3661991Sheppo uint32_t descriptor_size; /* num bytes in desc */ 3671991Sheppo uint32_t dring_len; /* number of dring elements */ 3681991Sheppo caddr_t dring; /* address of dring */ 3692793Slm66018 caddr_t vio_msgp; /* vio msg staging buffer */ 3702336Snarayan vd_task_t inband_task; /* task for inband descriptor */ 3712336Snarayan vd_task_t *dring_task; /* tasks dring elements */ 3722336Snarayan 3732336Snarayan kmutex_t lock; /* protects variables below */ 3742336Snarayan boolean_t enabled; /* is vdisk enabled? */ 3752336Snarayan boolean_t reset_state; /* reset connection state? */ 3762336Snarayan boolean_t reset_ldc; /* reset LDC channel? */ 3771991Sheppo } vd_t; 3781991Sheppo 3791991Sheppo typedef struct vds_operation { 3802793Slm66018 char *namep; 3811991Sheppo uint8_t operation; 3822336Snarayan int (*start)(vd_task_t *task); 3834838Slm66018 int (*complete)(vd_task_t *task); 3841991Sheppo } vds_operation_t; 3851991Sheppo 3862032Slm66018 typedef struct vd_ioctl { 3872032Slm66018 uint8_t operation; /* vdisk operation */ 3882032Slm66018 const char *operation_name; /* vdisk operation name */ 3892032Slm66018 size_t nbytes; /* size of operation buffer */ 3902032Slm66018 int cmd; /* corresponding ioctl cmd */ 3912032Slm66018 const char *cmd_name; /* ioctl cmd name */ 3922032Slm66018 void *arg; /* ioctl cmd argument */ 3932032Slm66018 /* convert input vd_buf to output ioctl_arg */ 3942032Slm66018 void (*copyin)(void *vd_buf, void *ioctl_arg); 3952032Slm66018 /* convert input ioctl_arg to output vd_buf */ 3962032Slm66018 void (*copyout)(void *ioctl_arg, void *vd_buf); 3975081Sachartre /* write is true if the operation writes any data to the backend */ 3985081Sachartre boolean_t write; 3992032Slm66018 } vd_ioctl_t; 4002032Slm66018 4012032Slm66018 /* Define trivial copyin/copyout conversion function flag */ 4022032Slm66018 #define VD_IDENTITY ((void (*)(void *, void *))-1) 4031991Sheppo 4041991Sheppo 4053401Snarayan static int vds_ldc_retries = VDS_RETRIES; 4062793Slm66018 static int vds_ldc_delay = VDS_LDC_DELAY; 4073401Snarayan static int vds_dev_retries = VDS_RETRIES; 4083401Snarayan static int vds_dev_delay = VDS_DEV_DELAY; 4091991Sheppo static void *vds_state; 4101991Sheppo 4114076Sachartre static uint_t vd_file_write_flags = VD_FILE_WRITE_FLAGS; 4124076Sachartre 4134696Sachartre static short vd_scsi_rdwr_timeout = VD_SCSI_RDWR_TIMEOUT; 4144696Sachartre 4152032Slm66018 /* 4162032Slm66018 * Supported protocol version pairs, from highest (newest) to lowest (oldest) 4172032Slm66018 * 4182032Slm66018 * Each supported major version should appear only once, paired with (and only 4192032Slm66018 * with) its highest supported minor version number (as the protocol requires 4202032Slm66018 * supporting all lower minor version numbers as well) 4212032Slm66018 */ 422*5365Slm66018 static const vio_ver_t vds_version[] = {{1, 1}}; 4232032Slm66018 static const size_t vds_num_versions = 4242032Slm66018 sizeof (vds_version)/sizeof (vds_version[0]); 4252032Slm66018 4262793Slm66018 static void vd_free_dring_task(vd_t *vdp); 4273401Snarayan static int vd_setup_vd(vd_t *vd); 4285081Sachartre static int vd_setup_single_slice_disk(vd_t *vd); 4293401Snarayan static boolean_t vd_enabled(vd_t *vd); 4304963Sachartre static ushort_t vd_lbl2cksum(struct dk_label *label); 4314963Sachartre static int vd_file_validate_geometry(vd_t *vd); 432*5365Slm66018 static boolean_t vd_file_is_iso_image(vd_t *vd); 433*5365Slm66018 static void vd_set_exported_operations(vd_t *vd); 4345081Sachartre 4353782Sachartre /* 4363782Sachartre * Function: 4373782Sachartre * vd_file_rw 4383782Sachartre * 4393782Sachartre * Description: 4403782Sachartre * Read or write to a disk on file. 4413782Sachartre * 4423782Sachartre * Parameters: 4433782Sachartre * vd - disk on which the operation is performed. 4443782Sachartre * slice - slice on which the operation is performed, 4454696Sachartre * VD_SLICE_NONE indicates that the operation 4464696Sachartre * is done using an absolute disk offset. 4473782Sachartre * operation - operation to execute: read (VD_OP_BREAD) or 4483782Sachartre * write (VD_OP_BWRITE). 4493782Sachartre * data - buffer where data are read to or written from. 4503782Sachartre * blk - starting block for the operation. 4513782Sachartre * len - number of bytes to read or write. 4523782Sachartre * 4533782Sachartre * Return Code: 4543782Sachartre * n >= 0 - success, n indicates the number of bytes read 4553782Sachartre * or written. 4563782Sachartre * -1 - error. 4573782Sachartre */ 4583782Sachartre static ssize_t 4593782Sachartre vd_file_rw(vd_t *vd, int slice, int operation, caddr_t data, size_t blk, 4603782Sachartre size_t len) 4613782Sachartre { 4623782Sachartre caddr_t maddr; 4633782Sachartre size_t offset, maxlen, moffset, mlen, n; 4643782Sachartre uint_t smflags; 4653782Sachartre enum seg_rw srw; 4663782Sachartre 4673782Sachartre ASSERT(vd->file); 4683782Sachartre ASSERT(len > 0); 4693782Sachartre 4705081Sachartre /* 4715081Sachartre * If a file is exported as a slice then we don't care about the vtoc. 4725081Sachartre * In that case, the vtoc is a fake mainly to make newfs happy and we 4735081Sachartre * handle any I/O as a raw disk access so that we can have access to the 4745081Sachartre * entire backend. 4755081Sachartre */ 4765081Sachartre if (vd->vdisk_type == VD_DISK_TYPE_SLICE || slice == VD_SLICE_NONE) { 4773782Sachartre /* raw disk access */ 4783782Sachartre offset = blk * DEV_BSIZE; 4793782Sachartre } else { 4803782Sachartre ASSERT(slice >= 0 && slice < V_NUMPAR); 4814963Sachartre 482*5365Slm66018 /* 483*5365Slm66018 * v1.0 vDisk clients depended on the server not verifying 484*5365Slm66018 * the label of a unformatted disk. This "feature" is 485*5365Slm66018 * maintained for backward compatibility but all versions 486*5365Slm66018 * from v1.1 onwards must do the right thing. 487*5365Slm66018 */ 4884963Sachartre if (vd->vdisk_label == VD_DISK_LABEL_UNK && 489*5365Slm66018 vio_ver_is_supported(vd->version, 1, 1) && 4904963Sachartre vd_file_validate_geometry(vd) != 0) { 4914963Sachartre PR0("Unknown disk label, can't do I/O from slice %d", 4924963Sachartre slice); 4934963Sachartre return (-1); 4944963Sachartre } 4954963Sachartre 4963782Sachartre if (blk >= vd->vtoc.v_part[slice].p_size) { 4973782Sachartre /* address past the end of the slice */ 4983782Sachartre PR0("req_addr (0x%lx) > psize (0x%lx)", 4993782Sachartre blk, vd->vtoc.v_part[slice].p_size); 5003782Sachartre return (0); 5013782Sachartre } 5023782Sachartre 5033782Sachartre offset = (vd->vtoc.v_part[slice].p_start + blk) * DEV_BSIZE; 5043782Sachartre 5053782Sachartre /* 5063782Sachartre * If the requested size is greater than the size 5073782Sachartre * of the partition, truncate the read/write. 5083782Sachartre */ 5093782Sachartre maxlen = (vd->vtoc.v_part[slice].p_size - blk) * DEV_BSIZE; 5103782Sachartre 5113782Sachartre if (len > maxlen) { 5123782Sachartre PR0("I/O size truncated to %lu bytes from %lu bytes", 5133782Sachartre maxlen, len); 5143782Sachartre len = maxlen; 5153782Sachartre } 5163782Sachartre } 5173782Sachartre 5183782Sachartre /* 5193782Sachartre * We have to ensure that we are reading/writing into the mmap 5203782Sachartre * range. If we have a partial disk image (e.g. an image of 5213782Sachartre * s0 instead s2) the system can try to access slices that 5223782Sachartre * are not included into the disk image. 5233782Sachartre */ 5243782Sachartre if ((offset + len) >= vd->file_size) { 5253782Sachartre PR0("offset + nbytes (0x%lx + 0x%lx) >= " 5263782Sachartre "file_size (0x%lx)", offset, len, vd->file_size); 5273782Sachartre return (-1); 5283782Sachartre } 5293782Sachartre 5303782Sachartre srw = (operation == VD_OP_BREAD)? S_READ : S_WRITE; 5314076Sachartre smflags = (operation == VD_OP_BREAD)? 0 : 5324076Sachartre (SM_WRITE | vd_file_write_flags); 5333782Sachartre n = len; 5343782Sachartre 5353782Sachartre do { 5363782Sachartre /* 5373782Sachartre * segmap_getmapflt() returns a MAXBSIZE chunk which is 5383782Sachartre * MAXBSIZE aligned. 5393782Sachartre */ 5403782Sachartre moffset = offset & MAXBOFFSET; 5413782Sachartre mlen = MIN(MAXBSIZE - moffset, n); 5423782Sachartre maddr = segmap_getmapflt(segkmap, vd->file_vnode, offset, 5433782Sachartre mlen, 1, srw); 5443782Sachartre /* 5453782Sachartre * Fault in the pages so we can check for error and ensure 5463782Sachartre * that we can safely used the mapped address. 5473782Sachartre */ 5483782Sachartre if (segmap_fault(kas.a_hat, segkmap, maddr, mlen, 5493782Sachartre F_SOFTLOCK, srw) != 0) { 5503782Sachartre (void) segmap_release(segkmap, maddr, 0); 5513782Sachartre return (-1); 5523782Sachartre } 5533782Sachartre 5543782Sachartre if (operation == VD_OP_BREAD) 5553782Sachartre bcopy(maddr + moffset, data, mlen); 5563782Sachartre else 5573782Sachartre bcopy(data, maddr + moffset, mlen); 5583782Sachartre 5593782Sachartre if (segmap_fault(kas.a_hat, segkmap, maddr, mlen, 5603782Sachartre F_SOFTUNLOCK, srw) != 0) { 5613782Sachartre (void) segmap_release(segkmap, maddr, 0); 5623782Sachartre return (-1); 5633782Sachartre } 5643782Sachartre if (segmap_release(segkmap, maddr, smflags) != 0) 5653782Sachartre return (-1); 5663782Sachartre n -= mlen; 5673782Sachartre offset += mlen; 5683782Sachartre data += mlen; 5693782Sachartre 5703782Sachartre } while (n > 0); 5713782Sachartre 5723782Sachartre return (len); 5733782Sachartre } 5743782Sachartre 5754696Sachartre /* 5764696Sachartre * Function: 5774963Sachartre * vd_file_build_default_label 5784963Sachartre * 5794963Sachartre * Description: 5804963Sachartre * Return a default label for the given disk. This is used when the disk 5814963Sachartre * does not have a valid VTOC so that the user can get a valid default 582*5365Slm66018 * configuration. The default label has all slice sizes set to 0 (except 5834963Sachartre * slice 2 which is the entire disk) to force the user to write a valid 5844963Sachartre * label onto the disk image. 5854963Sachartre * 5864963Sachartre * Parameters: 5874963Sachartre * vd - disk on which the operation is performed. 5884963Sachartre * label - the returned default label. 5894963Sachartre * 5904963Sachartre * Return Code: 5914963Sachartre * none. 5924963Sachartre */ 5934963Sachartre static void 5944963Sachartre vd_file_build_default_label(vd_t *vd, struct dk_label *label) 5954963Sachartre { 5964963Sachartre size_t size; 5974963Sachartre char prefix; 5985081Sachartre int slice, nparts; 5995081Sachartre uint16_t tag; 6004963Sachartre 6014963Sachartre ASSERT(vd->file); 6024963Sachartre 6034963Sachartre /* 6044963Sachartre * We must have a resonable number of cylinders and sectors so 6054963Sachartre * that newfs can run using default values. 6064963Sachartre * 6074963Sachartre * if (disk_size < 2MB) 6084963Sachartre * phys_cylinders = disk_size / 100K 6094963Sachartre * else 6104963Sachartre * phys_cylinders = disk_size / 300K 6114963Sachartre * 6124963Sachartre * phys_cylinders = (phys_cylinders == 0) ? 1 : phys_cylinders 6134963Sachartre * alt_cylinders = (phys_cylinders > 2) ? 2 : 0; 6144963Sachartre * data_cylinders = phys_cylinders - alt_cylinders 6154963Sachartre * 6164963Sachartre * sectors = disk_size / (phys_cylinders * blk_size) 6174963Sachartre * 6184963Sachartre * The file size test is an attempt to not have too few cylinders 6194963Sachartre * for a small file, or so many on a big file that you waste space 6204963Sachartre * for backup superblocks or cylinder group structures. 6214963Sachartre */ 6224963Sachartre if (vd->file_size < (2 * 1024 * 1024)) 6234963Sachartre label->dkl_pcyl = vd->file_size / (100 * 1024); 6244963Sachartre else 6254963Sachartre label->dkl_pcyl = vd->file_size / (300 * 1024); 6264963Sachartre 6274963Sachartre if (label->dkl_pcyl == 0) 6284963Sachartre label->dkl_pcyl = 1; 6294963Sachartre 6305081Sachartre label->dkl_acyl = 0; 6315081Sachartre 6325081Sachartre if (vd->vdisk_type == VD_DISK_TYPE_SLICE) { 6335081Sachartre nparts = 1; 6345081Sachartre slice = 0; 6355081Sachartre tag = V_UNASSIGNED; 6365081Sachartre } else { 6375081Sachartre if (label->dkl_pcyl > 2) 6385081Sachartre label->dkl_acyl = 2; 6395081Sachartre nparts = V_NUMPAR; 6405081Sachartre slice = VD_ENTIRE_DISK_SLICE; 6415081Sachartre tag = V_BACKUP; 6425081Sachartre } 6434963Sachartre 6444963Sachartre label->dkl_nsect = vd->file_size / 6454963Sachartre (DEV_BSIZE * label->dkl_pcyl); 6464963Sachartre label->dkl_ncyl = label->dkl_pcyl - label->dkl_acyl; 6474963Sachartre label->dkl_nhead = 1; 6484963Sachartre label->dkl_write_reinstruct = 0; 6494963Sachartre label->dkl_read_reinstruct = 0; 6504963Sachartre label->dkl_rpm = 7200; 6514963Sachartre label->dkl_apc = 0; 6524963Sachartre label->dkl_intrlv = 0; 6534963Sachartre 6544963Sachartre PR0("requested disk size: %ld bytes\n", vd->file_size); 6554963Sachartre PR0("setup: ncyl=%d nhead=%d nsec=%d\n", label->dkl_pcyl, 6564963Sachartre label->dkl_nhead, label->dkl_nsect); 6574963Sachartre PR0("provided disk size: %ld bytes\n", (uint64_t) 6584963Sachartre (label->dkl_pcyl * label->dkl_nhead * 6594963Sachartre label->dkl_nsect * DEV_BSIZE)); 6604963Sachartre 6614963Sachartre if (vd->file_size < (1ULL << 20)) { 6624963Sachartre size = vd->file_size >> 10; 6634963Sachartre prefix = 'K'; /* Kilobyte */ 6644963Sachartre } else if (vd->file_size < (1ULL << 30)) { 6654963Sachartre size = vd->file_size >> 20; 6664963Sachartre prefix = 'M'; /* Megabyte */ 6674963Sachartre } else if (vd->file_size < (1ULL << 40)) { 6684963Sachartre size = vd->file_size >> 30; 6694963Sachartre prefix = 'G'; /* Gigabyte */ 6704963Sachartre } else { 6714963Sachartre size = vd->file_size >> 40; 6724963Sachartre prefix = 'T'; /* Terabyte */ 6734963Sachartre } 6744963Sachartre 6754963Sachartre /* 6764963Sachartre * We must have a correct label name otherwise format(1m) will 6774963Sachartre * not recognized the disk as labeled. 6784963Sachartre */ 6794963Sachartre (void) snprintf(label->dkl_asciilabel, LEN_DKL_ASCII, 6804963Sachartre "SUN-DiskImage-%ld%cB cyl %d alt %d hd %d sec %d", 6814963Sachartre size, prefix, 6824963Sachartre label->dkl_ncyl, label->dkl_acyl, label->dkl_nhead, 6834963Sachartre label->dkl_nsect); 6844963Sachartre 6854963Sachartre /* default VTOC */ 6864963Sachartre label->dkl_vtoc.v_version = V_VERSION; 6875081Sachartre label->dkl_vtoc.v_nparts = nparts; 6884963Sachartre label->dkl_vtoc.v_sanity = VTOC_SANE; 6895081Sachartre label->dkl_vtoc.v_part[slice].p_tag = tag; 6905081Sachartre label->dkl_map[slice].dkl_cylno = 0; 6915081Sachartre label->dkl_map[slice].dkl_nblk = label->dkl_ncyl * 6924963Sachartre label->dkl_nhead * label->dkl_nsect; 6934963Sachartre label->dkl_cksum = vd_lbl2cksum(label); 6944963Sachartre } 6954963Sachartre 6964963Sachartre /* 6974963Sachartre * Function: 6984696Sachartre * vd_file_set_vtoc 6994696Sachartre * 7004696Sachartre * Description: 7014696Sachartre * Set the vtoc of a disk image by writing the label and backup 7024696Sachartre * labels into the disk image backend. 7034696Sachartre * 7044696Sachartre * Parameters: 7054696Sachartre * vd - disk on which the operation is performed. 7064696Sachartre * label - the data to be written. 7074696Sachartre * 7084696Sachartre * Return Code: 7094696Sachartre * 0 - success. 7104696Sachartre * n > 0 - error, n indicates the errno code. 7114696Sachartre */ 7124696Sachartre static int 7134696Sachartre vd_file_set_vtoc(vd_t *vd, struct dk_label *label) 7144696Sachartre { 7154696Sachartre int blk, sec, cyl, head, cnt; 7164696Sachartre 7174696Sachartre ASSERT(vd->file); 7184696Sachartre 7194696Sachartre if (VD_FILE_LABEL_WRITE(vd, label) < 0) { 7204696Sachartre PR0("fail to write disk label"); 7214696Sachartre return (EIO); 7224696Sachartre } 7234696Sachartre 7244696Sachartre /* 7254696Sachartre * Backup labels are on the last alternate cylinder's 7264696Sachartre * first five odd sectors. 7274696Sachartre */ 7284696Sachartre if (label->dkl_acyl == 0) { 7294696Sachartre PR0("no alternate cylinder, can not store backup labels"); 7304696Sachartre return (0); 7314696Sachartre } 7324696Sachartre 7334696Sachartre cyl = label->dkl_ncyl + label->dkl_acyl - 1; 7344696Sachartre head = label->dkl_nhead - 1; 7354696Sachartre 7364696Sachartre blk = (cyl * ((label->dkl_nhead * label->dkl_nsect) - label->dkl_apc)) + 7374696Sachartre (head * label->dkl_nsect); 7384696Sachartre 7394696Sachartre /* 7404696Sachartre * Write the backup labels. Make sure we don't try to write past 7414696Sachartre * the last cylinder. 7424696Sachartre */ 7434696Sachartre sec = 1; 7444696Sachartre 7454696Sachartre for (cnt = 0; cnt < VD_FILE_NUM_BACKUP; cnt++) { 7464696Sachartre 7474696Sachartre if (sec >= label->dkl_nsect) { 7484696Sachartre PR0("not enough sector to store all backup labels"); 7494696Sachartre return (0); 7504696Sachartre } 7514696Sachartre 7524696Sachartre if (vd_file_rw(vd, VD_SLICE_NONE, VD_OP_BWRITE, (caddr_t)label, 7534696Sachartre blk + sec, sizeof (struct dk_label)) < 0) { 7544696Sachartre PR0("error writing backup label at block %d\n", 7554696Sachartre blk + sec); 7564696Sachartre return (EIO); 7574696Sachartre } 7584696Sachartre 7594696Sachartre PR1("wrote backup label at block %d\n", blk + sec); 7604696Sachartre 7614696Sachartre sec += 2; 7624696Sachartre } 7634696Sachartre 7644696Sachartre return (0); 7654696Sachartre } 7664696Sachartre 7674696Sachartre /* 7684696Sachartre * Function: 7694696Sachartre * vd_file_get_devid_block 7704696Sachartre * 7714696Sachartre * Description: 7724696Sachartre * Return the block number where the device id is stored. 7734696Sachartre * 7744696Sachartre * Parameters: 7754696Sachartre * vd - disk on which the operation is performed. 7764696Sachartre * blkp - pointer to the block number 7774696Sachartre * 7784696Sachartre * Return Code: 7794696Sachartre * 0 - success 7804696Sachartre * ENOSPC - disk has no space to store a device id 7814696Sachartre */ 7824696Sachartre static int 7834696Sachartre vd_file_get_devid_block(vd_t *vd, size_t *blkp) 7844696Sachartre { 7854696Sachartre diskaddr_t spc, head, cyl; 7864696Sachartre 7874696Sachartre ASSERT(vd->file); 7884696Sachartre ASSERT(vd->vdisk_label == VD_DISK_LABEL_VTOC); 7894696Sachartre 7904696Sachartre /* this geometry doesn't allow us to have a devid */ 7914696Sachartre if (vd->dk_geom.dkg_acyl < 2) { 7924696Sachartre PR0("not enough alternate cylinder available for devid " 7934696Sachartre "(acyl=%u)", vd->dk_geom.dkg_acyl); 7944696Sachartre return (ENOSPC); 7954696Sachartre } 7964696Sachartre 7974696Sachartre /* the devid is in on the track next to the last cylinder */ 7984696Sachartre cyl = vd->dk_geom.dkg_ncyl + vd->dk_geom.dkg_acyl - 2; 7994696Sachartre spc = vd->dk_geom.dkg_nhead * vd->dk_geom.dkg_nsect; 8004696Sachartre head = vd->dk_geom.dkg_nhead - 1; 8014696Sachartre 8024696Sachartre *blkp = (cyl * (spc - vd->dk_geom.dkg_apc)) + 8034696Sachartre (head * vd->dk_geom.dkg_nsect) + 1; 8044696Sachartre 8054696Sachartre return (0); 8064696Sachartre } 8074696Sachartre 8084696Sachartre /* 8094696Sachartre * Return the checksum of a disk block containing an on-disk devid. 8104696Sachartre */ 8114696Sachartre static uint_t 8124696Sachartre vd_dkdevid2cksum(struct dk_devid *dkdevid) 8134696Sachartre { 8144696Sachartre uint_t chksum, *ip; 8154696Sachartre int i; 8164696Sachartre 8174696Sachartre chksum = 0; 8184696Sachartre ip = (uint_t *)dkdevid; 8194696Sachartre for (i = 0; i < ((DEV_BSIZE - sizeof (int)) / sizeof (int)); i++) 8204696Sachartre chksum ^= ip[i]; 8214696Sachartre 8224696Sachartre return (chksum); 8234696Sachartre } 8244696Sachartre 8254696Sachartre /* 8264696Sachartre * Function: 8274696Sachartre * vd_file_read_devid 8284696Sachartre * 8294696Sachartre * Description: 8304696Sachartre * Read the device id stored on a disk image. 8314696Sachartre * 8324696Sachartre * Parameters: 8334696Sachartre * vd - disk on which the operation is performed. 8344696Sachartre * devid - the return address of the device ID. 8354696Sachartre * 8364696Sachartre * Return Code: 8374696Sachartre * 0 - success 8384696Sachartre * EIO - I/O error while trying to access the disk image 8394696Sachartre * EINVAL - no valid device id was found 8404696Sachartre * ENOSPC - disk has no space to store a device id 8414696Sachartre */ 8424696Sachartre static int 8434696Sachartre vd_file_read_devid(vd_t *vd, ddi_devid_t *devid) 8444696Sachartre { 8454696Sachartre struct dk_devid *dkdevid; 8464696Sachartre size_t blk; 8474696Sachartre uint_t chksum; 8484696Sachartre int status, sz; 8494696Sachartre 8504696Sachartre if ((status = vd_file_get_devid_block(vd, &blk)) != 0) 8514696Sachartre return (status); 8524696Sachartre 8534696Sachartre dkdevid = kmem_zalloc(DEV_BSIZE, KM_SLEEP); 8544696Sachartre 8554696Sachartre /* get the devid */ 8564696Sachartre if ((vd_file_rw(vd, VD_SLICE_NONE, VD_OP_BREAD, (caddr_t)dkdevid, blk, 8574696Sachartre DEV_BSIZE)) < 0) { 8584696Sachartre PR0("error reading devid block at %lu", blk); 8594696Sachartre status = EIO; 8604696Sachartre goto done; 8614696Sachartre } 8624696Sachartre 8634696Sachartre /* validate the revision */ 8644696Sachartre if ((dkdevid->dkd_rev_hi != DK_DEVID_REV_MSB) || 8654696Sachartre (dkdevid->dkd_rev_lo != DK_DEVID_REV_LSB)) { 8664696Sachartre PR0("invalid devid found at block %lu (bad revision)", blk); 8674696Sachartre status = EINVAL; 8684696Sachartre goto done; 8694696Sachartre } 8704696Sachartre 8714696Sachartre /* compute checksum */ 8724696Sachartre chksum = vd_dkdevid2cksum(dkdevid); 8734696Sachartre 8744696Sachartre /* compare the checksums */ 8754696Sachartre if (DKD_GETCHKSUM(dkdevid) != chksum) { 8764696Sachartre PR0("invalid devid found at block %lu (bad checksum)", blk); 8774696Sachartre status = EINVAL; 8784696Sachartre goto done; 8794696Sachartre } 8804696Sachartre 8814696Sachartre /* validate the device id */ 8824696Sachartre if (ddi_devid_valid((ddi_devid_t)&dkdevid->dkd_devid) != DDI_SUCCESS) { 8834696Sachartre PR0("invalid devid found at block %lu", blk); 8844696Sachartre status = EINVAL; 8854696Sachartre goto done; 8864696Sachartre } 8874696Sachartre 8884696Sachartre PR1("devid read at block %lu", blk); 8894696Sachartre 8904696Sachartre sz = ddi_devid_sizeof((ddi_devid_t)&dkdevid->dkd_devid); 8914696Sachartre *devid = kmem_alloc(sz, KM_SLEEP); 8924696Sachartre bcopy(&dkdevid->dkd_devid, *devid, sz); 8934696Sachartre 8944696Sachartre done: 8954696Sachartre kmem_free(dkdevid, DEV_BSIZE); 8964696Sachartre return (status); 8974696Sachartre 8984696Sachartre } 8994696Sachartre 9004696Sachartre /* 9014696Sachartre * Function: 9024696Sachartre * vd_file_write_devid 9034696Sachartre * 9044696Sachartre * Description: 9054696Sachartre * Write a device id into disk image. 9064696Sachartre * 9074696Sachartre * Parameters: 9084696Sachartre * vd - disk on which the operation is performed. 9094696Sachartre * devid - the device ID to store. 9104696Sachartre * 9114696Sachartre * Return Code: 9124696Sachartre * 0 - success 9134696Sachartre * EIO - I/O error while trying to access the disk image 9144696Sachartre * ENOSPC - disk has no space to store a device id 9154696Sachartre */ 9164696Sachartre static int 9174696Sachartre vd_file_write_devid(vd_t *vd, ddi_devid_t devid) 9184696Sachartre { 9194696Sachartre struct dk_devid *dkdevid; 9204696Sachartre uint_t chksum; 9214696Sachartre size_t blk; 9224696Sachartre int status; 9234696Sachartre 9244696Sachartre if ((status = vd_file_get_devid_block(vd, &blk)) != 0) 9254696Sachartre return (status); 9264696Sachartre 9274696Sachartre dkdevid = kmem_zalloc(DEV_BSIZE, KM_SLEEP); 9284696Sachartre 9294696Sachartre /* set revision */ 9304696Sachartre dkdevid->dkd_rev_hi = DK_DEVID_REV_MSB; 9314696Sachartre dkdevid->dkd_rev_lo = DK_DEVID_REV_LSB; 9324696Sachartre 9334696Sachartre /* copy devid */ 9344696Sachartre bcopy(devid, &dkdevid->dkd_devid, ddi_devid_sizeof(devid)); 9354696Sachartre 9364696Sachartre /* compute checksum */ 9374696Sachartre chksum = vd_dkdevid2cksum(dkdevid); 9384696Sachartre 9394696Sachartre /* set checksum */ 9404696Sachartre DKD_FORMCHKSUM(chksum, dkdevid); 9414696Sachartre 9424696Sachartre /* store the devid */ 9434696Sachartre if ((status = vd_file_rw(vd, VD_SLICE_NONE, VD_OP_BWRITE, 9444696Sachartre (caddr_t)dkdevid, blk, DEV_BSIZE)) < 0) { 9454696Sachartre PR0("Error writing devid block at %lu", blk); 9464696Sachartre status = EIO; 9474696Sachartre } else { 9484696Sachartre PR1("devid written at block %lu", blk); 9494696Sachartre status = 0; 9504696Sachartre } 9514696Sachartre 9524696Sachartre kmem_free(dkdevid, DEV_BSIZE); 9534696Sachartre return (status); 9544696Sachartre } 9554696Sachartre 9564696Sachartre /* 9574696Sachartre * Function: 958*5365Slm66018 * vd_do_scsi_rdwr 9594696Sachartre * 9604696Sachartre * Description: 9614696Sachartre * Read or write to a SCSI disk using an absolute disk offset. 9624696Sachartre * 9634696Sachartre * Parameters: 9644696Sachartre * vd - disk on which the operation is performed. 9654696Sachartre * operation - operation to execute: read (VD_OP_BREAD) or 9664696Sachartre * write (VD_OP_BWRITE). 9674696Sachartre * data - buffer where data are read to or written from. 9684696Sachartre * blk - starting block for the operation. 9694696Sachartre * len - number of bytes to read or write. 9704696Sachartre * 9714696Sachartre * Return Code: 9724696Sachartre * 0 - success 9734696Sachartre * n != 0 - error. 9744696Sachartre */ 9754696Sachartre static int 976*5365Slm66018 vd_do_scsi_rdwr(vd_t *vd, int operation, caddr_t data, size_t blk, size_t len) 9774696Sachartre { 9784696Sachartre struct uscsi_cmd ucmd; 9794696Sachartre union scsi_cdb cdb; 9804696Sachartre int nsectors, nblk; 9814696Sachartre int max_sectors; 9824696Sachartre int status, rval; 9834696Sachartre 9844696Sachartre ASSERT(!vd->file); 985*5365Slm66018 ASSERT(vd->vdisk_block_size > 0); 9864696Sachartre 9874696Sachartre max_sectors = vd->max_xfer_sz; 988*5365Slm66018 nblk = (len / vd->vdisk_block_size); 989*5365Slm66018 990*5365Slm66018 if (len % vd->vdisk_block_size != 0) 9914696Sachartre return (EINVAL); 9924696Sachartre 9934696Sachartre /* 9944696Sachartre * Build and execute the uscsi ioctl. We build a group0, group1 9954696Sachartre * or group4 command as necessary, since some targets 9964696Sachartre * do not support group1 commands. 9974696Sachartre */ 9984696Sachartre while (nblk) { 9994696Sachartre 10004696Sachartre bzero(&ucmd, sizeof (ucmd)); 10014696Sachartre bzero(&cdb, sizeof (cdb)); 10024696Sachartre 10034696Sachartre nsectors = (max_sectors < nblk) ? max_sectors : nblk; 10044696Sachartre 1005*5365Slm66018 /* 1006*5365Slm66018 * Some of the optical drives on sun4v machines are ATAPI 1007*5365Slm66018 * devices which use Group 1 Read/Write commands so we need 1008*5365Slm66018 * to explicitly check a flag which is set when a domain 1009*5365Slm66018 * is bound. 1010*5365Slm66018 */ 1011*5365Slm66018 if (blk < (2 << 20) && nsectors <= 0xff && !vd->is_atapi_dev) { 10124696Sachartre FORMG0ADDR(&cdb, blk); 10134696Sachartre FORMG0COUNT(&cdb, nsectors); 10144696Sachartre ucmd.uscsi_cdblen = CDB_GROUP0; 10154696Sachartre } else if (blk > 0xffffffff) { 10164696Sachartre FORMG4LONGADDR(&cdb, blk); 10174696Sachartre FORMG4COUNT(&cdb, nsectors); 10184696Sachartre ucmd.uscsi_cdblen = CDB_GROUP4; 10194696Sachartre cdb.scc_cmd |= SCMD_GROUP4; 10204696Sachartre } else { 10214696Sachartre FORMG1ADDR(&cdb, blk); 10224696Sachartre FORMG1COUNT(&cdb, nsectors); 10234696Sachartre ucmd.uscsi_cdblen = CDB_GROUP1; 10244696Sachartre cdb.scc_cmd |= SCMD_GROUP1; 10254696Sachartre } 10264696Sachartre ucmd.uscsi_cdb = (caddr_t)&cdb; 10274696Sachartre ucmd.uscsi_bufaddr = data; 1028*5365Slm66018 ucmd.uscsi_buflen = nsectors * vd->block_size; 10294696Sachartre ucmd.uscsi_timeout = vd_scsi_rdwr_timeout; 10304696Sachartre /* 10314696Sachartre * Set flags so that the command is isolated from normal 10324696Sachartre * commands and no error message is printed. 10334696Sachartre */ 10344696Sachartre ucmd.uscsi_flags = USCSI_ISOLATE | USCSI_SILENT; 10354696Sachartre 10364696Sachartre if (operation == VD_OP_BREAD) { 10374696Sachartre cdb.scc_cmd |= SCMD_READ; 10384696Sachartre ucmd.uscsi_flags |= USCSI_READ; 10394696Sachartre } else { 10404696Sachartre cdb.scc_cmd |= SCMD_WRITE; 10414696Sachartre } 10424696Sachartre 10434696Sachartre status = ldi_ioctl(vd->ldi_handle[VD_ENTIRE_DISK_SLICE], 10445081Sachartre USCSICMD, (intptr_t)&ucmd, (vd->open_flags | FKIOCTL), 10454696Sachartre kcred, &rval); 10464696Sachartre 10474696Sachartre if (status == 0) 10484696Sachartre status = ucmd.uscsi_status; 10494696Sachartre 10504696Sachartre if (status != 0) 10514696Sachartre break; 10524696Sachartre 10534696Sachartre /* 10544696Sachartre * Check if partial DMA breakup is required. If so, reduce 10554696Sachartre * the request size by half and retry the last request. 10564696Sachartre */ 10574696Sachartre if (ucmd.uscsi_resid == ucmd.uscsi_buflen) { 10584696Sachartre max_sectors >>= 1; 10594696Sachartre if (max_sectors <= 0) { 10604696Sachartre status = EIO; 10614696Sachartre break; 10624696Sachartre } 10634696Sachartre continue; 10644696Sachartre } 10654696Sachartre 10664696Sachartre if (ucmd.uscsi_resid != 0) { 10674696Sachartre status = EIO; 10684696Sachartre break; 10694696Sachartre } 10704696Sachartre 10714696Sachartre blk += nsectors; 10724696Sachartre nblk -= nsectors; 1073*5365Slm66018 data += nsectors * vd->vdisk_block_size; /* SECSIZE */ 10744696Sachartre } 10754696Sachartre 10764696Sachartre return (status); 10774696Sachartre } 10784696Sachartre 10794838Slm66018 /* 1080*5365Slm66018 * Function: 1081*5365Slm66018 * vd_scsi_rdwr 1082*5365Slm66018 * 1083*5365Slm66018 * Description: 1084*5365Slm66018 * Wrapper function to read or write to a SCSI disk using an absolute 1085*5365Slm66018 * disk offset. It checks the blocksize of the underlying device and, 1086*5365Slm66018 * if necessary, adjusts the buffers accordingly before calling 1087*5365Slm66018 * vd_do_scsi_rdwr() to do the actual read or write. 1088*5365Slm66018 * 1089*5365Slm66018 * Parameters: 1090*5365Slm66018 * vd - disk on which the operation is performed. 1091*5365Slm66018 * operation - operation to execute: read (VD_OP_BREAD) or 1092*5365Slm66018 * write (VD_OP_BWRITE). 1093*5365Slm66018 * data - buffer where data are read to or written from. 1094*5365Slm66018 * blk - starting block for the operation. 1095*5365Slm66018 * len - number of bytes to read or write. 1096*5365Slm66018 * 1097*5365Slm66018 * Return Code: 1098*5365Slm66018 * 0 - success 1099*5365Slm66018 * n != 0 - error. 1100*5365Slm66018 */ 1101*5365Slm66018 static int 1102*5365Slm66018 vd_scsi_rdwr(vd_t *vd, int operation, caddr_t data, size_t vblk, size_t vlen) 1103*5365Slm66018 { 1104*5365Slm66018 int rv; 1105*5365Slm66018 1106*5365Slm66018 size_t pblk; /* physical device block number of data on device */ 1107*5365Slm66018 size_t delta; /* relative offset between pblk and vblk */ 1108*5365Slm66018 size_t pnblk; /* number of physical blocks to be read from device */ 1109*5365Slm66018 size_t plen; /* length of data to be read from physical device */ 1110*5365Slm66018 char *buf; /* buffer area to fit physical device's block size */ 1111*5365Slm66018 1112*5365Slm66018 /* 1113*5365Slm66018 * If the vdisk block size and the block size of the underlying device 1114*5365Slm66018 * match we can skip straight to vd_do_scsi_rdwr(), otherwise we need 1115*5365Slm66018 * to create a buffer large enough to handle the device's block size 1116*5365Slm66018 * and adjust the block to be read from and the amount of data to 1117*5365Slm66018 * read to correspond with the device's block size. 1118*5365Slm66018 */ 1119*5365Slm66018 if (vd->vdisk_block_size == vd->block_size) 1120*5365Slm66018 return (vd_do_scsi_rdwr(vd, operation, data, vblk, vlen)); 1121*5365Slm66018 1122*5365Slm66018 if (vd->vdisk_block_size > vd->block_size) 1123*5365Slm66018 return (EINVAL); 1124*5365Slm66018 1125*5365Slm66018 /* 1126*5365Slm66018 * Writing of physical block sizes larger than the virtual block size 1127*5365Slm66018 * is not supported. This would be added if/when support for guests 1128*5365Slm66018 * writing to DVDs is implemented. 1129*5365Slm66018 */ 1130*5365Slm66018 if (operation == VD_OP_BWRITE) 1131*5365Slm66018 return (ENOTSUP); 1132*5365Slm66018 1133*5365Slm66018 /* BEGIN CSTYLED */ 1134*5365Slm66018 /* 1135*5365Slm66018 * Below is a diagram showing the relationship between the physical 1136*5365Slm66018 * and virtual blocks. If the virtual blocks marked by 'X' below are 1137*5365Slm66018 * requested, then the physical blocks denoted by 'Y' are read. 1138*5365Slm66018 * 1139*5365Slm66018 * vblk 1140*5365Slm66018 * | vlen 1141*5365Slm66018 * |<--------------->| 1142*5365Slm66018 * v v 1143*5365Slm66018 * --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+- virtual disk: 1144*5365Slm66018 * | | | |XX|XX|XX|XX|XX|XX| | | | | | } block size is 1145*5365Slm66018 * --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+- vd->vdisk_block_size 1146*5365Slm66018 * : : : : 1147*5365Slm66018 * >:==:< delta : : 1148*5365Slm66018 * : : : : 1149*5365Slm66018 * --+-----+-----+-----+-----+-----+-----+-----+-- physical disk: 1150*5365Slm66018 * | |YY:YY|YYYYY|YYYYY|YY:YY| | | } block size is 1151*5365Slm66018 * --+-----+-----+-----+-----+-----+-----+-----+-- vd->block_size 1152*5365Slm66018 * ^ ^ 1153*5365Slm66018 * |<--------------------->| 1154*5365Slm66018 * | plen 1155*5365Slm66018 * pblk 1156*5365Slm66018 */ 1157*5365Slm66018 /* END CSTYLED */ 1158*5365Slm66018 pblk = (vblk * vd->vdisk_block_size) / vd->block_size; 1159*5365Slm66018 delta = (vblk * vd->vdisk_block_size) - (pblk * vd->block_size); 1160*5365Slm66018 pnblk = ((delta + vlen - 1) / vd->block_size) + 1; 1161*5365Slm66018 plen = pnblk * vd->block_size; 1162*5365Slm66018 1163*5365Slm66018 PR2("vblk %lx:pblk %lx: vlen %ld:plen %ld", vblk, pblk, vlen, plen); 1164*5365Slm66018 1165*5365Slm66018 buf = kmem_zalloc(sizeof (caddr_t) * plen, KM_SLEEP); 1166*5365Slm66018 rv = vd_do_scsi_rdwr(vd, operation, (caddr_t)buf, pblk, plen); 1167*5365Slm66018 bcopy(buf + delta, data, vlen); 1168*5365Slm66018 1169*5365Slm66018 kmem_free(buf, sizeof (caddr_t) * plen); 1170*5365Slm66018 1171*5365Slm66018 return (rv); 1172*5365Slm66018 } 1173*5365Slm66018 1174*5365Slm66018 /* 11754838Slm66018 * Return Values 11764838Slm66018 * EINPROGRESS - operation was successfully started 11774838Slm66018 * EIO - encountered LDC (aka. task error) 11784838Slm66018 * 0 - operation completed successfully 11794838Slm66018 * 11804838Slm66018 * Side Effect 11814838Slm66018 * sets request->status = <disk operation status> 11824838Slm66018 */ 11831991Sheppo static int 11842336Snarayan vd_start_bio(vd_task_t *task) 11851991Sheppo { 11862531Snarayan int rv, status = 0; 11872336Snarayan vd_t *vd = task->vd; 11882336Snarayan vd_dring_payload_t *request = task->request; 11892336Snarayan struct buf *buf = &task->buf; 11902531Snarayan uint8_t mtype; 11913401Snarayan int slice; 11925081Sachartre char *bufaddr = 0; 11935081Sachartre size_t buflen; 11942336Snarayan 11952336Snarayan ASSERT(vd != NULL); 11962336Snarayan ASSERT(request != NULL); 11973401Snarayan 11983401Snarayan slice = request->slice; 11993401Snarayan 12004696Sachartre ASSERT(slice == VD_SLICE_NONE || slice < vd->nslices); 12012336Snarayan ASSERT((request->operation == VD_OP_BREAD) || 12022336Snarayan (request->operation == VD_OP_BWRITE)); 12032336Snarayan 12044838Slm66018 if (request->nbytes == 0) { 12054838Slm66018 /* no service for trivial requests */ 12064838Slm66018 request->status = EINVAL; 12074838Slm66018 return (0); 12084838Slm66018 } 12092336Snarayan 12102336Snarayan PR1("%s %lu bytes at block %lu", 12112336Snarayan (request->operation == VD_OP_BREAD) ? "Read" : "Write", 12122336Snarayan request->nbytes, request->addr); 12132336Snarayan 12145081Sachartre /* 12155081Sachartre * We have to check the open flags because the functions processing 12165081Sachartre * the read/write request will not do it. 12175081Sachartre */ 12185081Sachartre if (request->operation == VD_OP_BWRITE && !(vd->open_flags & FWRITE)) { 12195081Sachartre PR0("write fails because backend is opened read-only"); 12205081Sachartre request->nbytes = 0; 12215081Sachartre request->status = EROFS; 12225081Sachartre return (0); 12235081Sachartre } 12242336Snarayan 12252531Snarayan mtype = (&vd->inband_task == task) ? LDC_SHADOW_MAP : LDC_DIRECT_MAP; 12262531Snarayan 12272531Snarayan /* Map memory exported by client */ 12282531Snarayan status = ldc_mem_map(task->mhdl, request->cookie, request->ncookies, 12292531Snarayan mtype, (request->operation == VD_OP_BREAD) ? LDC_MEM_W : LDC_MEM_R, 12305081Sachartre &bufaddr, NULL); 12312531Snarayan if (status != 0) { 12322793Slm66018 PR0("ldc_mem_map() returned err %d ", status); 12334838Slm66018 return (EIO); 12342336Snarayan } 12352336Snarayan 12365081Sachartre buflen = request->nbytes; 12375081Sachartre 12385081Sachartre status = ldc_mem_acquire(task->mhdl, 0, buflen); 12392531Snarayan if (status != 0) { 12402531Snarayan (void) ldc_mem_unmap(task->mhdl); 12412793Slm66018 PR0("ldc_mem_acquire() returned err %d ", status); 12424838Slm66018 return (EIO); 12432531Snarayan } 12442531Snarayan 12452336Snarayan /* Start the block I/O */ 12463401Snarayan if (vd->file) { 12475081Sachartre rv = vd_file_rw(vd, slice, request->operation, bufaddr, 12483782Sachartre request->addr, request->nbytes); 12493782Sachartre if (rv < 0) { 12503401Snarayan request->nbytes = 0; 12514838Slm66018 request->status = EIO; 12523782Sachartre } else { 12533782Sachartre request->nbytes = rv; 12544838Slm66018 request->status = 0; 12553401Snarayan } 12563401Snarayan } else { 12574696Sachartre if (slice == VD_SLICE_NONE) { 12584696Sachartre /* 12594696Sachartre * This is not a disk image so it is a real disk. We 12604696Sachartre * assume that the underlying device driver supports 12614696Sachartre * USCSICMD ioctls. This is the case of all SCSI devices 12624696Sachartre * (sd, ssd...). 12634696Sachartre * 12644696Sachartre * In the future if we have non-SCSI disks we would need 12654696Sachartre * to invoke the appropriate function to do I/O using an 1266*5365Slm66018 * absolute disk offset (for example using DIOCTL_RWCMD 12674696Sachartre * for IDE disks). 12684696Sachartre */ 12695081Sachartre rv = vd_scsi_rdwr(vd, request->operation, bufaddr, 12705081Sachartre request->addr, request->nbytes); 12714696Sachartre if (rv != 0) { 12724696Sachartre request->nbytes = 0; 12734838Slm66018 request->status = EIO; 12744696Sachartre } else { 12754838Slm66018 request->status = 0; 12764696Sachartre } 12774696Sachartre } else { 12785081Sachartre bioinit(buf); 12795081Sachartre buf->b_flags = B_BUSY; 12805081Sachartre buf->b_bcount = request->nbytes; 12815081Sachartre buf->b_lblkno = request->addr; 12825081Sachartre buf->b_edev = vd->dev[slice]; 12835081Sachartre buf->b_un.b_addr = bufaddr; 12845081Sachartre buf->b_flags |= (request->operation == VD_OP_BREAD)? 12855081Sachartre B_READ : B_WRITE; 12865081Sachartre 12874838Slm66018 request->status = 12884838Slm66018 ldi_strategy(vd->ldi_handle[slice], buf); 12894838Slm66018 12904838Slm66018 /* 12914838Slm66018 * This is to indicate to the caller that the request 12924838Slm66018 * needs to be finished by vd_complete_bio() by calling 12934838Slm66018 * biowait() there and waiting for that to return before 12944838Slm66018 * triggering the notification of the vDisk client. 12954838Slm66018 * 12964838Slm66018 * This is necessary when writing to real disks as 12974838Slm66018 * otherwise calls to ldi_strategy() would be serialized 12984838Slm66018 * behind the calls to biowait() and performance would 12994838Slm66018 * suffer. 13004838Slm66018 */ 13014838Slm66018 if (request->status == 0) 13024696Sachartre return (EINPROGRESS); 13035081Sachartre 13045081Sachartre biofini(buf); 13054696Sachartre } 13063401Snarayan } 13073401Snarayan 13082336Snarayan /* Clean up after error */ 13095081Sachartre rv = ldc_mem_release(task->mhdl, 0, buflen); 13102531Snarayan if (rv) { 13112793Slm66018 PR0("ldc_mem_release() returned err %d ", rv); 13124838Slm66018 status = EIO; 13132531Snarayan } 13142531Snarayan rv = ldc_mem_unmap(task->mhdl); 13152531Snarayan if (rv) { 13164838Slm66018 PR0("ldc_mem_unmap() returned err %d ", rv); 13174838Slm66018 status = EIO; 13182531Snarayan } 13192531Snarayan 13202336Snarayan return (status); 13212336Snarayan } 13222336Snarayan 13234838Slm66018 /* 13244838Slm66018 * This function should only be called from vd_notify to ensure that requests 13254838Slm66018 * are responded to in the order that they are received. 13264838Slm66018 */ 13272336Snarayan static int 13282336Snarayan send_msg(ldc_handle_t ldc_handle, void *msg, size_t msglen) 13292336Snarayan { 13302793Slm66018 int status; 13312336Snarayan size_t nbytes; 13322336Snarayan 13332793Slm66018 do { 13342336Snarayan nbytes = msglen; 13352336Snarayan status = ldc_write(ldc_handle, msg, &nbytes); 13362793Slm66018 if (status != EWOULDBLOCK) 13372793Slm66018 break; 13382793Slm66018 drv_usecwait(vds_ldc_delay); 13392793Slm66018 } while (status == EWOULDBLOCK); 13402336Snarayan 13412336Snarayan if (status != 0) { 13422793Slm66018 if (status != ECONNRESET) 13432793Slm66018 PR0("ldc_write() returned errno %d", status); 13442336Snarayan return (status); 13452336Snarayan } else if (nbytes != msglen) { 13462793Slm66018 PR0("ldc_write() performed only partial write"); 13472336Snarayan return (EIO); 13482336Snarayan } 13492336Snarayan 13502336Snarayan PR1("SENT %lu bytes", msglen); 13512336Snarayan return (0); 13522336Snarayan } 13532336Snarayan 13542336Snarayan static void 13552336Snarayan vd_need_reset(vd_t *vd, boolean_t reset_ldc) 13562336Snarayan { 13572336Snarayan mutex_enter(&vd->lock); 13582336Snarayan vd->reset_state = B_TRUE; 13592336Snarayan vd->reset_ldc = reset_ldc; 13602336Snarayan mutex_exit(&vd->lock); 13612336Snarayan } 13622336Snarayan 13632336Snarayan /* 13642336Snarayan * Reset the state of the connection with a client, if needed; reset the LDC 13652336Snarayan * transport as well, if needed. This function should only be called from the 13662793Slm66018 * "vd_recv_msg", as it waits for tasks - otherwise a deadlock can occur. 13672336Snarayan */ 13682336Snarayan static void 13692336Snarayan vd_reset_if_needed(vd_t *vd) 13702336Snarayan { 13712793Slm66018 int status = 0; 13722336Snarayan 13732336Snarayan mutex_enter(&vd->lock); 13742336Snarayan if (!vd->reset_state) { 13752336Snarayan ASSERT(!vd->reset_ldc); 13762336Snarayan mutex_exit(&vd->lock); 13772336Snarayan return; 13782336Snarayan } 13792336Snarayan mutex_exit(&vd->lock); 13802336Snarayan 13812336Snarayan PR0("Resetting connection state with %s", VD_CLIENT(vd)); 13822336Snarayan 13832336Snarayan /* 13842336Snarayan * Let any asynchronous I/O complete before possibly pulling the rug 13852336Snarayan * out from under it; defer checking vd->reset_ldc, as one of the 13862336Snarayan * asynchronous tasks might set it 13872336Snarayan */ 13882336Snarayan ddi_taskq_wait(vd->completionq); 13892336Snarayan 13903401Snarayan if (vd->file) { 13915331Samw status = VOP_FSYNC(vd->file_vnode, FSYNC, kcred, NULL); 13923401Snarayan if (status) { 13933401Snarayan PR0("VOP_FSYNC returned errno %d", status); 13943401Snarayan } 13953401Snarayan } 13963401Snarayan 13972336Snarayan if ((vd->initialized & VD_DRING) && 13982336Snarayan ((status = ldc_mem_dring_unmap(vd->dring_handle)) != 0)) 13992793Slm66018 PR0("ldc_mem_dring_unmap() returned errno %d", status); 14002793Slm66018 14012793Slm66018 vd_free_dring_task(vd); 14022793Slm66018 14032793Slm66018 /* Free the staging buffer for msgs */ 14042793Slm66018 if (vd->vio_msgp != NULL) { 14052793Slm66018 kmem_free(vd->vio_msgp, vd->max_msglen); 14062793Slm66018 vd->vio_msgp = NULL; 14072336Snarayan } 14082336Snarayan 14092793Slm66018 /* Free the inband message buffer */ 14102793Slm66018 if (vd->inband_task.msg != NULL) { 14112793Slm66018 kmem_free(vd->inband_task.msg, vd->max_msglen); 14122793Slm66018 vd->inband_task.msg = NULL; 14132793Slm66018 } 14142336Snarayan 14152336Snarayan mutex_enter(&vd->lock); 14162793Slm66018 14172793Slm66018 if (vd->reset_ldc) 14182793Slm66018 PR0("taking down LDC channel"); 14192410Slm66018 if (vd->reset_ldc && ((status = ldc_down(vd->ldc_handle)) != 0)) 14202793Slm66018 PR0("ldc_down() returned errno %d", status); 14212336Snarayan 14222336Snarayan vd->initialized &= ~(VD_SID | VD_SEQ_NUM | VD_DRING); 14232336Snarayan vd->state = VD_STATE_INIT; 14242336Snarayan vd->max_msglen = sizeof (vio_msg_t); /* baseline vio message size */ 14252336Snarayan 14262793Slm66018 /* Allocate the staging buffer */ 14272793Slm66018 vd->vio_msgp = kmem_alloc(vd->max_msglen, KM_SLEEP); 14282793Slm66018 14293010Slm66018 PR0("calling ldc_up\n"); 14303010Slm66018 (void) ldc_up(vd->ldc_handle); 14312793Slm66018 14322336Snarayan vd->reset_state = B_FALSE; 14332336Snarayan vd->reset_ldc = B_FALSE; 14342793Slm66018 14352336Snarayan mutex_exit(&vd->lock); 14362336Snarayan } 14372336Snarayan 14382793Slm66018 static void vd_recv_msg(void *arg); 14392793Slm66018 14402793Slm66018 static void 14412793Slm66018 vd_mark_in_reset(vd_t *vd) 14422793Slm66018 { 14432793Slm66018 int status; 14442793Slm66018 14452793Slm66018 PR0("vd_mark_in_reset: marking vd in reset\n"); 14462793Slm66018 14472793Slm66018 vd_need_reset(vd, B_FALSE); 14482793Slm66018 status = ddi_taskq_dispatch(vd->startq, vd_recv_msg, vd, DDI_SLEEP); 14492793Slm66018 if (status == DDI_FAILURE) { 14502793Slm66018 PR0("cannot schedule task to recv msg\n"); 14512793Slm66018 vd_need_reset(vd, B_TRUE); 14522793Slm66018 return; 14532793Slm66018 } 14542793Slm66018 } 14552793Slm66018 14562336Snarayan static int 14573401Snarayan vd_mark_elem_done(vd_t *vd, int idx, int elem_status, int elem_nbytes) 14582336Snarayan { 14592336Snarayan boolean_t accepted; 14602336Snarayan int status; 14612336Snarayan vd_dring_entry_t *elem = VD_DRING_ELEM(idx); 14622336Snarayan 14632793Slm66018 if (vd->reset_state) 14642793Slm66018 return (0); 14652336Snarayan 14662336Snarayan /* Acquire the element */ 14672793Slm66018 if (!vd->reset_state && 14682793Slm66018 (status = ldc_mem_dring_acquire(vd->dring_handle, idx, idx)) != 0) { 14692793Slm66018 if (status == ECONNRESET) { 14702793Slm66018 vd_mark_in_reset(vd); 14712793Slm66018 return (0); 14722793Slm66018 } else { 14732793Slm66018 PR0("ldc_mem_dring_acquire() returned errno %d", 14742793Slm66018 status); 14752793Slm66018 return (status); 14762793Slm66018 } 14772336Snarayan } 14782336Snarayan 14792336Snarayan /* Set the element's status and mark it done */ 14802336Snarayan accepted = (elem->hdr.dstate == VIO_DESC_ACCEPTED); 14812336Snarayan if (accepted) { 14823401Snarayan elem->payload.nbytes = elem_nbytes; 14832336Snarayan elem->payload.status = elem_status; 14842336Snarayan elem->hdr.dstate = VIO_DESC_DONE; 14852336Snarayan } else { 14862336Snarayan /* Perhaps client timed out waiting for I/O... */ 14872793Slm66018 PR0("element %u no longer \"accepted\"", idx); 14882336Snarayan VD_DUMP_DRING_ELEM(elem); 14892336Snarayan } 14902336Snarayan /* Release the element */ 14912793Slm66018 if (!vd->reset_state && 14922793Slm66018 (status = ldc_mem_dring_release(vd->dring_handle, idx, idx)) != 0) { 14932793Slm66018 if (status == ECONNRESET) { 14942793Slm66018 vd_mark_in_reset(vd); 14952793Slm66018 return (0); 14962793Slm66018 } else { 14972793Slm66018 PR0("ldc_mem_dring_release() returned errno %d", 14982793Slm66018 status); 14992793Slm66018 return (status); 15002793Slm66018 } 15012336Snarayan } 15022336Snarayan 15032336Snarayan return (accepted ? 0 : EINVAL); 15042336Snarayan } 15052336Snarayan 15064838Slm66018 /* 15074838Slm66018 * Return Values 15084838Slm66018 * 0 - operation completed successfully 15094838Slm66018 * EIO - encountered LDC / task error 15104838Slm66018 * 15114838Slm66018 * Side Effect 15124838Slm66018 * sets request->status = <disk operation status> 15134838Slm66018 */ 15144838Slm66018 static int 15154838Slm66018 vd_complete_bio(vd_task_t *task) 15162336Snarayan { 15172336Snarayan int status = 0; 15184838Slm66018 int rv = 0; 15192336Snarayan vd_t *vd = task->vd; 15202336Snarayan vd_dring_payload_t *request = task->request; 15212336Snarayan struct buf *buf = &task->buf; 15222336Snarayan 15232336Snarayan 15242336Snarayan ASSERT(vd != NULL); 15252336Snarayan ASSERT(request != NULL); 15262336Snarayan ASSERT(task->msg != NULL); 15272336Snarayan ASSERT(task->msglen >= sizeof (*task->msg)); 15283401Snarayan ASSERT(!vd->file); 15294838Slm66018 ASSERT(request->slice != VD_SLICE_NONE); 15304838Slm66018 15314838Slm66018 /* Wait for the I/O to complete [ call to ldi_strategy(9f) ] */ 15322336Snarayan request->status = biowait(buf); 15332336Snarayan 15343401Snarayan /* return back the number of bytes read/written */ 15353401Snarayan request->nbytes = buf->b_bcount - buf->b_resid; 15363401Snarayan 15372531Snarayan /* Release the buffer */ 15382793Slm66018 if (!vd->reset_state) 15392793Slm66018 status = ldc_mem_release(task->mhdl, 0, buf->b_bcount); 15402531Snarayan if (status) { 15412793Slm66018 PR0("ldc_mem_release() returned errno %d copying to " 15422793Slm66018 "client", status); 15432793Slm66018 if (status == ECONNRESET) { 15442793Slm66018 vd_mark_in_reset(vd); 15452793Slm66018 } 15464838Slm66018 rv = EIO; 15471991Sheppo } 15482336Snarayan 15492793Slm66018 /* Unmap the memory, even if in reset */ 15502531Snarayan status = ldc_mem_unmap(task->mhdl); 15512531Snarayan if (status) { 15522793Slm66018 PR0("ldc_mem_unmap() returned errno %d copying to client", 15532531Snarayan status); 15542793Slm66018 if (status == ECONNRESET) { 15552793Slm66018 vd_mark_in_reset(vd); 15562793Slm66018 } 15574838Slm66018 rv = EIO; 15582531Snarayan } 15592531Snarayan 15602336Snarayan biofini(buf); 15612336Snarayan 15624838Slm66018 return (rv); 15634838Slm66018 } 15644838Slm66018 15654838Slm66018 /* 15664838Slm66018 * Description: 15674838Slm66018 * This function is called by the two functions called by a taskq 15684838Slm66018 * [ vd_complete_notify() and vd_serial_notify()) ] to send the 15694838Slm66018 * message to the client. 15704838Slm66018 * 15714838Slm66018 * Parameters: 15724838Slm66018 * arg - opaque pointer to structure containing task to be completed 15734838Slm66018 * 15744838Slm66018 * Return Values 15754838Slm66018 * None 15764838Slm66018 */ 15774838Slm66018 static void 15784838Slm66018 vd_notify(vd_task_t *task) 15794838Slm66018 { 15804838Slm66018 int status; 15814838Slm66018 15824838Slm66018 ASSERT(task != NULL); 15834838Slm66018 ASSERT(task->vd != NULL); 15844838Slm66018 15854838Slm66018 if (task->vd->reset_state) 15864838Slm66018 return; 15874838Slm66018 15884838Slm66018 /* 15894838Slm66018 * Send the "ack" or "nack" back to the client; if sending the message 15904838Slm66018 * via LDC fails, arrange to reset both the connection state and LDC 15914838Slm66018 * itself 15924838Slm66018 */ 15934838Slm66018 PR2("Sending %s", 15944838Slm66018 (task->msg->tag.vio_subtype == VIO_SUBTYPE_ACK) ? "ACK" : "NACK"); 15954838Slm66018 15964838Slm66018 status = send_msg(task->vd->ldc_handle, task->msg, task->msglen); 15974838Slm66018 switch (status) { 15984838Slm66018 case 0: 15994838Slm66018 break; 16004838Slm66018 case ECONNRESET: 16014838Slm66018 vd_mark_in_reset(task->vd); 16024838Slm66018 break; 16034838Slm66018 default: 16044838Slm66018 PR0("initiating full reset"); 16054838Slm66018 vd_need_reset(task->vd, B_TRUE); 16064838Slm66018 break; 16074838Slm66018 } 16084838Slm66018 16094838Slm66018 DTRACE_PROBE1(task__end, vd_task_t *, task); 16104838Slm66018 } 16114838Slm66018 16124838Slm66018 /* 16134838Slm66018 * Description: 16144838Slm66018 * Mark the Dring entry as Done and (if necessary) send an ACK/NACK to 16154838Slm66018 * the vDisk client 16164838Slm66018 * 16174838Slm66018 * Parameters: 16184838Slm66018 * task - structure containing the request sent from client 16194838Slm66018 * 16204838Slm66018 * Return Values 16214838Slm66018 * None 16224838Slm66018 */ 16234838Slm66018 static void 16244838Slm66018 vd_complete_notify(vd_task_t *task) 16254838Slm66018 { 16264838Slm66018 int status = 0; 16274838Slm66018 vd_t *vd = task->vd; 16284838Slm66018 vd_dring_payload_t *request = task->request; 16294838Slm66018 16302336Snarayan /* Update the dring element for a dring client */ 16314838Slm66018 if (!vd->reset_state && (vd->xfer_mode == VIO_DRING_MODE)) { 16323401Snarayan status = vd_mark_elem_done(vd, task->index, 16333401Snarayan request->status, request->nbytes); 16342793Slm66018 if (status == ECONNRESET) 16352793Slm66018 vd_mark_in_reset(vd); 16362793Slm66018 } 16372336Snarayan 16382336Snarayan /* 16394838Slm66018 * If a transport error occurred while marking the element done or 16404838Slm66018 * previously while executing the task, arrange to "nack" the message 16414838Slm66018 * when the final task in the descriptor element range completes 16422336Snarayan */ 16434838Slm66018 if ((status != 0) || (task->status != 0)) 16442336Snarayan task->msg->tag.vio_subtype = VIO_SUBTYPE_NACK; 16452336Snarayan 16462336Snarayan /* 16472336Snarayan * Only the final task for a range of elements will respond to and 16482336Snarayan * free the message 16492336Snarayan */ 16502793Slm66018 if (task->type == VD_NONFINAL_RANGE_TASK) { 16512336Snarayan return; 16522793Slm66018 } 16532336Snarayan 16544838Slm66018 vd_notify(task); 16554838Slm66018 } 16564838Slm66018 16574838Slm66018 /* 16584838Slm66018 * Description: 16594838Slm66018 * This is the basic completion function called to handle inband data 16604838Slm66018 * requests and handshake messages. All it needs to do is trigger a 16614838Slm66018 * message to the client that the request is completed. 16624838Slm66018 * 16634838Slm66018 * Parameters: 16644838Slm66018 * arg - opaque pointer to structure containing task to be completed 16654838Slm66018 * 16664838Slm66018 * Return Values 16674838Slm66018 * None 16684838Slm66018 */ 16694838Slm66018 static void 16704838Slm66018 vd_serial_notify(void *arg) 16714838Slm66018 { 16724838Slm66018 vd_task_t *task = (vd_task_t *)arg; 16734838Slm66018 16744838Slm66018 ASSERT(task != NULL); 16754838Slm66018 vd_notify(task); 16761991Sheppo } 16771991Sheppo 16782032Slm66018 static void 16792032Slm66018 vd_geom2dk_geom(void *vd_buf, void *ioctl_arg) 16802032Slm66018 { 16812032Slm66018 VD_GEOM2DK_GEOM((vd_geom_t *)vd_buf, (struct dk_geom *)ioctl_arg); 16822032Slm66018 } 16832032Slm66018 16842032Slm66018 static void 16852032Slm66018 vd_vtoc2vtoc(void *vd_buf, void *ioctl_arg) 16862032Slm66018 { 16872032Slm66018 VD_VTOC2VTOC((vd_vtoc_t *)vd_buf, (struct vtoc *)ioctl_arg); 16882032Slm66018 } 16892032Slm66018 16902032Slm66018 static void 16912032Slm66018 dk_geom2vd_geom(void *ioctl_arg, void *vd_buf) 16922032Slm66018 { 16932032Slm66018 DK_GEOM2VD_GEOM((struct dk_geom *)ioctl_arg, (vd_geom_t *)vd_buf); 16942032Slm66018 } 16952032Slm66018 16962032Slm66018 static void 16972032Slm66018 vtoc2vd_vtoc(void *ioctl_arg, void *vd_buf) 16982032Slm66018 { 16992032Slm66018 VTOC2VD_VTOC((struct vtoc *)ioctl_arg, (vd_vtoc_t *)vd_buf); 17002032Slm66018 } 17012032Slm66018 17022531Snarayan static void 17032531Snarayan vd_get_efi_in(void *vd_buf, void *ioctl_arg) 17042531Snarayan { 17052531Snarayan vd_efi_t *vd_efi = (vd_efi_t *)vd_buf; 17062531Snarayan dk_efi_t *dk_efi = (dk_efi_t *)ioctl_arg; 17072531Snarayan 17082531Snarayan dk_efi->dki_lba = vd_efi->lba; 17092531Snarayan dk_efi->dki_length = vd_efi->length; 17102531Snarayan dk_efi->dki_data = kmem_zalloc(vd_efi->length, KM_SLEEP); 17112531Snarayan } 17122531Snarayan 17132531Snarayan static void 17142531Snarayan vd_get_efi_out(void *ioctl_arg, void *vd_buf) 17152531Snarayan { 17162531Snarayan int len; 17172531Snarayan vd_efi_t *vd_efi = (vd_efi_t *)vd_buf; 17182531Snarayan dk_efi_t *dk_efi = (dk_efi_t *)ioctl_arg; 17192531Snarayan 17202531Snarayan len = vd_efi->length; 17212531Snarayan DK_EFI2VD_EFI(dk_efi, vd_efi); 17222531Snarayan kmem_free(dk_efi->dki_data, len); 17232531Snarayan } 17242531Snarayan 17252531Snarayan static void 17262531Snarayan vd_set_efi_in(void *vd_buf, void *ioctl_arg) 17272531Snarayan { 17282531Snarayan vd_efi_t *vd_efi = (vd_efi_t *)vd_buf; 17292531Snarayan dk_efi_t *dk_efi = (dk_efi_t *)ioctl_arg; 17302531Snarayan 17312531Snarayan dk_efi->dki_data = kmem_alloc(vd_efi->length, KM_SLEEP); 17322531Snarayan VD_EFI2DK_EFI(vd_efi, dk_efi); 17332531Snarayan } 17342531Snarayan 17352531Snarayan static void 17362531Snarayan vd_set_efi_out(void *ioctl_arg, void *vd_buf) 17372531Snarayan { 17382531Snarayan vd_efi_t *vd_efi = (vd_efi_t *)vd_buf; 17392531Snarayan dk_efi_t *dk_efi = (dk_efi_t *)ioctl_arg; 17402531Snarayan 17412531Snarayan kmem_free(dk_efi->dki_data, vd_efi->length); 17422531Snarayan } 17432531Snarayan 17444963Sachartre static vd_disk_label_t 17455081Sachartre vd_read_vtoc(vd_t *vd, struct vtoc *vtoc) 17462531Snarayan { 17472531Snarayan int status, rval; 17482531Snarayan struct dk_gpt *efi; 17492531Snarayan size_t efi_len; 17502531Snarayan 17515081Sachartre ASSERT(vd->ldi_handle[0] != NULL); 17525081Sachartre 17535081Sachartre status = ldi_ioctl(vd->ldi_handle[0], DKIOCGVTOC, (intptr_t)vtoc, 17545081Sachartre (vd->open_flags | FKIOCTL), kcred, &rval); 17552531Snarayan 17562531Snarayan if (status == 0) { 17574963Sachartre return (VD_DISK_LABEL_VTOC); 17582531Snarayan } else if (status != ENOTSUP) { 17592793Slm66018 PR0("ldi_ioctl(DKIOCGVTOC) returned error %d", status); 17604963Sachartre return (VD_DISK_LABEL_UNK); 17612531Snarayan } 17622531Snarayan 17635081Sachartre status = vds_efi_alloc_and_read(vd->ldi_handle[0], &efi, &efi_len); 17642531Snarayan 17652531Snarayan if (status) { 17662793Slm66018 PR0("vds_efi_alloc_and_read returned error %d", status); 17674963Sachartre return (VD_DISK_LABEL_UNK); 17682531Snarayan } 17692531Snarayan 17702531Snarayan vd_efi_to_vtoc(efi, vtoc); 17712531Snarayan vd_efi_free(efi, efi_len); 17722531Snarayan 17734963Sachartre return (VD_DISK_LABEL_EFI); 17742531Snarayan } 17752531Snarayan 17763782Sachartre static ushort_t 17773401Snarayan vd_lbl2cksum(struct dk_label *label) 17783401Snarayan { 17793401Snarayan int count; 17803782Sachartre ushort_t sum, *sp; 17813401Snarayan 17823401Snarayan count = (sizeof (struct dk_label)) / (sizeof (short)) - 1; 17833782Sachartre sp = (ushort_t *)label; 17843401Snarayan sum = 0; 17853401Snarayan while (count--) { 17863401Snarayan sum ^= *sp++; 17873401Snarayan } 17883401Snarayan 17893401Snarayan return (sum); 17903401Snarayan } 17913401Snarayan 17924696Sachartre /* 17934696Sachartre * Handle ioctls to a disk slice. 17944838Slm66018 * 17954838Slm66018 * Return Values 17964838Slm66018 * 0 - Indicates that there are no errors in disk operations 17974838Slm66018 * ENOTSUP - Unknown disk label type or unsupported DKIO ioctl 17984838Slm66018 * EINVAL - Not enough room to copy the EFI label 17994838Slm66018 * 18004696Sachartre */ 18011991Sheppo static int 18022032Slm66018 vd_do_slice_ioctl(vd_t *vd, int cmd, void *ioctl_arg) 18031991Sheppo { 18042531Snarayan dk_efi_t *dk_ioc; 18052531Snarayan 18062531Snarayan switch (vd->vdisk_label) { 18072531Snarayan 18084696Sachartre /* ioctls for a slice from a disk with a VTOC label */ 18092531Snarayan case VD_DISK_LABEL_VTOC: 18102531Snarayan 18112531Snarayan switch (cmd) { 18122531Snarayan case DKIOCGGEOM: 18132531Snarayan ASSERT(ioctl_arg != NULL); 18142531Snarayan bcopy(&vd->dk_geom, ioctl_arg, sizeof (vd->dk_geom)); 18152531Snarayan return (0); 18162531Snarayan case DKIOCGVTOC: 18172531Snarayan ASSERT(ioctl_arg != NULL); 18182531Snarayan bcopy(&vd->vtoc, ioctl_arg, sizeof (vd->vtoc)); 18192531Snarayan return (0); 18202531Snarayan default: 18212531Snarayan return (ENOTSUP); 18222531Snarayan } 18232531Snarayan 18244696Sachartre /* ioctls for a slice from a disk with an EFI label */ 18252531Snarayan case VD_DISK_LABEL_EFI: 18262531Snarayan 18272531Snarayan switch (cmd) { 18282531Snarayan case DKIOCGETEFI: 18292531Snarayan ASSERT(ioctl_arg != NULL); 18302531Snarayan dk_ioc = (dk_efi_t *)ioctl_arg; 18312531Snarayan if (dk_ioc->dki_length < vd->dk_efi.dki_length) 18322531Snarayan return (EINVAL); 18332531Snarayan bcopy(vd->dk_efi.dki_data, dk_ioc->dki_data, 18342531Snarayan vd->dk_efi.dki_length); 18352531Snarayan return (0); 18362531Snarayan default: 18372531Snarayan return (ENOTSUP); 18382531Snarayan } 18392531Snarayan 18401991Sheppo default: 18414838Slm66018 /* Unknown disk label type */ 18421991Sheppo return (ENOTSUP); 18431991Sheppo } 18441991Sheppo } 18451991Sheppo 18464696Sachartre /* 18474963Sachartre * Function: 18484963Sachartre * vd_file_validate_geometry 18494963Sachartre * 18504963Sachartre * Description: 18514963Sachartre * Read the label and validate the geometry of a disk image. The driver 18524963Sachartre * label, vtoc and geometry information are updated according to the 18534963Sachartre * label read from the disk image. 18544963Sachartre * 18554963Sachartre * If no valid label is found, the label is set to unknown and the 18564963Sachartre * function returns EINVAL, but a default vtoc and geometry are provided 18574963Sachartre * to the driver. 18584963Sachartre * 18594963Sachartre * Parameters: 18604963Sachartre * vd - disk on which the operation is performed. 18614963Sachartre * 18624963Sachartre * Return Code: 18634963Sachartre * 0 - success. 18644963Sachartre * EIO - error reading the label from the disk image. 18654963Sachartre * EINVAL - unknown disk label. 18664963Sachartre */ 18674963Sachartre static int 18684963Sachartre vd_file_validate_geometry(vd_t *vd) 18694963Sachartre { 18704963Sachartre struct dk_label label; 18714963Sachartre struct dk_geom *geom = &vd->dk_geom; 18724963Sachartre struct vtoc *vtoc = &vd->vtoc; 18734963Sachartre int i; 18744963Sachartre int status = 0; 18754963Sachartre 18764963Sachartre ASSERT(vd->file); 18774963Sachartre 18785081Sachartre if (vd->vdisk_type == VD_DISK_TYPE_SLICE) { 18795081Sachartre /* 18805081Sachartre * For single slice disk we always fake the geometry, and we 18815081Sachartre * only need to do it once because the geometry will never 18825081Sachartre * change. 18835081Sachartre */ 18845081Sachartre if (vd->vdisk_label == VD_DISK_LABEL_VTOC) 18855081Sachartre /* geometry was already validated */ 18865081Sachartre return (0); 18875081Sachartre 18885081Sachartre ASSERT(vd->vdisk_label == VD_DISK_LABEL_UNK); 18894963Sachartre vd_file_build_default_label(vd, &label); 18905081Sachartre vd->vdisk_label = VD_DISK_LABEL_VTOC; 18914963Sachartre } else { 18925081Sachartre if (VD_FILE_LABEL_READ(vd, &label) < 0) 18935081Sachartre return (EIO); 18945081Sachartre 18955081Sachartre if (label.dkl_magic != DKL_MAGIC || 18965081Sachartre label.dkl_cksum != vd_lbl2cksum(&label) || 18975081Sachartre label.dkl_vtoc.v_sanity != VTOC_SANE || 18985081Sachartre label.dkl_vtoc.v_nparts != V_NUMPAR) { 18995081Sachartre vd->vdisk_label = VD_DISK_LABEL_UNK; 19005081Sachartre vd_file_build_default_label(vd, &label); 19015081Sachartre status = EINVAL; 19025081Sachartre } else { 19035081Sachartre vd->vdisk_label = VD_DISK_LABEL_VTOC; 19045081Sachartre } 19054963Sachartre } 19064963Sachartre 19074963Sachartre /* Update the driver geometry */ 19084963Sachartre bzero(geom, sizeof (struct dk_geom)); 19094963Sachartre 19104963Sachartre geom->dkg_ncyl = label.dkl_ncyl; 19114963Sachartre geom->dkg_acyl = label.dkl_acyl; 19124963Sachartre geom->dkg_nhead = label.dkl_nhead; 19134963Sachartre geom->dkg_nsect = label.dkl_nsect; 19144963Sachartre geom->dkg_intrlv = label.dkl_intrlv; 19154963Sachartre geom->dkg_apc = label.dkl_apc; 19164963Sachartre geom->dkg_rpm = label.dkl_rpm; 19174963Sachartre geom->dkg_pcyl = label.dkl_pcyl; 19184963Sachartre geom->dkg_write_reinstruct = label.dkl_write_reinstruct; 19194963Sachartre geom->dkg_read_reinstruct = label.dkl_read_reinstruct; 19204963Sachartre 19214963Sachartre /* Update the driver vtoc */ 19224963Sachartre bzero(vtoc, sizeof (struct vtoc)); 19234963Sachartre 19244963Sachartre vtoc->v_sanity = label.dkl_vtoc.v_sanity; 19254963Sachartre vtoc->v_version = label.dkl_vtoc.v_version; 19264963Sachartre vtoc->v_sectorsz = DEV_BSIZE; 19274963Sachartre vtoc->v_nparts = label.dkl_vtoc.v_nparts; 19284963Sachartre 19294963Sachartre for (i = 0; i < vtoc->v_nparts; i++) { 19304963Sachartre vtoc->v_part[i].p_tag = 19314963Sachartre label.dkl_vtoc.v_part[i].p_tag; 19324963Sachartre vtoc->v_part[i].p_flag = 19334963Sachartre label.dkl_vtoc.v_part[i].p_flag; 19344963Sachartre vtoc->v_part[i].p_start = 19354963Sachartre label.dkl_map[i].dkl_cylno * 19364963Sachartre (label.dkl_nhead * label.dkl_nsect); 19374963Sachartre vtoc->v_part[i].p_size = label.dkl_map[i].dkl_nblk; 19384963Sachartre vtoc->timestamp[i] = 19394963Sachartre label.dkl_vtoc.v_timestamp[i]; 19404963Sachartre } 19414963Sachartre /* 19424963Sachartre * The bootinfo array can not be copied with bcopy() because 19434963Sachartre * elements are of type long in vtoc (so 64-bit) and of type 19444963Sachartre * int in dk_vtoc (so 32-bit). 19454963Sachartre */ 19464963Sachartre vtoc->v_bootinfo[0] = label.dkl_vtoc.v_bootinfo[0]; 19474963Sachartre vtoc->v_bootinfo[1] = label.dkl_vtoc.v_bootinfo[1]; 19484963Sachartre vtoc->v_bootinfo[2] = label.dkl_vtoc.v_bootinfo[2]; 19494963Sachartre bcopy(label.dkl_asciilabel, vtoc->v_asciilabel, 19504963Sachartre LEN_DKL_ASCII); 19514963Sachartre bcopy(label.dkl_vtoc.v_volume, vtoc->v_volume, 19524963Sachartre LEN_DKL_VVOL); 19534963Sachartre 19544963Sachartre return (status); 19554963Sachartre } 19564963Sachartre 19574963Sachartre /* 19584838Slm66018 * Handle ioctls to a disk image (file-based). 19594838Slm66018 * 19604838Slm66018 * Return Values 19614838Slm66018 * 0 - Indicates that there are no errors 19624838Slm66018 * != 0 - Disk operation returned an error 19634696Sachartre */ 19644696Sachartre static int 19654696Sachartre vd_do_file_ioctl(vd_t *vd, int cmd, void *ioctl_arg) 19664696Sachartre { 19674696Sachartre struct dk_label label; 19684696Sachartre struct dk_geom *geom; 19694696Sachartre struct vtoc *vtoc; 19704696Sachartre int i, rc; 19714696Sachartre 19724696Sachartre ASSERT(vd->file); 19734696Sachartre 19744696Sachartre switch (cmd) { 19754696Sachartre 19764696Sachartre case DKIOCGGEOM: 19774696Sachartre ASSERT(ioctl_arg != NULL); 19784696Sachartre geom = (struct dk_geom *)ioctl_arg; 19794696Sachartre 19804963Sachartre rc = vd_file_validate_geometry(vd); 19815081Sachartre if (rc != 0 && rc != EINVAL) { 19825081Sachartre ASSERT(vd->vdisk_type != VD_DISK_TYPE_SLICE); 19834963Sachartre return (rc); 19845081Sachartre } 19854963Sachartre 19864963Sachartre bcopy(&vd->dk_geom, geom, sizeof (struct dk_geom)); 19874696Sachartre return (0); 19884696Sachartre 19894696Sachartre case DKIOCGVTOC: 19904696Sachartre ASSERT(ioctl_arg != NULL); 19914696Sachartre vtoc = (struct vtoc *)ioctl_arg; 19924696Sachartre 19934963Sachartre rc = vd_file_validate_geometry(vd); 19945081Sachartre if (rc != 0 && rc != EINVAL) { 19955081Sachartre ASSERT(vd->vdisk_type != VD_DISK_TYPE_SLICE); 19964963Sachartre return (rc); 19975081Sachartre } 19984963Sachartre 19994963Sachartre bcopy(&vd->vtoc, vtoc, sizeof (struct vtoc)); 20004696Sachartre return (0); 20014696Sachartre 20024696Sachartre case DKIOCSGEOM: 20034696Sachartre ASSERT(ioctl_arg != NULL); 20044696Sachartre geom = (struct dk_geom *)ioctl_arg; 20054696Sachartre 20065081Sachartre /* geometry can only be changed for full disk */ 20075081Sachartre if (vd->vdisk_type != VD_DISK_TYPE_DISK) 20085081Sachartre return (ENOTSUP); 20095081Sachartre 20104696Sachartre if (geom->dkg_nhead == 0 || geom->dkg_nsect == 0) 20114696Sachartre return (EINVAL); 20124696Sachartre 20134696Sachartre /* 20144696Sachartre * The current device geometry is not updated, just the driver 20154696Sachartre * "notion" of it. The device geometry will be effectively 20164696Sachartre * updated when a label is written to the device during a next 20174696Sachartre * DKIOCSVTOC. 20184696Sachartre */ 20194696Sachartre bcopy(ioctl_arg, &vd->dk_geom, sizeof (vd->dk_geom)); 20204696Sachartre return (0); 20214696Sachartre 20224696Sachartre case DKIOCSVTOC: 20234696Sachartre ASSERT(ioctl_arg != NULL); 20244696Sachartre ASSERT(vd->dk_geom.dkg_nhead != 0 && 20254696Sachartre vd->dk_geom.dkg_nsect != 0); 20264696Sachartre vtoc = (struct vtoc *)ioctl_arg; 20274696Sachartre 20285081Sachartre /* vtoc can only be changed for full disk */ 20295081Sachartre if (vd->vdisk_type != VD_DISK_TYPE_DISK) 20305081Sachartre return (ENOTSUP); 20315081Sachartre 20324696Sachartre if (vtoc->v_sanity != VTOC_SANE || 20334696Sachartre vtoc->v_sectorsz != DEV_BSIZE || 20344696Sachartre vtoc->v_nparts != V_NUMPAR) 20354696Sachartre return (EINVAL); 20364696Sachartre 20374696Sachartre bzero(&label, sizeof (label)); 20384696Sachartre label.dkl_ncyl = vd->dk_geom.dkg_ncyl; 20394696Sachartre label.dkl_acyl = vd->dk_geom.dkg_acyl; 20404696Sachartre label.dkl_pcyl = vd->dk_geom.dkg_pcyl; 20414696Sachartre label.dkl_nhead = vd->dk_geom.dkg_nhead; 20424696Sachartre label.dkl_nsect = vd->dk_geom.dkg_nsect; 20434696Sachartre label.dkl_intrlv = vd->dk_geom.dkg_intrlv; 20444696Sachartre label.dkl_apc = vd->dk_geom.dkg_apc; 20454696Sachartre label.dkl_rpm = vd->dk_geom.dkg_rpm; 20464696Sachartre label.dkl_write_reinstruct = vd->dk_geom.dkg_write_reinstruct; 20474696Sachartre label.dkl_read_reinstruct = vd->dk_geom.dkg_read_reinstruct; 20484696Sachartre 20494696Sachartre label.dkl_vtoc.v_nparts = V_NUMPAR; 20504696Sachartre label.dkl_vtoc.v_sanity = VTOC_SANE; 20514696Sachartre label.dkl_vtoc.v_version = vtoc->v_version; 20524696Sachartre for (i = 0; i < V_NUMPAR; i++) { 20534696Sachartre label.dkl_vtoc.v_timestamp[i] = 20544696Sachartre vtoc->timestamp[i]; 20554696Sachartre label.dkl_vtoc.v_part[i].p_tag = 20564696Sachartre vtoc->v_part[i].p_tag; 20574696Sachartre label.dkl_vtoc.v_part[i].p_flag = 20584696Sachartre vtoc->v_part[i].p_flag; 20594696Sachartre label.dkl_map[i].dkl_cylno = 20604696Sachartre vtoc->v_part[i].p_start / 20614696Sachartre (label.dkl_nhead * label.dkl_nsect); 20624696Sachartre label.dkl_map[i].dkl_nblk = 20634696Sachartre vtoc->v_part[i].p_size; 20644696Sachartre } 20654696Sachartre /* 20664696Sachartre * The bootinfo array can not be copied with bcopy() because 20674696Sachartre * elements are of type long in vtoc (so 64-bit) and of type 20684696Sachartre * int in dk_vtoc (so 32-bit). 20694696Sachartre */ 20704696Sachartre label.dkl_vtoc.v_bootinfo[0] = vtoc->v_bootinfo[0]; 20714696Sachartre label.dkl_vtoc.v_bootinfo[1] = vtoc->v_bootinfo[1]; 20724696Sachartre label.dkl_vtoc.v_bootinfo[2] = vtoc->v_bootinfo[2]; 20734696Sachartre bcopy(vtoc->v_asciilabel, label.dkl_asciilabel, 20744696Sachartre LEN_DKL_ASCII); 20754696Sachartre bcopy(vtoc->v_volume, label.dkl_vtoc.v_volume, 20764696Sachartre LEN_DKL_VVOL); 20774696Sachartre 20784696Sachartre /* re-compute checksum */ 20794696Sachartre label.dkl_magic = DKL_MAGIC; 20804696Sachartre label.dkl_cksum = vd_lbl2cksum(&label); 20814696Sachartre 20824696Sachartre /* write label to the disk image */ 20834696Sachartre if ((rc = vd_file_set_vtoc(vd, &label)) != 0) 20844696Sachartre return (rc); 20854696Sachartre 20864963Sachartre /* check the geometry and update the driver info */ 20874963Sachartre if ((rc = vd_file_validate_geometry(vd)) != 0) 20884963Sachartre return (rc); 20894696Sachartre 20904696Sachartre /* 20914696Sachartre * The disk geometry may have changed, so we need to write 20924696Sachartre * the devid (if there is one) so that it is stored at the 20934696Sachartre * right location. 20944696Sachartre */ 20954696Sachartre if (vd->file_devid != NULL && 20964696Sachartre vd_file_write_devid(vd, vd->file_devid) != 0) { 20974696Sachartre PR0("Fail to write devid"); 20984696Sachartre } 20994696Sachartre 21004696Sachartre return (0); 21014696Sachartre 21025188Szk194757 case DKIOCFLUSHWRITECACHE: 21035331Samw return (VOP_FSYNC(vd->file_vnode, FSYNC, kcred, NULL)); 21045188Szk194757 21054696Sachartre default: 21064696Sachartre return (ENOTSUP); 21074696Sachartre } 21084696Sachartre } 21094696Sachartre 21104838Slm66018 /* 21114838Slm66018 * Description: 21124838Slm66018 * This is the function that processes the ioctl requests (farming it 21134838Slm66018 * out to functions that handle slices, files or whole disks) 21144838Slm66018 * 21154838Slm66018 * Return Values 21164838Slm66018 * 0 - ioctl operation completed successfully 21174838Slm66018 * != 0 - The LDC error value encountered 21184838Slm66018 * (propagated back up the call stack as a task error) 21194838Slm66018 * 21204838Slm66018 * Side Effect 21214838Slm66018 * sets request->status to the return value of the ioctl function. 21224838Slm66018 */ 21231991Sheppo static int 21242032Slm66018 vd_do_ioctl(vd_t *vd, vd_dring_payload_t *request, void* buf, vd_ioctl_t *ioctl) 21251991Sheppo { 21264838Slm66018 int rval = 0, status = 0; 21271991Sheppo size_t nbytes = request->nbytes; /* modifiable copy */ 21281991Sheppo 21291991Sheppo 21301991Sheppo ASSERT(request->slice < vd->nslices); 21311991Sheppo PR0("Performing %s", ioctl->operation_name); 21321991Sheppo 21332032Slm66018 /* Get data from client and convert, if necessary */ 21342032Slm66018 if (ioctl->copyin != NULL) { 21351991Sheppo ASSERT(nbytes != 0 && buf != NULL); 21361991Sheppo PR1("Getting \"arg\" data from client"); 21371991Sheppo if ((status = ldc_mem_copy(vd->ldc_handle, buf, 0, &nbytes, 21384696Sachartre request->cookie, request->ncookies, 21394696Sachartre LDC_COPY_IN)) != 0) { 21402793Slm66018 PR0("ldc_mem_copy() returned errno %d " 21411991Sheppo "copying from client", status); 21421991Sheppo return (status); 21431991Sheppo } 21442032Slm66018 21452032Slm66018 /* Convert client's data, if necessary */ 21462032Slm66018 if (ioctl->copyin == VD_IDENTITY) /* use client buffer */ 21472032Slm66018 ioctl->arg = buf; 21482032Slm66018 else /* convert client vdisk operation data to ioctl data */ 21492032Slm66018 (ioctl->copyin)(buf, (void *)ioctl->arg); 21501991Sheppo } 21511991Sheppo 21521991Sheppo /* 21531991Sheppo * Handle single-slice block devices internally; otherwise, have the 21541991Sheppo * real driver perform the ioctl() 21551991Sheppo */ 21564696Sachartre if (vd->file) { 21574838Slm66018 request->status = 21584838Slm66018 vd_do_file_ioctl(vd, ioctl->cmd, (void *)ioctl->arg); 21594838Slm66018 21604696Sachartre } else if (vd->vdisk_type == VD_DISK_TYPE_SLICE && !vd->pseudo) { 21614838Slm66018 request->status = 21624838Slm66018 vd_do_slice_ioctl(vd, ioctl->cmd, (void *)ioctl->arg); 21634838Slm66018 21644838Slm66018 } else { 21654838Slm66018 request->status = ldi_ioctl(vd->ldi_handle[request->slice], 21665081Sachartre ioctl->cmd, (intptr_t)ioctl->arg, vd->open_flags | FKIOCTL, 21674838Slm66018 kcred, &rval); 21684838Slm66018 21694838Slm66018 #ifdef DEBUG 21704838Slm66018 if (rval != 0) { 21714838Slm66018 PR0("%s set rval = %d, which is not being returned to" 21724838Slm66018 " client", ioctl->cmd_name, rval); 21734838Slm66018 } 21744838Slm66018 #endif /* DEBUG */ 21751991Sheppo } 21764838Slm66018 21774838Slm66018 if (request->status != 0) { 21784838Slm66018 PR0("ioctl(%s) = errno %d", ioctl->cmd_name, request->status); 21794838Slm66018 return (0); 21801991Sheppo } 21811991Sheppo 21822032Slm66018 /* Convert data and send to client, if necessary */ 21832032Slm66018 if (ioctl->copyout != NULL) { 21841991Sheppo ASSERT(nbytes != 0 && buf != NULL); 21851991Sheppo PR1("Sending \"arg\" data to client"); 21862032Slm66018 21872032Slm66018 /* Convert ioctl data to vdisk operation data, if necessary */ 21882032Slm66018 if (ioctl->copyout != VD_IDENTITY) 21892032Slm66018 (ioctl->copyout)((void *)ioctl->arg, buf); 21902032Slm66018 21911991Sheppo if ((status = ldc_mem_copy(vd->ldc_handle, buf, 0, &nbytes, 21924696Sachartre request->cookie, request->ncookies, 21934696Sachartre LDC_COPY_OUT)) != 0) { 21942793Slm66018 PR0("ldc_mem_copy() returned errno %d " 21951991Sheppo "copying to client", status); 21961991Sheppo return (status); 21971991Sheppo } 21981991Sheppo } 21991991Sheppo 22001991Sheppo return (status); 22011991Sheppo } 22021991Sheppo 22031991Sheppo #define RNDSIZE(expr) P2ROUNDUP(sizeof (expr), sizeof (uint64_t)) 22044838Slm66018 22054838Slm66018 /* 22064838Slm66018 * Description: 22074838Slm66018 * This generic function is called by the task queue to complete 22084838Slm66018 * the processing of the tasks. The specific completion function 22094838Slm66018 * is passed in as a field in the task pointer. 22104838Slm66018 * 22114838Slm66018 * Parameters: 22124838Slm66018 * arg - opaque pointer to structure containing task to be completed 22134838Slm66018 * 22144838Slm66018 * Return Values 22154838Slm66018 * None 22164838Slm66018 */ 22174838Slm66018 static void 22184838Slm66018 vd_complete(void *arg) 22194838Slm66018 { 22204838Slm66018 vd_task_t *task = (vd_task_t *)arg; 22214838Slm66018 22224838Slm66018 ASSERT(task != NULL); 22234838Slm66018 ASSERT(task->status == EINPROGRESS); 22244838Slm66018 ASSERT(task->completef != NULL); 22254838Slm66018 22264838Slm66018 task->status = task->completef(task); 22274838Slm66018 if (task->status) 22284838Slm66018 PR0("%s: Error %d completing task", __func__, task->status); 22294838Slm66018 22304838Slm66018 /* Now notify the vDisk client */ 22314838Slm66018 vd_complete_notify(task); 22324838Slm66018 } 22334838Slm66018 22341991Sheppo static int 22352336Snarayan vd_ioctl(vd_task_t *task) 22361991Sheppo { 22374696Sachartre int i, status; 22382336Snarayan void *buf = NULL; 22392336Snarayan struct dk_geom dk_geom = {0}; 22402336Snarayan struct vtoc vtoc = {0}; 22412531Snarayan struct dk_efi dk_efi = {0}; 22422336Snarayan vd_t *vd = task->vd; 22432336Snarayan vd_dring_payload_t *request = task->request; 22442336Snarayan vd_ioctl_t ioctl[] = { 22451991Sheppo /* Command (no-copy) operations */ 22462032Slm66018 {VD_OP_FLUSH, STRINGIZE(VD_OP_FLUSH), 0, 22472032Slm66018 DKIOCFLUSHWRITECACHE, STRINGIZE(DKIOCFLUSHWRITECACHE), 22485081Sachartre NULL, NULL, NULL, B_TRUE}, 22491991Sheppo 22501991Sheppo /* "Get" (copy-out) operations */ 22512032Slm66018 {VD_OP_GET_WCE, STRINGIZE(VD_OP_GET_WCE), RNDSIZE(int), 22522032Slm66018 DKIOCGETWCE, STRINGIZE(DKIOCGETWCE), 22535081Sachartre NULL, VD_IDENTITY, VD_IDENTITY, B_FALSE}, 22542032Slm66018 {VD_OP_GET_DISKGEOM, STRINGIZE(VD_OP_GET_DISKGEOM), 22552032Slm66018 RNDSIZE(vd_geom_t), 22562032Slm66018 DKIOCGGEOM, STRINGIZE(DKIOCGGEOM), 22575081Sachartre &dk_geom, NULL, dk_geom2vd_geom, B_FALSE}, 22582032Slm66018 {VD_OP_GET_VTOC, STRINGIZE(VD_OP_GET_VTOC), RNDSIZE(vd_vtoc_t), 22592032Slm66018 DKIOCGVTOC, STRINGIZE(DKIOCGVTOC), 22605081Sachartre &vtoc, NULL, vtoc2vd_vtoc, B_FALSE}, 22612531Snarayan {VD_OP_GET_EFI, STRINGIZE(VD_OP_GET_EFI), RNDSIZE(vd_efi_t), 22622531Snarayan DKIOCGETEFI, STRINGIZE(DKIOCGETEFI), 22635081Sachartre &dk_efi, vd_get_efi_in, vd_get_efi_out, B_FALSE}, 22641991Sheppo 22651991Sheppo /* "Set" (copy-in) operations */ 22662032Slm66018 {VD_OP_SET_WCE, STRINGIZE(VD_OP_SET_WCE), RNDSIZE(int), 22672032Slm66018 DKIOCSETWCE, STRINGIZE(DKIOCSETWCE), 22685081Sachartre NULL, VD_IDENTITY, VD_IDENTITY, B_TRUE}, 22692032Slm66018 {VD_OP_SET_DISKGEOM, STRINGIZE(VD_OP_SET_DISKGEOM), 22702032Slm66018 RNDSIZE(vd_geom_t), 22712032Slm66018 DKIOCSGEOM, STRINGIZE(DKIOCSGEOM), 22725081Sachartre &dk_geom, vd_geom2dk_geom, NULL, B_TRUE}, 22732032Slm66018 {VD_OP_SET_VTOC, STRINGIZE(VD_OP_SET_VTOC), RNDSIZE(vd_vtoc_t), 22742032Slm66018 DKIOCSVTOC, STRINGIZE(DKIOCSVTOC), 22755081Sachartre &vtoc, vd_vtoc2vtoc, NULL, B_TRUE}, 22762531Snarayan {VD_OP_SET_EFI, STRINGIZE(VD_OP_SET_EFI), RNDSIZE(vd_efi_t), 22772531Snarayan DKIOCSETEFI, STRINGIZE(DKIOCSETEFI), 22785081Sachartre &dk_efi, vd_set_efi_in, vd_set_efi_out, B_TRUE}, 22791991Sheppo }; 22801991Sheppo size_t nioctls = (sizeof (ioctl))/(sizeof (ioctl[0])); 22811991Sheppo 22821991Sheppo 22832336Snarayan ASSERT(vd != NULL); 22842336Snarayan ASSERT(request != NULL); 22851991Sheppo ASSERT(request->slice < vd->nslices); 22861991Sheppo 22871991Sheppo /* 22881991Sheppo * Determine ioctl corresponding to caller's "operation" and 22891991Sheppo * validate caller's "nbytes" 22901991Sheppo */ 22911991Sheppo for (i = 0; i < nioctls; i++) { 22921991Sheppo if (request->operation == ioctl[i].operation) { 22932032Slm66018 /* LDC memory operations require 8-byte multiples */ 22942032Slm66018 ASSERT(ioctl[i].nbytes % sizeof (uint64_t) == 0); 22952032Slm66018 22962531Snarayan if (request->operation == VD_OP_GET_EFI || 22972531Snarayan request->operation == VD_OP_SET_EFI) { 22982531Snarayan if (request->nbytes >= ioctl[i].nbytes) 22992531Snarayan break; 23002793Slm66018 PR0("%s: Expected at least nbytes = %lu, " 23012531Snarayan "got %lu", ioctl[i].operation_name, 23022531Snarayan ioctl[i].nbytes, request->nbytes); 23032531Snarayan return (EINVAL); 23042531Snarayan } 23052531Snarayan 23062032Slm66018 if (request->nbytes != ioctl[i].nbytes) { 23072793Slm66018 PR0("%s: Expected nbytes = %lu, got %lu", 23082032Slm66018 ioctl[i].operation_name, ioctl[i].nbytes, 23092032Slm66018 request->nbytes); 23101991Sheppo return (EINVAL); 23111991Sheppo } 23121991Sheppo 23131991Sheppo break; 23141991Sheppo } 23151991Sheppo } 23161991Sheppo ASSERT(i < nioctls); /* because "operation" already validated */ 23171991Sheppo 23185081Sachartre if (!(vd->open_flags & FWRITE) && ioctl[i].write) { 23195081Sachartre PR0("%s fails because backend is opened read-only", 23205081Sachartre ioctl[i].operation_name); 23215081Sachartre request->status = EROFS; 23225081Sachartre return (0); 23235081Sachartre } 23245081Sachartre 23251991Sheppo if (request->nbytes) 23261991Sheppo buf = kmem_zalloc(request->nbytes, KM_SLEEP); 23271991Sheppo status = vd_do_ioctl(vd, request, buf, &ioctl[i]); 23281991Sheppo if (request->nbytes) 23291991Sheppo kmem_free(buf, request->nbytes); 23304696Sachartre 23311991Sheppo return (status); 23321991Sheppo } 23331991Sheppo 23342531Snarayan static int 23352531Snarayan vd_get_devid(vd_task_t *task) 23362531Snarayan { 23372531Snarayan vd_t *vd = task->vd; 23382531Snarayan vd_dring_payload_t *request = task->request; 23392531Snarayan vd_devid_t *vd_devid; 23402531Snarayan impl_devid_t *devid; 23414696Sachartre int status, bufid_len, devid_len, len, sz; 23422793Slm66018 int bufbytes; 23432793Slm66018 23442793Slm66018 PR1("Get Device ID, nbytes=%ld", request->nbytes); 23452531Snarayan 23463401Snarayan if (vd->file) { 23474696Sachartre if (vd->file_devid == NULL) { 23484696Sachartre PR2("No Device ID"); 23494838Slm66018 request->status = ENOENT; 23504838Slm66018 return (0); 23514696Sachartre } else { 23524696Sachartre sz = ddi_devid_sizeof(vd->file_devid); 23534696Sachartre devid = kmem_alloc(sz, KM_SLEEP); 23544696Sachartre bcopy(vd->file_devid, devid, sz); 23554696Sachartre } 23564696Sachartre } else { 23574696Sachartre if (ddi_lyr_get_devid(vd->dev[request->slice], 23584696Sachartre (ddi_devid_t *)&devid) != DDI_SUCCESS) { 23594696Sachartre PR2("No Device ID"); 23604838Slm66018 request->status = ENOENT; 23614838Slm66018 return (0); 23624696Sachartre } 23632531Snarayan } 23642531Snarayan 23652531Snarayan bufid_len = request->nbytes - sizeof (vd_devid_t) + 1; 23662531Snarayan devid_len = DEVID_GETLEN(devid); 23672531Snarayan 23682793Slm66018 /* 23692793Slm66018 * Save the buffer size here for use in deallocation. 23702793Slm66018 * The actual number of bytes copied is returned in 23712793Slm66018 * the 'nbytes' field of the request structure. 23722793Slm66018 */ 23732793Slm66018 bufbytes = request->nbytes; 23742793Slm66018 23752793Slm66018 vd_devid = kmem_zalloc(bufbytes, KM_SLEEP); 23762531Snarayan vd_devid->length = devid_len; 23772531Snarayan vd_devid->type = DEVID_GETTYPE(devid); 23782531Snarayan 23792531Snarayan len = (devid_len > bufid_len)? bufid_len : devid_len; 23802531Snarayan 23812531Snarayan bcopy(devid->did_id, vd_devid->id, len); 23822531Snarayan 23834963Sachartre request->status = 0; 23844963Sachartre 23852531Snarayan /* LDC memory operations require 8-byte multiples */ 23862531Snarayan ASSERT(request->nbytes % sizeof (uint64_t) == 0); 23872531Snarayan 23882531Snarayan if ((status = ldc_mem_copy(vd->ldc_handle, (caddr_t)vd_devid, 0, 23892531Snarayan &request->nbytes, request->cookie, request->ncookies, 23902531Snarayan LDC_COPY_OUT)) != 0) { 23912793Slm66018 PR0("ldc_mem_copy() returned errno %d copying to client", 23922531Snarayan status); 23932531Snarayan } 23942793Slm66018 PR1("post mem_copy: nbytes=%ld", request->nbytes); 23952793Slm66018 23962793Slm66018 kmem_free(vd_devid, bufbytes); 23972531Snarayan ddi_devid_free((ddi_devid_t)devid); 23982531Snarayan 23992531Snarayan return (status); 24002531Snarayan } 24012531Snarayan 24021991Sheppo /* 24031991Sheppo * Define the supported operations once the functions for performing them have 24041991Sheppo * been defined 24051991Sheppo */ 24061991Sheppo static const vds_operation_t vds_operation[] = { 24072793Slm66018 #define X(_s) #_s, _s 24082793Slm66018 {X(VD_OP_BREAD), vd_start_bio, vd_complete_bio}, 24092793Slm66018 {X(VD_OP_BWRITE), vd_start_bio, vd_complete_bio}, 24102793Slm66018 {X(VD_OP_FLUSH), vd_ioctl, NULL}, 24112793Slm66018 {X(VD_OP_GET_WCE), vd_ioctl, NULL}, 24122793Slm66018 {X(VD_OP_SET_WCE), vd_ioctl, NULL}, 24132793Slm66018 {X(VD_OP_GET_VTOC), vd_ioctl, NULL}, 24142793Slm66018 {X(VD_OP_SET_VTOC), vd_ioctl, NULL}, 24152793Slm66018 {X(VD_OP_GET_DISKGEOM), vd_ioctl, NULL}, 24162793Slm66018 {X(VD_OP_SET_DISKGEOM), vd_ioctl, NULL}, 24172793Slm66018 {X(VD_OP_GET_EFI), vd_ioctl, NULL}, 24182793Slm66018 {X(VD_OP_SET_EFI), vd_ioctl, NULL}, 24192793Slm66018 {X(VD_OP_GET_DEVID), vd_get_devid, NULL}, 24202793Slm66018 #undef X 24211991Sheppo }; 24221991Sheppo 24231991Sheppo static const size_t vds_noperations = 24241991Sheppo (sizeof (vds_operation))/(sizeof (vds_operation[0])); 24251991Sheppo 24261991Sheppo /* 24272336Snarayan * Process a task specifying a client I/O request 24284838Slm66018 * 24294838Slm66018 * Parameters: 24304838Slm66018 * task - structure containing the request sent from client 24314838Slm66018 * 24324838Slm66018 * Return Value 24334838Slm66018 * 0 - success 24344838Slm66018 * ENOTSUP - Unknown/Unsupported VD_OP_XXX operation 24354838Slm66018 * EINVAL - Invalid disk slice 24364838Slm66018 * != 0 - some other non-zero return value from start function 24371991Sheppo */ 24381991Sheppo static int 24394838Slm66018 vd_do_process_task(vd_task_t *task) 24401991Sheppo { 24414838Slm66018 int i; 24422336Snarayan vd_t *vd = task->vd; 24432336Snarayan vd_dring_payload_t *request = task->request; 24442336Snarayan 24452336Snarayan ASSERT(vd != NULL); 24462336Snarayan ASSERT(request != NULL); 24471991Sheppo 24482336Snarayan /* Find the requested operation */ 24494838Slm66018 for (i = 0; i < vds_noperations; i++) { 24504838Slm66018 if (request->operation == vds_operation[i].operation) { 24514838Slm66018 /* all operations should have a start func */ 24524838Slm66018 ASSERT(vds_operation[i].start != NULL); 24534838Slm66018 24544838Slm66018 task->completef = vds_operation[i].complete; 24552336Snarayan break; 24564838Slm66018 } 24574838Slm66018 } 2458*5365Slm66018 2459*5365Slm66018 /* 2460*5365Slm66018 * We need to check that the requested operation is permitted 2461*5365Slm66018 * for the particular client that sent it or that the loop above 2462*5365Slm66018 * did not complete without finding the operation type (indicating 2463*5365Slm66018 * that the requested operation is unknown/unimplemented) 2464*5365Slm66018 */ 2465*5365Slm66018 if ((VD_OP_SUPPORTED(vd->operations, request->operation) == B_FALSE) || 2466*5365Slm66018 (i == vds_noperations)) { 24672793Slm66018 PR0("Unsupported operation %u", request->operation); 2468*5365Slm66018 request->status = ENOTSUP; 2469*5365Slm66018 return (0); 24702336Snarayan } 24712336Snarayan 24722748Slm66018 /* Range-check slice */ 24734696Sachartre if (request->slice >= vd->nslices && 24744696Sachartre (vd->vdisk_type != VD_DISK_TYPE_DISK || 24754696Sachartre request->slice != VD_SLICE_NONE)) { 24762793Slm66018 PR0("Invalid \"slice\" %u (max %u) for virtual disk", 24772748Slm66018 request->slice, (vd->nslices - 1)); 24782748Slm66018 return (EINVAL); 24792748Slm66018 } 24802748Slm66018 24814838Slm66018 /* 24824838Slm66018 * Call the function pointer that starts the operation. 24834838Slm66018 */ 24844838Slm66018 return (vds_operation[i].start(task)); 24854838Slm66018 } 24864838Slm66018 24874838Slm66018 /* 24884838Slm66018 * Description: 24894838Slm66018 * This function is called by both the in-band and descriptor ring 24904838Slm66018 * message processing functions paths to actually execute the task 24914838Slm66018 * requested by the vDisk client. It in turn calls its worker 24924838Slm66018 * function, vd_do_process_task(), to carry our the request. 24934838Slm66018 * 24944838Slm66018 * Any transport errors (e.g. LDC errors, vDisk protocol errors) are 24954838Slm66018 * saved in the 'status' field of the task and are propagated back 24964838Slm66018 * up the call stack to trigger a NACK 24974838Slm66018 * 24984838Slm66018 * Any request errors (e.g. ENOTTY from an ioctl) are saved in 24994838Slm66018 * the 'status' field of the request and result in an ACK being sent 25004838Slm66018 * by the completion handler. 25014838Slm66018 * 25024838Slm66018 * Parameters: 25034838Slm66018 * task - structure containing the request sent from client 25044838Slm66018 * 25054838Slm66018 * Return Value 25064838Slm66018 * 0 - successful synchronous request. 25074838Slm66018 * != 0 - transport error (e.g. LDC errors, vDisk protocol) 25084838Slm66018 * EINPROGRESS - task will be finished in a completion handler 25094838Slm66018 */ 25104838Slm66018 static int 25114838Slm66018 vd_process_task(vd_task_t *task) 25124838Slm66018 { 25134838Slm66018 vd_t *vd = task->vd; 25144838Slm66018 int status; 25154838Slm66018 25164838Slm66018 DTRACE_PROBE1(task__start, vd_task_t *, task); 25174838Slm66018 25184838Slm66018 task->status = vd_do_process_task(task); 25194838Slm66018 25204838Slm66018 /* 25214838Slm66018 * If the task processing function returned EINPROGRESS indicating 25224838Slm66018 * that the task needs completing then schedule a taskq entry to 25234838Slm66018 * finish it now. 25244838Slm66018 * 25254838Slm66018 * Otherwise the task processing function returned either zero 25264838Slm66018 * indicating that the task was finished in the start function (and we 25274838Slm66018 * don't need to wait in a completion function) or the start function 25284838Slm66018 * returned an error - in both cases all that needs to happen is the 25294838Slm66018 * notification to the vDisk client higher up the call stack. 25304838Slm66018 * If the task was using a Descriptor Ring, we need to mark it as done 25314838Slm66018 * at this stage. 25324838Slm66018 */ 25334838Slm66018 if (task->status == EINPROGRESS) { 25344838Slm66018 /* Queue a task to complete the operation */ 25354838Slm66018 (void) ddi_taskq_dispatch(vd->completionq, vd_complete, 25364838Slm66018 task, DDI_SLEEP); 25374838Slm66018 25384838Slm66018 } else if (!vd->reset_state && (vd->xfer_mode == VIO_DRING_MODE)) { 25394838Slm66018 /* Update the dring element if it's a dring client */ 25404838Slm66018 status = vd_mark_elem_done(vd, task->index, 25414838Slm66018 task->request->status, task->request->nbytes); 25424838Slm66018 if (status == ECONNRESET) 25434838Slm66018 vd_mark_in_reset(vd); 25441991Sheppo } 25451991Sheppo 25464838Slm66018 return (task->status); 25471991Sheppo } 25481991Sheppo 25491991Sheppo /* 25502032Slm66018 * Return true if the "type", "subtype", and "env" fields of the "tag" first 25512032Slm66018 * argument match the corresponding remaining arguments; otherwise, return false 25521991Sheppo */ 25532032Slm66018 boolean_t 25541991Sheppo vd_msgtype(vio_msg_tag_t *tag, int type, int subtype, int env) 25551991Sheppo { 25561991Sheppo return ((tag->vio_msgtype == type) && 25574696Sachartre (tag->vio_subtype == subtype) && 25584696Sachartre (tag->vio_subtype_env == env)) ? B_TRUE : B_FALSE; 25591991Sheppo } 25601991Sheppo 25612032Slm66018 /* 25622032Slm66018 * Check whether the major/minor version specified in "ver_msg" is supported 25632032Slm66018 * by this server. 25642032Slm66018 */ 25652032Slm66018 static boolean_t 25662032Slm66018 vds_supported_version(vio_ver_msg_t *ver_msg) 25672032Slm66018 { 25682032Slm66018 for (int i = 0; i < vds_num_versions; i++) { 25692032Slm66018 ASSERT(vds_version[i].major > 0); 25702032Slm66018 ASSERT((i == 0) || 25712032Slm66018 (vds_version[i].major < vds_version[i-1].major)); 25722032Slm66018 25732032Slm66018 /* 25742032Slm66018 * If the major versions match, adjust the minor version, if 25752032Slm66018 * necessary, down to the highest value supported by this 25762032Slm66018 * server and return true so this message will get "ack"ed; 25772032Slm66018 * the client should also support all minor versions lower 25782032Slm66018 * than the value it sent 25792032Slm66018 */ 25802032Slm66018 if (ver_msg->ver_major == vds_version[i].major) { 25812032Slm66018 if (ver_msg->ver_minor > vds_version[i].minor) { 25822032Slm66018 PR0("Adjusting minor version from %u to %u", 25832032Slm66018 ver_msg->ver_minor, vds_version[i].minor); 25842032Slm66018 ver_msg->ver_minor = vds_version[i].minor; 25852032Slm66018 } 25862032Slm66018 return (B_TRUE); 25872032Slm66018 } 25882032Slm66018 25892032Slm66018 /* 25902032Slm66018 * If the message contains a higher major version number, set 25912032Slm66018 * the message's major/minor versions to the current values 25922032Slm66018 * and return false, so this message will get "nack"ed with 25932032Slm66018 * these values, and the client will potentially try again 25942032Slm66018 * with the same or a lower version 25952032Slm66018 */ 25962032Slm66018 if (ver_msg->ver_major > vds_version[i].major) { 25972032Slm66018 ver_msg->ver_major = vds_version[i].major; 25982032Slm66018 ver_msg->ver_minor = vds_version[i].minor; 25992032Slm66018 return (B_FALSE); 26002032Slm66018 } 26012032Slm66018 26022032Slm66018 /* 26032032Slm66018 * Otherwise, the message's major version is less than the 26042032Slm66018 * current major version, so continue the loop to the next 26052032Slm66018 * (lower) supported version 26062032Slm66018 */ 26072032Slm66018 } 26082032Slm66018 26092032Slm66018 /* 26102032Slm66018 * No common version was found; "ground" the version pair in the 26112032Slm66018 * message to terminate negotiation 26122032Slm66018 */ 26132032Slm66018 ver_msg->ver_major = 0; 26142032Slm66018 ver_msg->ver_minor = 0; 26152032Slm66018 return (B_FALSE); 26162032Slm66018 } 26172032Slm66018 26182032Slm66018 /* 26192032Slm66018 * Process a version message from a client. vds expects to receive version 26202032Slm66018 * messages from clients seeking service, but never issues version messages 26212032Slm66018 * itself; therefore, vds can ACK or NACK client version messages, but does 26222032Slm66018 * not expect to receive version-message ACKs or NACKs (and will treat such 26232032Slm66018 * messages as invalid). 26242032Slm66018 */ 26251991Sheppo static int 26262032Slm66018 vd_process_ver_msg(vd_t *vd, vio_msg_t *msg, size_t msglen) 26271991Sheppo { 26281991Sheppo vio_ver_msg_t *ver_msg = (vio_ver_msg_t *)msg; 26291991Sheppo 26301991Sheppo 26311991Sheppo ASSERT(msglen >= sizeof (msg->tag)); 26321991Sheppo 26331991Sheppo if (!vd_msgtype(&msg->tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, 26344696Sachartre VIO_VER_INFO)) { 26351991Sheppo return (ENOMSG); /* not a version message */ 26361991Sheppo } 26371991Sheppo 26381991Sheppo if (msglen != sizeof (*ver_msg)) { 26392793Slm66018 PR0("Expected %lu-byte version message; " 26401991Sheppo "received %lu bytes", sizeof (*ver_msg), msglen); 26411991Sheppo return (EBADMSG); 26421991Sheppo } 26431991Sheppo 26441991Sheppo if (ver_msg->dev_class != VDEV_DISK) { 26452793Slm66018 PR0("Expected device class %u (disk); received %u", 26461991Sheppo VDEV_DISK, ver_msg->dev_class); 26471991Sheppo return (EBADMSG); 26481991Sheppo } 26491991Sheppo 26502032Slm66018 /* 26512032Slm66018 * We're talking to the expected kind of client; set our device class 26522032Slm66018 * for "ack/nack" back to the client 26532032Slm66018 */ 26542032Slm66018 ver_msg->dev_class = VDEV_DISK_SERVER; 26552032Slm66018 26562032Slm66018 /* 26572032Slm66018 * Check whether the (valid) version message specifies a version 26582032Slm66018 * supported by this server. If the version is not supported, return 26592032Slm66018 * EBADMSG so the message will get "nack"ed; vds_supported_version() 26602032Slm66018 * will have updated the message with a supported version for the 26612032Slm66018 * client to consider 26622032Slm66018 */ 26632032Slm66018 if (!vds_supported_version(ver_msg)) 26641991Sheppo return (EBADMSG); 26652032Slm66018 26662032Slm66018 26672032Slm66018 /* 26682032Slm66018 * A version has been agreed upon; use the client's SID for 26692032Slm66018 * communication on this channel now 26702032Slm66018 */ 26712032Slm66018 ASSERT(!(vd->initialized & VD_SID)); 26722032Slm66018 vd->sid = ver_msg->tag.vio_sid; 26732032Slm66018 vd->initialized |= VD_SID; 26741991Sheppo 26752032Slm66018 /* 2676*5365Slm66018 * Store the negotiated major and minor version values in the "vd" data 2677*5365Slm66018 * structure so that we can check if certain operations are supported 2678*5365Slm66018 * by the client. 26792032Slm66018 */ 2680*5365Slm66018 vd->version.major = ver_msg->ver_major; 2681*5365Slm66018 vd->version.minor = ver_msg->ver_minor; 26822032Slm66018 26832032Slm66018 PR0("Using major version %u, minor version %u", 26842032Slm66018 ver_msg->ver_major, ver_msg->ver_minor); 26851991Sheppo return (0); 26861991Sheppo } 26871991Sheppo 2688*5365Slm66018 static void 2689*5365Slm66018 vd_set_exported_operations(vd_t *vd) 2690*5365Slm66018 { 2691*5365Slm66018 vd->operations = 0; /* clear field */ 2692*5365Slm66018 2693*5365Slm66018 /* 2694*5365Slm66018 * We need to check from the highest version supported to the 2695*5365Slm66018 * lowest because versions with a higher minor number implicitly 2696*5365Slm66018 * support versions with a lower minor number. 2697*5365Slm66018 */ 2698*5365Slm66018 if (vio_ver_is_supported(vd->version, 1, 1)) { 2699*5365Slm66018 ASSERT(vd->open_flags & FREAD); 2700*5365Slm66018 vd->operations |= VD_OP_MASK_READ; 2701*5365Slm66018 2702*5365Slm66018 if (vd->open_flags & FWRITE) 2703*5365Slm66018 vd->operations |= VD_OP_MASK_WRITE; 2704*5365Slm66018 2705*5365Slm66018 if (vd->file && vd_file_is_iso_image(vd)) { 2706*5365Slm66018 /* 2707*5365Slm66018 * can't write to ISO images, make sure that write 2708*5365Slm66018 * support is not set in case administrator did not 2709*5365Slm66018 * use "options=ro" when doing an ldm add-vdsdev 2710*5365Slm66018 */ 2711*5365Slm66018 vd->operations &= ~VD_OP_MASK_WRITE; 2712*5365Slm66018 } 2713*5365Slm66018 } else if (vio_ver_is_supported(vd->version, 1, 0)) { 2714*5365Slm66018 vd->operations = VD_OP_MASK_READ | VD_OP_MASK_WRITE; 2715*5365Slm66018 } 2716*5365Slm66018 2717*5365Slm66018 /* we should have already agreed on a version */ 2718*5365Slm66018 ASSERT(vd->operations != 0); 2719*5365Slm66018 } 2720*5365Slm66018 27211991Sheppo static int 27221991Sheppo vd_process_attr_msg(vd_t *vd, vio_msg_t *msg, size_t msglen) 27231991Sheppo { 27241991Sheppo vd_attr_msg_t *attr_msg = (vd_attr_msg_t *)msg; 27253401Snarayan int status, retry = 0; 27261991Sheppo 27271991Sheppo 27281991Sheppo ASSERT(msglen >= sizeof (msg->tag)); 27291991Sheppo 27301991Sheppo if (!vd_msgtype(&msg->tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, 27314696Sachartre VIO_ATTR_INFO)) { 27322336Snarayan PR0("Message is not an attribute message"); 27332336Snarayan return (ENOMSG); 27341991Sheppo } 27351991Sheppo 27361991Sheppo if (msglen != sizeof (*attr_msg)) { 27372793Slm66018 PR0("Expected %lu-byte attribute message; " 27381991Sheppo "received %lu bytes", sizeof (*attr_msg), msglen); 27391991Sheppo return (EBADMSG); 27401991Sheppo } 27411991Sheppo 27421991Sheppo if (attr_msg->max_xfer_sz == 0) { 27432793Slm66018 PR0("Received maximum transfer size of 0 from client"); 27441991Sheppo return (EBADMSG); 27451991Sheppo } 27461991Sheppo 27471991Sheppo if ((attr_msg->xfer_mode != VIO_DESC_MODE) && 27481991Sheppo (attr_msg->xfer_mode != VIO_DRING_MODE)) { 27492793Slm66018 PR0("Client requested unsupported transfer mode"); 27501991Sheppo return (EBADMSG); 27511991Sheppo } 27521991Sheppo 27533401Snarayan /* 27543401Snarayan * check if the underlying disk is ready, if not try accessing 27553401Snarayan * the device again. Open the vdisk device and extract info 27563401Snarayan * about it, as this is needed to respond to the attr info msg 27573401Snarayan */ 27583401Snarayan if ((vd->initialized & VD_DISK_READY) == 0) { 27593401Snarayan PR0("Retry setting up disk (%s)", vd->device_path); 27603401Snarayan do { 27613401Snarayan status = vd_setup_vd(vd); 27623401Snarayan if (status != EAGAIN || ++retry > vds_dev_retries) 27633401Snarayan break; 27643401Snarayan 27653401Snarayan /* incremental delay */ 27663401Snarayan delay(drv_usectohz(vds_dev_delay)); 27673401Snarayan 27683401Snarayan /* if vdisk is no longer enabled - return error */ 27693401Snarayan if (!vd_enabled(vd)) 27703401Snarayan return (ENXIO); 27713401Snarayan 27723401Snarayan } while (status == EAGAIN); 27733401Snarayan 27743401Snarayan if (status) 27753401Snarayan return (ENXIO); 27763401Snarayan 27773401Snarayan vd->initialized |= VD_DISK_READY; 27783401Snarayan ASSERT(vd->nslices > 0 && vd->nslices <= V_NUMPAR); 27793401Snarayan PR0("vdisk_type = %s, pseudo = %s, file = %s, nslices = %u", 27803401Snarayan ((vd->vdisk_type == VD_DISK_TYPE_DISK) ? "disk" : "slice"), 27813401Snarayan (vd->pseudo ? "yes" : "no"), 27823401Snarayan (vd->file ? "yes" : "no"), 27833401Snarayan vd->nslices); 27843401Snarayan } 27853401Snarayan 27861991Sheppo /* Success: valid message and transfer mode */ 27871991Sheppo vd->xfer_mode = attr_msg->xfer_mode; 27882793Slm66018 27891991Sheppo if (vd->xfer_mode == VIO_DESC_MODE) { 27902793Slm66018 27911991Sheppo /* 27921991Sheppo * The vd_dring_inband_msg_t contains one cookie; need room 27931991Sheppo * for up to n-1 more cookies, where "n" is the number of full 27941991Sheppo * pages plus possibly one partial page required to cover 27951991Sheppo * "max_xfer_sz". Add room for one more cookie if 27961991Sheppo * "max_xfer_sz" isn't an integral multiple of the page size. 27971991Sheppo * Must first get the maximum transfer size in bytes. 27981991Sheppo */ 27991991Sheppo size_t max_xfer_bytes = attr_msg->vdisk_block_size ? 28001991Sheppo attr_msg->vdisk_block_size*attr_msg->max_xfer_sz : 28011991Sheppo attr_msg->max_xfer_sz; 28021991Sheppo size_t max_inband_msglen = 28031991Sheppo sizeof (vd_dring_inband_msg_t) + 28041991Sheppo ((max_xfer_bytes/PAGESIZE + 28054696Sachartre ((max_xfer_bytes % PAGESIZE) ? 1 : 0))* 28064696Sachartre (sizeof (ldc_mem_cookie_t))); 28071991Sheppo 28081991Sheppo /* 28091991Sheppo * Set the maximum expected message length to 28101991Sheppo * accommodate in-band-descriptor messages with all 28111991Sheppo * their cookies 28121991Sheppo */ 28131991Sheppo vd->max_msglen = MAX(vd->max_msglen, max_inband_msglen); 28142336Snarayan 28152336Snarayan /* 28162336Snarayan * Initialize the data structure for processing in-band I/O 28172336Snarayan * request descriptors 28182336Snarayan */ 28192336Snarayan vd->inband_task.vd = vd; 28202793Slm66018 vd->inband_task.msg = kmem_alloc(vd->max_msglen, KM_SLEEP); 28212336Snarayan vd->inband_task.index = 0; 28222336Snarayan vd->inband_task.type = VD_FINAL_RANGE_TASK; /* range == 1 */ 28231991Sheppo } 28241991Sheppo 28252410Slm66018 /* Return the device's block size and max transfer size to the client */ 28262410Slm66018 attr_msg->vdisk_block_size = DEV_BSIZE; 2827*5365Slm66018 attr_msg->vdisk_block_size = vd->block_size; 28282410Slm66018 attr_msg->max_xfer_sz = vd->max_xfer_sz; 28292410Slm66018 28301991Sheppo attr_msg->vdisk_size = vd->vdisk_size; 28311991Sheppo attr_msg->vdisk_type = vd->vdisk_type; 2832*5365Slm66018 attr_msg->vdisk_media = vd->vdisk_media; 2833*5365Slm66018 2834*5365Slm66018 /* Discover and save the list of supported VD_OP_XXX operations */ 2835*5365Slm66018 vd_set_exported_operations(vd); 2836*5365Slm66018 attr_msg->operations = vd->operations; 2837*5365Slm66018 28381991Sheppo PR0("%s", VD_CLIENT(vd)); 28392793Slm66018 28402793Slm66018 ASSERT(vd->dring_task == NULL); 28412793Slm66018 28421991Sheppo return (0); 28431991Sheppo } 28441991Sheppo 28451991Sheppo static int 28461991Sheppo vd_process_dring_reg_msg(vd_t *vd, vio_msg_t *msg, size_t msglen) 28471991Sheppo { 28481991Sheppo int status; 28491991Sheppo size_t expected; 28501991Sheppo ldc_mem_info_t dring_minfo; 28511991Sheppo vio_dring_reg_msg_t *reg_msg = (vio_dring_reg_msg_t *)msg; 28521991Sheppo 28531991Sheppo 28541991Sheppo ASSERT(msglen >= sizeof (msg->tag)); 28551991Sheppo 28561991Sheppo if (!vd_msgtype(&msg->tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, 28574696Sachartre VIO_DRING_REG)) { 28582336Snarayan PR0("Message is not a register-dring message"); 28592336Snarayan return (ENOMSG); 28601991Sheppo } 28611991Sheppo 28621991Sheppo if (msglen < sizeof (*reg_msg)) { 28632793Slm66018 PR0("Expected at least %lu-byte register-dring message; " 28641991Sheppo "received %lu bytes", sizeof (*reg_msg), msglen); 28651991Sheppo return (EBADMSG); 28661991Sheppo } 28671991Sheppo 28681991Sheppo expected = sizeof (*reg_msg) + 28691991Sheppo (reg_msg->ncookies - 1)*(sizeof (reg_msg->cookie[0])); 28701991Sheppo if (msglen != expected) { 28712793Slm66018 PR0("Expected %lu-byte register-dring message; " 28721991Sheppo "received %lu bytes", expected, msglen); 28731991Sheppo return (EBADMSG); 28741991Sheppo } 28751991Sheppo 28761991Sheppo if (vd->initialized & VD_DRING) { 28772793Slm66018 PR0("A dring was previously registered; only support one"); 28781991Sheppo return (EBADMSG); 28791991Sheppo } 28801991Sheppo 28812336Snarayan if (reg_msg->num_descriptors > INT32_MAX) { 28822793Slm66018 PR0("reg_msg->num_descriptors = %u; must be <= %u (%s)", 28832336Snarayan reg_msg->ncookies, INT32_MAX, STRINGIZE(INT32_MAX)); 28842336Snarayan return (EBADMSG); 28852336Snarayan } 28862336Snarayan 28871991Sheppo if (reg_msg->ncookies != 1) { 28881991Sheppo /* 28891991Sheppo * In addition to fixing the assertion in the success case 28901991Sheppo * below, supporting drings which require more than one 28911991Sheppo * "cookie" requires increasing the value of vd->max_msglen 28921991Sheppo * somewhere in the code path prior to receiving the message 28931991Sheppo * which results in calling this function. Note that without 28941991Sheppo * making this change, the larger message size required to 28951991Sheppo * accommodate multiple cookies cannot be successfully 28961991Sheppo * received, so this function will not even get called. 28971991Sheppo * Gracefully accommodating more dring cookies might 28981991Sheppo * reasonably demand exchanging an additional attribute or 28991991Sheppo * making a minor protocol adjustment 29001991Sheppo */ 29012793Slm66018 PR0("reg_msg->ncookies = %u != 1", reg_msg->ncookies); 29021991Sheppo return (EBADMSG); 29031991Sheppo } 29041991Sheppo 29051991Sheppo status = ldc_mem_dring_map(vd->ldc_handle, reg_msg->cookie, 29061991Sheppo reg_msg->ncookies, reg_msg->num_descriptors, 29072531Snarayan reg_msg->descriptor_size, LDC_DIRECT_MAP, &vd->dring_handle); 29081991Sheppo if (status != 0) { 29092793Slm66018 PR0("ldc_mem_dring_map() returned errno %d", status); 29101991Sheppo return (status); 29111991Sheppo } 29121991Sheppo 29131991Sheppo /* 29141991Sheppo * To remove the need for this assertion, must call 29151991Sheppo * ldc_mem_dring_nextcookie() successfully ncookies-1 times after a 29161991Sheppo * successful call to ldc_mem_dring_map() 29171991Sheppo */ 29181991Sheppo ASSERT(reg_msg->ncookies == 1); 29191991Sheppo 29201991Sheppo if ((status = 29214696Sachartre ldc_mem_dring_info(vd->dring_handle, &dring_minfo)) != 0) { 29222793Slm66018 PR0("ldc_mem_dring_info() returned errno %d", status); 29231991Sheppo if ((status = ldc_mem_dring_unmap(vd->dring_handle)) != 0) 29242793Slm66018 PR0("ldc_mem_dring_unmap() returned errno %d", status); 29251991Sheppo return (status); 29261991Sheppo } 29271991Sheppo 29281991Sheppo if (dring_minfo.vaddr == NULL) { 29292793Slm66018 PR0("Descriptor ring virtual address is NULL"); 29302032Slm66018 return (ENXIO); 29311991Sheppo } 29321991Sheppo 29331991Sheppo 29342336Snarayan /* Initialize for valid message and mapped dring */ 29351991Sheppo PR1("descriptor size = %u, dring length = %u", 29361991Sheppo vd->descriptor_size, vd->dring_len); 29371991Sheppo vd->initialized |= VD_DRING; 29381991Sheppo vd->dring_ident = 1; /* "There Can Be Only One" */ 29391991Sheppo vd->dring = dring_minfo.vaddr; 29401991Sheppo vd->descriptor_size = reg_msg->descriptor_size; 29411991Sheppo vd->dring_len = reg_msg->num_descriptors; 29421991Sheppo reg_msg->dring_ident = vd->dring_ident; 29432336Snarayan 29442336Snarayan /* 29452336Snarayan * Allocate and initialize a "shadow" array of data structures for 29462336Snarayan * tasks to process I/O requests in dring elements 29472336Snarayan */ 29482336Snarayan vd->dring_task = 29492336Snarayan kmem_zalloc((sizeof (*vd->dring_task)) * vd->dring_len, KM_SLEEP); 29502336Snarayan for (int i = 0; i < vd->dring_len; i++) { 29512336Snarayan vd->dring_task[i].vd = vd; 29522336Snarayan vd->dring_task[i].index = i; 29532336Snarayan vd->dring_task[i].request = &VD_DRING_ELEM(i)->payload; 29542531Snarayan 29552531Snarayan status = ldc_mem_alloc_handle(vd->ldc_handle, 29562531Snarayan &(vd->dring_task[i].mhdl)); 29572531Snarayan if (status) { 29582793Slm66018 PR0("ldc_mem_alloc_handle() returned err %d ", status); 29592531Snarayan return (ENXIO); 29602531Snarayan } 29612793Slm66018 29622793Slm66018 vd->dring_task[i].msg = kmem_alloc(vd->max_msglen, KM_SLEEP); 29632336Snarayan } 29642336Snarayan 29651991Sheppo return (0); 29661991Sheppo } 29671991Sheppo 29681991Sheppo static int 29691991Sheppo vd_process_dring_unreg_msg(vd_t *vd, vio_msg_t *msg, size_t msglen) 29701991Sheppo { 29711991Sheppo vio_dring_unreg_msg_t *unreg_msg = (vio_dring_unreg_msg_t *)msg; 29721991Sheppo 29731991Sheppo 29741991Sheppo ASSERT(msglen >= sizeof (msg->tag)); 29751991Sheppo 29761991Sheppo if (!vd_msgtype(&msg->tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, 29774696Sachartre VIO_DRING_UNREG)) { 29782336Snarayan PR0("Message is not an unregister-dring message"); 29792336Snarayan return (ENOMSG); 29801991Sheppo } 29811991Sheppo 29821991Sheppo if (msglen != sizeof (*unreg_msg)) { 29832793Slm66018 PR0("Expected %lu-byte unregister-dring message; " 29841991Sheppo "received %lu bytes", sizeof (*unreg_msg), msglen); 29851991Sheppo return (EBADMSG); 29861991Sheppo } 29871991Sheppo 29881991Sheppo if (unreg_msg->dring_ident != vd->dring_ident) { 29892793Slm66018 PR0("Expected dring ident %lu; received %lu", 29901991Sheppo vd->dring_ident, unreg_msg->dring_ident); 29911991Sheppo return (EBADMSG); 29921991Sheppo } 29931991Sheppo 29941991Sheppo return (0); 29951991Sheppo } 29961991Sheppo 29971991Sheppo static int 29981991Sheppo process_rdx_msg(vio_msg_t *msg, size_t msglen) 29991991Sheppo { 30001991Sheppo ASSERT(msglen >= sizeof (msg->tag)); 30011991Sheppo 30022336Snarayan if (!vd_msgtype(&msg->tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_RDX)) { 30032336Snarayan PR0("Message is not an RDX message"); 30042336Snarayan return (ENOMSG); 30052336Snarayan } 30061991Sheppo 30071991Sheppo if (msglen != sizeof (vio_rdx_msg_t)) { 30082793Slm66018 PR0("Expected %lu-byte RDX message; received %lu bytes", 30091991Sheppo sizeof (vio_rdx_msg_t), msglen); 30101991Sheppo return (EBADMSG); 30111991Sheppo } 30121991Sheppo 30132336Snarayan PR0("Valid RDX message"); 30141991Sheppo return (0); 30151991Sheppo } 30161991Sheppo 30171991Sheppo static int 30181991Sheppo vd_check_seq_num(vd_t *vd, uint64_t seq_num) 30191991Sheppo { 30201991Sheppo if ((vd->initialized & VD_SEQ_NUM) && (seq_num != vd->seq_num + 1)) { 30212793Slm66018 PR0("Received seq_num %lu; expected %lu", 30221991Sheppo seq_num, (vd->seq_num + 1)); 30232793Slm66018 PR0("initiating soft reset"); 30242336Snarayan vd_need_reset(vd, B_FALSE); 30251991Sheppo return (1); 30261991Sheppo } 30271991Sheppo 30281991Sheppo vd->seq_num = seq_num; 30291991Sheppo vd->initialized |= VD_SEQ_NUM; /* superfluous after first time... */ 30301991Sheppo return (0); 30311991Sheppo } 30321991Sheppo 30331991Sheppo /* 30341991Sheppo * Return the expected size of an inband-descriptor message with all the 30351991Sheppo * cookies it claims to include 30361991Sheppo */ 30371991Sheppo static size_t 30381991Sheppo expected_inband_size(vd_dring_inband_msg_t *msg) 30391991Sheppo { 30401991Sheppo return ((sizeof (*msg)) + 30411991Sheppo (msg->payload.ncookies - 1)*(sizeof (msg->payload.cookie[0]))); 30421991Sheppo } 30431991Sheppo 30441991Sheppo /* 30451991Sheppo * Process an in-band descriptor message: used with clients like OBP, with 30461991Sheppo * which vds exchanges descriptors within VIO message payloads, rather than 30471991Sheppo * operating on them within a descriptor ring 30481991Sheppo */ 30491991Sheppo static int 30502793Slm66018 vd_process_desc_msg(vd_t *vd, vio_msg_t *msg, size_t msglen) 30511991Sheppo { 30521991Sheppo size_t expected; 30531991Sheppo vd_dring_inband_msg_t *desc_msg = (vd_dring_inband_msg_t *)msg; 30541991Sheppo 30551991Sheppo 30561991Sheppo ASSERT(msglen >= sizeof (msg->tag)); 30571991Sheppo 30581991Sheppo if (!vd_msgtype(&msg->tag, VIO_TYPE_DATA, VIO_SUBTYPE_INFO, 30594696Sachartre VIO_DESC_DATA)) { 30602336Snarayan PR1("Message is not an in-band-descriptor message"); 30612336Snarayan return (ENOMSG); 30622336Snarayan } 30631991Sheppo 30641991Sheppo if (msglen < sizeof (*desc_msg)) { 30652793Slm66018 PR0("Expected at least %lu-byte descriptor message; " 30661991Sheppo "received %lu bytes", sizeof (*desc_msg), msglen); 30671991Sheppo return (EBADMSG); 30681991Sheppo } 30691991Sheppo 30701991Sheppo if (msglen != (expected = expected_inband_size(desc_msg))) { 30712793Slm66018 PR0("Expected %lu-byte descriptor message; " 30721991Sheppo "received %lu bytes", expected, msglen); 30731991Sheppo return (EBADMSG); 30741991Sheppo } 30751991Sheppo 30762336Snarayan if (vd_check_seq_num(vd, desc_msg->hdr.seq_num) != 0) 30771991Sheppo return (EBADMSG); 30782336Snarayan 30792336Snarayan /* 30802336Snarayan * Valid message: Set up the in-band descriptor task and process the 30812336Snarayan * request. Arrange to acknowledge the client's message, unless an 30822336Snarayan * error processing the descriptor task results in setting 30832336Snarayan * VIO_SUBTYPE_NACK 30842336Snarayan */ 30852336Snarayan PR1("Valid in-band-descriptor message"); 30862336Snarayan msg->tag.vio_subtype = VIO_SUBTYPE_ACK; 30872793Slm66018 30882793Slm66018 ASSERT(vd->inband_task.msg != NULL); 30892793Slm66018 30902793Slm66018 bcopy(msg, vd->inband_task.msg, msglen); 30912336Snarayan vd->inband_task.msglen = msglen; 30922793Slm66018 30932793Slm66018 /* 30942793Slm66018 * The task request is now the payload of the message 30952793Slm66018 * that was just copied into the body of the task. 30962793Slm66018 */ 30972793Slm66018 desc_msg = (vd_dring_inband_msg_t *)vd->inband_task.msg; 30982336Snarayan vd->inband_task.request = &desc_msg->payload; 30992793Slm66018 31002336Snarayan return (vd_process_task(&vd->inband_task)); 31011991Sheppo } 31021991Sheppo 31031991Sheppo static int 31042336Snarayan vd_process_element(vd_t *vd, vd_task_type_t type, uint32_t idx, 31052793Slm66018 vio_msg_t *msg, size_t msglen) 31061991Sheppo { 31072336Snarayan int status; 31082336Snarayan boolean_t ready; 31092336Snarayan vd_dring_entry_t *elem = VD_DRING_ELEM(idx); 31102336Snarayan 31112336Snarayan 31122336Snarayan /* Accept the updated dring element */ 31132336Snarayan if ((status = ldc_mem_dring_acquire(vd->dring_handle, idx, idx)) != 0) { 31142793Slm66018 PR0("ldc_mem_dring_acquire() returned errno %d", status); 31151991Sheppo return (status); 31161991Sheppo } 31172336Snarayan ready = (elem->hdr.dstate == VIO_DESC_READY); 31182336Snarayan if (ready) { 31192336Snarayan elem->hdr.dstate = VIO_DESC_ACCEPTED; 31202336Snarayan } else { 31212793Slm66018 PR0("descriptor %u not ready", idx); 31222336Snarayan VD_DUMP_DRING_ELEM(elem); 31232336Snarayan } 31242336Snarayan if ((status = ldc_mem_dring_release(vd->dring_handle, idx, idx)) != 0) { 31252793Slm66018 PR0("ldc_mem_dring_release() returned errno %d", status); 31261991Sheppo return (status); 31271991Sheppo } 31282336Snarayan if (!ready) 31292336Snarayan return (EBUSY); 31302336Snarayan 31312336Snarayan 31322336Snarayan /* Initialize a task and process the accepted element */ 31332336Snarayan PR1("Processing dring element %u", idx); 31342336Snarayan vd->dring_task[idx].type = type; 31352793Slm66018 31362793Slm66018 /* duplicate msg buf for cookies etc. */ 31372793Slm66018 bcopy(msg, vd->dring_task[idx].msg, msglen); 31382793Slm66018 31392336Snarayan vd->dring_task[idx].msglen = msglen; 31404838Slm66018 return (vd_process_task(&vd->dring_task[idx])); 31411991Sheppo } 31421991Sheppo 31431991Sheppo static int 31442336Snarayan vd_process_element_range(vd_t *vd, int start, int end, 31452793Slm66018 vio_msg_t *msg, size_t msglen) 31462336Snarayan { 31472336Snarayan int i, n, nelem, status = 0; 31482336Snarayan boolean_t inprogress = B_FALSE; 31492336Snarayan vd_task_type_t type; 31502336Snarayan 31512336Snarayan 31522336Snarayan ASSERT(start >= 0); 31532336Snarayan ASSERT(end >= 0); 31542336Snarayan 31552336Snarayan /* 31562336Snarayan * Arrange to acknowledge the client's message, unless an error 31572336Snarayan * processing one of the dring elements results in setting 31582336Snarayan * VIO_SUBTYPE_NACK 31592336Snarayan */ 31602336Snarayan msg->tag.vio_subtype = VIO_SUBTYPE_ACK; 31612336Snarayan 31622336Snarayan /* 31632336Snarayan * Process the dring elements in the range 31642336Snarayan */ 31652336Snarayan nelem = ((end < start) ? end + vd->dring_len : end) - start + 1; 31662336Snarayan for (i = start, n = nelem; n > 0; i = (i + 1) % vd->dring_len, n--) { 31672336Snarayan ((vio_dring_msg_t *)msg)->end_idx = i; 31682336Snarayan type = (n == 1) ? VD_FINAL_RANGE_TASK : VD_NONFINAL_RANGE_TASK; 31692793Slm66018 status = vd_process_element(vd, type, i, msg, msglen); 31702336Snarayan if (status == EINPROGRESS) 31712336Snarayan inprogress = B_TRUE; 31722336Snarayan else if (status != 0) 31732336Snarayan break; 31742336Snarayan } 31752336Snarayan 31762336Snarayan /* 31772336Snarayan * If some, but not all, operations of a multi-element range are in 31782336Snarayan * progress, wait for other operations to complete before returning 31792336Snarayan * (which will result in "ack" or "nack" of the message). Note that 31802336Snarayan * all outstanding operations will need to complete, not just the ones 31812336Snarayan * corresponding to the current range of dring elements; howevever, as 31822336Snarayan * this situation is an error case, performance is less critical. 31832336Snarayan */ 31842336Snarayan if ((nelem > 1) && (status != EINPROGRESS) && inprogress) 31852336Snarayan ddi_taskq_wait(vd->completionq); 31862336Snarayan 31872336Snarayan return (status); 31882336Snarayan } 31892336Snarayan 31902336Snarayan static int 31912793Slm66018 vd_process_dring_msg(vd_t *vd, vio_msg_t *msg, size_t msglen) 31921991Sheppo { 31931991Sheppo vio_dring_msg_t *dring_msg = (vio_dring_msg_t *)msg; 31941991Sheppo 31951991Sheppo 31961991Sheppo ASSERT(msglen >= sizeof (msg->tag)); 31971991Sheppo 31981991Sheppo if (!vd_msgtype(&msg->tag, VIO_TYPE_DATA, VIO_SUBTYPE_INFO, 31994696Sachartre VIO_DRING_DATA)) { 32002336Snarayan PR1("Message is not a dring-data message"); 32012336Snarayan return (ENOMSG); 32021991Sheppo } 32031991Sheppo 32041991Sheppo if (msglen != sizeof (*dring_msg)) { 32052793Slm66018 PR0("Expected %lu-byte dring message; received %lu bytes", 32061991Sheppo sizeof (*dring_msg), msglen); 32071991Sheppo return (EBADMSG); 32081991Sheppo } 32091991Sheppo 32102336Snarayan if (vd_check_seq_num(vd, dring_msg->seq_num) != 0) 32111991Sheppo return (EBADMSG); 32121991Sheppo 32131991Sheppo if (dring_msg->dring_ident != vd->dring_ident) { 32142793Slm66018 PR0("Expected dring ident %lu; received ident %lu", 32151991Sheppo vd->dring_ident, dring_msg->dring_ident); 32161991Sheppo return (EBADMSG); 32171991Sheppo } 32181991Sheppo 32192336Snarayan if (dring_msg->start_idx >= vd->dring_len) { 32202793Slm66018 PR0("\"start_idx\" = %u; must be less than %u", 32212336Snarayan dring_msg->start_idx, vd->dring_len); 32222336Snarayan return (EBADMSG); 32232336Snarayan } 32242336Snarayan 32252336Snarayan if ((dring_msg->end_idx < 0) || 32262336Snarayan (dring_msg->end_idx >= vd->dring_len)) { 32272793Slm66018 PR0("\"end_idx\" = %u; must be >= 0 and less than %u", 32282336Snarayan dring_msg->end_idx, vd->dring_len); 32292336Snarayan return (EBADMSG); 32302336Snarayan } 32312336Snarayan 32322336Snarayan /* Valid message; process range of updated dring elements */ 32332336Snarayan PR1("Processing descriptor range, start = %u, end = %u", 32342336Snarayan dring_msg->start_idx, dring_msg->end_idx); 32352336Snarayan return (vd_process_element_range(vd, dring_msg->start_idx, 32364696Sachartre dring_msg->end_idx, msg, msglen)); 32371991Sheppo } 32381991Sheppo 32391991Sheppo static int 32401991Sheppo recv_msg(ldc_handle_t ldc_handle, void *msg, size_t *nbytes) 32411991Sheppo { 32421991Sheppo int retry, status; 32431991Sheppo size_t size = *nbytes; 32441991Sheppo 32451991Sheppo 32461991Sheppo for (retry = 0, status = ETIMEDOUT; 32471991Sheppo retry < vds_ldc_retries && status == ETIMEDOUT; 32481991Sheppo retry++) { 32491991Sheppo PR1("ldc_read() attempt %d", (retry + 1)); 32501991Sheppo *nbytes = size; 32511991Sheppo status = ldc_read(ldc_handle, msg, nbytes); 32521991Sheppo } 32531991Sheppo 32542793Slm66018 if (status) { 32552793Slm66018 PR0("ldc_read() returned errno %d", status); 32562793Slm66018 if (status != ECONNRESET) 32572793Slm66018 return (ENOMSG); 32581991Sheppo return (status); 32591991Sheppo } else if (*nbytes == 0) { 32601991Sheppo PR1("ldc_read() returned 0 and no message read"); 32611991Sheppo return (ENOMSG); 32621991Sheppo } 32631991Sheppo 32641991Sheppo PR1("RCVD %lu-byte message", *nbytes); 32651991Sheppo return (0); 32661991Sheppo } 32671991Sheppo 32681991Sheppo static int 32692793Slm66018 vd_do_process_msg(vd_t *vd, vio_msg_t *msg, size_t msglen) 32701991Sheppo { 32711991Sheppo int status; 32721991Sheppo 32731991Sheppo 32741991Sheppo PR1("Processing (%x/%x/%x) message", msg->tag.vio_msgtype, 32751991Sheppo msg->tag.vio_subtype, msg->tag.vio_subtype_env); 32762793Slm66018 #ifdef DEBUG 32772793Slm66018 vd_decode_tag(msg); 32782793Slm66018 #endif 32791991Sheppo 32801991Sheppo /* 32811991Sheppo * Validate session ID up front, since it applies to all messages 32821991Sheppo * once set 32831991Sheppo */ 32841991Sheppo if ((msg->tag.vio_sid != vd->sid) && (vd->initialized & VD_SID)) { 32852793Slm66018 PR0("Expected SID %u, received %u", vd->sid, 32861991Sheppo msg->tag.vio_sid); 32871991Sheppo return (EBADMSG); 32881991Sheppo } 32891991Sheppo 32902793Slm66018 PR1("\tWhile in state %d (%s)", vd->state, vd_decode_state(vd->state)); 32911991Sheppo 32921991Sheppo /* 32931991Sheppo * Process the received message based on connection state 32941991Sheppo */ 32951991Sheppo switch (vd->state) { 32961991Sheppo case VD_STATE_INIT: /* expect version message */ 32972032Slm66018 if ((status = vd_process_ver_msg(vd, msg, msglen)) != 0) 32981991Sheppo return (status); 32991991Sheppo 33001991Sheppo /* Version negotiated, move to that state */ 33011991Sheppo vd->state = VD_STATE_VER; 33021991Sheppo return (0); 33031991Sheppo 33041991Sheppo case VD_STATE_VER: /* expect attribute message */ 33051991Sheppo if ((status = vd_process_attr_msg(vd, msg, msglen)) != 0) 33061991Sheppo return (status); 33071991Sheppo 33081991Sheppo /* Attributes exchanged, move to that state */ 33091991Sheppo vd->state = VD_STATE_ATTR; 33101991Sheppo return (0); 33111991Sheppo 33121991Sheppo case VD_STATE_ATTR: 33131991Sheppo switch (vd->xfer_mode) { 33141991Sheppo case VIO_DESC_MODE: /* expect RDX message */ 33151991Sheppo if ((status = process_rdx_msg(msg, msglen)) != 0) 33161991Sheppo return (status); 33171991Sheppo 33181991Sheppo /* Ready to receive in-band descriptors */ 33191991Sheppo vd->state = VD_STATE_DATA; 33201991Sheppo return (0); 33211991Sheppo 33221991Sheppo case VIO_DRING_MODE: /* expect register-dring message */ 33231991Sheppo if ((status = 33244696Sachartre vd_process_dring_reg_msg(vd, msg, msglen)) != 0) 33251991Sheppo return (status); 33261991Sheppo 33271991Sheppo /* One dring negotiated, move to that state */ 33281991Sheppo vd->state = VD_STATE_DRING; 33291991Sheppo return (0); 33301991Sheppo 33311991Sheppo default: 33321991Sheppo ASSERT("Unsupported transfer mode"); 33332793Slm66018 PR0("Unsupported transfer mode"); 33341991Sheppo return (ENOTSUP); 33351991Sheppo } 33361991Sheppo 33371991Sheppo case VD_STATE_DRING: /* expect RDX, register-dring, or unreg-dring */ 33381991Sheppo if ((status = process_rdx_msg(msg, msglen)) == 0) { 33391991Sheppo /* Ready to receive data */ 33401991Sheppo vd->state = VD_STATE_DATA; 33411991Sheppo return (0); 33421991Sheppo } else if (status != ENOMSG) { 33431991Sheppo return (status); 33441991Sheppo } 33451991Sheppo 33461991Sheppo 33471991Sheppo /* 33481991Sheppo * If another register-dring message is received, stay in 33491991Sheppo * dring state in case the client sends RDX; although the 33501991Sheppo * protocol allows multiple drings, this server does not 33511991Sheppo * support using more than one 33521991Sheppo */ 33531991Sheppo if ((status = 33544696Sachartre vd_process_dring_reg_msg(vd, msg, msglen)) != ENOMSG) 33551991Sheppo return (status); 33561991Sheppo 33571991Sheppo /* 33581991Sheppo * Acknowledge an unregister-dring message, but reset the 33591991Sheppo * connection anyway: Although the protocol allows 33601991Sheppo * unregistering drings, this server cannot serve a vdisk 33611991Sheppo * without its only dring 33621991Sheppo */ 33631991Sheppo status = vd_process_dring_unreg_msg(vd, msg, msglen); 33641991Sheppo return ((status == 0) ? ENOTSUP : status); 33651991Sheppo 33661991Sheppo case VD_STATE_DATA: 33671991Sheppo switch (vd->xfer_mode) { 33681991Sheppo case VIO_DESC_MODE: /* expect in-band-descriptor message */ 33692793Slm66018 return (vd_process_desc_msg(vd, msg, msglen)); 33701991Sheppo 33711991Sheppo case VIO_DRING_MODE: /* expect dring-data or unreg-dring */ 33721991Sheppo /* 33731991Sheppo * Typically expect dring-data messages, so handle 33741991Sheppo * them first 33751991Sheppo */ 33761991Sheppo if ((status = vd_process_dring_msg(vd, msg, 33774696Sachartre msglen)) != ENOMSG) 33781991Sheppo return (status); 33791991Sheppo 33801991Sheppo /* 33811991Sheppo * Acknowledge an unregister-dring message, but reset 33821991Sheppo * the connection anyway: Although the protocol 33831991Sheppo * allows unregistering drings, this server cannot 33841991Sheppo * serve a vdisk without its only dring 33851991Sheppo */ 33861991Sheppo status = vd_process_dring_unreg_msg(vd, msg, msglen); 33871991Sheppo return ((status == 0) ? ENOTSUP : status); 33881991Sheppo 33891991Sheppo default: 33901991Sheppo ASSERT("Unsupported transfer mode"); 33912793Slm66018 PR0("Unsupported transfer mode"); 33921991Sheppo return (ENOTSUP); 33931991Sheppo } 33941991Sheppo 33951991Sheppo default: 33961991Sheppo ASSERT("Invalid client connection state"); 33972793Slm66018 PR0("Invalid client connection state"); 33981991Sheppo return (ENOTSUP); 33991991Sheppo } 34001991Sheppo } 34011991Sheppo 34022336Snarayan static int 34032793Slm66018 vd_process_msg(vd_t *vd, vio_msg_t *msg, size_t msglen) 34041991Sheppo { 34051991Sheppo int status; 34061991Sheppo boolean_t reset_ldc = B_FALSE; 34074838Slm66018 vd_task_t task; 34081991Sheppo 34091991Sheppo /* 34101991Sheppo * Check that the message is at least big enough for a "tag", so that 34111991Sheppo * message processing can proceed based on tag-specified message type 34121991Sheppo */ 34131991Sheppo if (msglen < sizeof (vio_msg_tag_t)) { 34142793Slm66018 PR0("Received short (%lu-byte) message", msglen); 34151991Sheppo /* Can't "nack" short message, so drop the big hammer */ 34162793Slm66018 PR0("initiating full reset"); 34172336Snarayan vd_need_reset(vd, B_TRUE); 34182336Snarayan return (EBADMSG); 34191991Sheppo } 34201991Sheppo 34211991Sheppo /* 34221991Sheppo * Process the message 34231991Sheppo */ 34242793Slm66018 switch (status = vd_do_process_msg(vd, msg, msglen)) { 34251991Sheppo case 0: 34261991Sheppo /* "ack" valid, successfully-processed messages */ 34271991Sheppo msg->tag.vio_subtype = VIO_SUBTYPE_ACK; 34281991Sheppo break; 34291991Sheppo 34302336Snarayan case EINPROGRESS: 34312336Snarayan /* The completion handler will "ack" or "nack" the message */ 34322336Snarayan return (EINPROGRESS); 34331991Sheppo case ENOMSG: 34342793Slm66018 PR0("Received unexpected message"); 34351991Sheppo _NOTE(FALLTHROUGH); 34361991Sheppo case EBADMSG: 34371991Sheppo case ENOTSUP: 34384838Slm66018 /* "transport" error will cause NACK of invalid messages */ 34391991Sheppo msg->tag.vio_subtype = VIO_SUBTYPE_NACK; 34401991Sheppo break; 34411991Sheppo 34421991Sheppo default: 34434838Slm66018 /* "transport" error will cause NACK of invalid messages */ 34441991Sheppo msg->tag.vio_subtype = VIO_SUBTYPE_NACK; 34451991Sheppo /* An LDC error probably occurred, so try resetting it */ 34461991Sheppo reset_ldc = B_TRUE; 34471991Sheppo break; 34481991Sheppo } 34491991Sheppo 34502793Slm66018 PR1("\tResulting in state %d (%s)", vd->state, 34514696Sachartre vd_decode_state(vd->state)); 34522793Slm66018 34534838Slm66018 /* populate the task so we can dispatch it on the taskq */ 34544838Slm66018 task.vd = vd; 34554838Slm66018 task.msg = msg; 34564838Slm66018 task.msglen = msglen; 34574838Slm66018 34584838Slm66018 /* 34594838Slm66018 * Queue a task to send the notification that the operation completed. 34604838Slm66018 * We need to ensure that requests are responded to in the correct 34614838Slm66018 * order and since the taskq is processed serially this ordering 34624838Slm66018 * is maintained. 34634838Slm66018 */ 34644838Slm66018 (void) ddi_taskq_dispatch(vd->completionq, vd_serial_notify, 34654838Slm66018 &task, DDI_SLEEP); 34664838Slm66018 34674838Slm66018 /* 34684838Slm66018 * To ensure handshake negotiations do not happen out of order, such 34694838Slm66018 * requests that come through this path should not be done in parallel 34704838Slm66018 * so we need to wait here until the response is sent to the client. 34714838Slm66018 */ 34724838Slm66018 ddi_taskq_wait(vd->completionq); 34731991Sheppo 34742336Snarayan /* Arrange to reset the connection for nack'ed or failed messages */ 34752793Slm66018 if ((status != 0) || reset_ldc) { 34762793Slm66018 PR0("initiating %s reset", 34772793Slm66018 (reset_ldc) ? "full" : "soft"); 34782336Snarayan vd_need_reset(vd, reset_ldc); 34792793Slm66018 } 34802336Snarayan 34812336Snarayan return (status); 34822336Snarayan } 34832336Snarayan 34842336Snarayan static boolean_t 34852336Snarayan vd_enabled(vd_t *vd) 34862336Snarayan { 34872336Snarayan boolean_t enabled; 34882336Snarayan 34892336Snarayan mutex_enter(&vd->lock); 34902336Snarayan enabled = vd->enabled; 34912336Snarayan mutex_exit(&vd->lock); 34922336Snarayan return (enabled); 34931991Sheppo } 34941991Sheppo 34951991Sheppo static void 34962032Slm66018 vd_recv_msg(void *arg) 34971991Sheppo { 34982032Slm66018 vd_t *vd = (vd_t *)arg; 34992793Slm66018 int rv = 0, status = 0; 35001991Sheppo 35011991Sheppo ASSERT(vd != NULL); 35022793Slm66018 35032336Snarayan PR2("New task to receive incoming message(s)"); 35042793Slm66018 35052793Slm66018 35062336Snarayan while (vd_enabled(vd) && status == 0) { 35072336Snarayan size_t msglen, msgsize; 35082793Slm66018 ldc_status_t lstatus; 35092336Snarayan 35102336Snarayan /* 35112336Snarayan * Receive and process a message 35122336Snarayan */ 35132336Snarayan vd_reset_if_needed(vd); /* can change vd->max_msglen */ 35142793Slm66018 35152793Slm66018 /* 35162793Slm66018 * check if channel is UP - else break out of loop 35172793Slm66018 */ 35182793Slm66018 status = ldc_status(vd->ldc_handle, &lstatus); 35192793Slm66018 if (lstatus != LDC_UP) { 35202793Slm66018 PR0("channel not up (status=%d), exiting recv loop\n", 35212793Slm66018 lstatus); 35222793Slm66018 break; 35232793Slm66018 } 35242793Slm66018 35252793Slm66018 ASSERT(vd->max_msglen != 0); 35262793Slm66018 35272793Slm66018 msgsize = vd->max_msglen; /* stable copy for alloc/free */ 35282793Slm66018 msglen = msgsize; /* actual len after recv_msg() */ 35292793Slm66018 35302793Slm66018 status = recv_msg(vd->ldc_handle, vd->vio_msgp, &msglen); 35312793Slm66018 switch (status) { 35322793Slm66018 case 0: 35332793Slm66018 rv = vd_process_msg(vd, (vio_msg_t *)vd->vio_msgp, 35344696Sachartre msglen); 35352793Slm66018 /* check if max_msglen changed */ 35362793Slm66018 if (msgsize != vd->max_msglen) { 35372793Slm66018 PR0("max_msglen changed 0x%lx to 0x%lx bytes\n", 35382793Slm66018 msgsize, vd->max_msglen); 35392793Slm66018 kmem_free(vd->vio_msgp, msgsize); 35402793Slm66018 vd->vio_msgp = 35414696Sachartre kmem_alloc(vd->max_msglen, KM_SLEEP); 35422793Slm66018 } 35432793Slm66018 if (rv == EINPROGRESS) 35442793Slm66018 continue; 35452793Slm66018 break; 35462793Slm66018 35472793Slm66018 case ENOMSG: 35482793Slm66018 break; 35492793Slm66018 35502793Slm66018 case ECONNRESET: 35512793Slm66018 PR0("initiating soft reset (ECONNRESET)\n"); 35522793Slm66018 vd_need_reset(vd, B_FALSE); 35532793Slm66018 status = 0; 35542793Slm66018 break; 35552793Slm66018 35562793Slm66018 default: 35572336Snarayan /* Probably an LDC failure; arrange to reset it */ 35582793Slm66018 PR0("initiating full reset (status=0x%x)", status); 35592336Snarayan vd_need_reset(vd, B_TRUE); 35602793Slm66018 break; 35612336Snarayan } 35622032Slm66018 } 35632793Slm66018 35642336Snarayan PR2("Task finished"); 35652032Slm66018 } 35662032Slm66018 35672032Slm66018 static uint_t 35681991Sheppo vd_handle_ldc_events(uint64_t event, caddr_t arg) 35691991Sheppo { 35701991Sheppo vd_t *vd = (vd_t *)(void *)arg; 35712793Slm66018 int status; 35721991Sheppo 35731991Sheppo ASSERT(vd != NULL); 35742336Snarayan 35752336Snarayan if (!vd_enabled(vd)) 35762336Snarayan return (LDC_SUCCESS); 35772336Snarayan 35782793Slm66018 if (event & LDC_EVT_DOWN) { 35793166Ssg70180 PR0("LDC_EVT_DOWN: LDC channel went down"); 35802793Slm66018 35812793Slm66018 vd_need_reset(vd, B_TRUE); 35822793Slm66018 status = ddi_taskq_dispatch(vd->startq, vd_recv_msg, vd, 35832793Slm66018 DDI_SLEEP); 35842793Slm66018 if (status == DDI_FAILURE) { 35852793Slm66018 PR0("cannot schedule task to recv msg\n"); 35862793Slm66018 vd_need_reset(vd, B_TRUE); 35872793Slm66018 } 35882793Slm66018 } 35892793Slm66018 35902336Snarayan if (event & LDC_EVT_RESET) { 35912793Slm66018 PR0("LDC_EVT_RESET: LDC channel was reset"); 35922793Slm66018 35932793Slm66018 if (vd->state != VD_STATE_INIT) { 35942793Slm66018 PR0("scheduling full reset"); 35952793Slm66018 vd_need_reset(vd, B_FALSE); 35962793Slm66018 status = ddi_taskq_dispatch(vd->startq, vd_recv_msg, 35972793Slm66018 vd, DDI_SLEEP); 35982793Slm66018 if (status == DDI_FAILURE) { 35992793Slm66018 PR0("cannot schedule task to recv msg\n"); 36002793Slm66018 vd_need_reset(vd, B_TRUE); 36012793Slm66018 } 36022793Slm66018 36032793Slm66018 } else { 36042793Slm66018 PR0("channel already reset, ignoring...\n"); 36052793Slm66018 PR0("doing ldc up...\n"); 36062793Slm66018 (void) ldc_up(vd->ldc_handle); 36072793Slm66018 } 36082793Slm66018 36092336Snarayan return (LDC_SUCCESS); 36102336Snarayan } 36112336Snarayan 36122336Snarayan if (event & LDC_EVT_UP) { 36132793Slm66018 PR0("EVT_UP: LDC is up\nResetting client connection state"); 36142793Slm66018 PR0("initiating soft reset"); 36152336Snarayan vd_need_reset(vd, B_FALSE); 36162793Slm66018 status = ddi_taskq_dispatch(vd->startq, vd_recv_msg, 36172793Slm66018 vd, DDI_SLEEP); 36182793Slm66018 if (status == DDI_FAILURE) { 36192793Slm66018 PR0("cannot schedule task to recv msg\n"); 36202793Slm66018 vd_need_reset(vd, B_TRUE); 36212793Slm66018 return (LDC_SUCCESS); 36222793Slm66018 } 36232336Snarayan } 36242336Snarayan 36252336Snarayan if (event & LDC_EVT_READ) { 36262336Snarayan int status; 36272336Snarayan 36282336Snarayan PR1("New data available"); 36292336Snarayan /* Queue a task to receive the new data */ 36302336Snarayan status = ddi_taskq_dispatch(vd->startq, vd_recv_msg, vd, 36312336Snarayan DDI_SLEEP); 36322793Slm66018 36332793Slm66018 if (status == DDI_FAILURE) { 36342793Slm66018 PR0("cannot schedule task to recv msg\n"); 36352793Slm66018 vd_need_reset(vd, B_TRUE); 36362793Slm66018 } 36372336Snarayan } 36382336Snarayan 36392336Snarayan return (LDC_SUCCESS); 36401991Sheppo } 36411991Sheppo 36421991Sheppo static uint_t 36431991Sheppo vds_check_for_vd(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 36441991Sheppo { 36451991Sheppo _NOTE(ARGUNUSED(key, val)) 36461991Sheppo (*((uint_t *)arg))++; 36471991Sheppo return (MH_WALK_TERMINATE); 36481991Sheppo } 36491991Sheppo 36501991Sheppo 36511991Sheppo static int 36521991Sheppo vds_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 36531991Sheppo { 36541991Sheppo uint_t vd_present = 0; 36551991Sheppo minor_t instance; 36561991Sheppo vds_t *vds; 36571991Sheppo 36581991Sheppo 36591991Sheppo switch (cmd) { 36601991Sheppo case DDI_DETACH: 36611991Sheppo /* the real work happens below */ 36621991Sheppo break; 36631991Sheppo case DDI_SUSPEND: 36642336Snarayan PR0("No action required for DDI_SUSPEND"); 36651991Sheppo return (DDI_SUCCESS); 36661991Sheppo default: 36672793Slm66018 PR0("Unrecognized \"cmd\""); 36681991Sheppo return (DDI_FAILURE); 36691991Sheppo } 36701991Sheppo 36711991Sheppo ASSERT(cmd == DDI_DETACH); 36721991Sheppo instance = ddi_get_instance(dip); 36731991Sheppo if ((vds = ddi_get_soft_state(vds_state, instance)) == NULL) { 36742793Slm66018 PR0("Could not get state for instance %u", instance); 36751991Sheppo ddi_soft_state_free(vds_state, instance); 36761991Sheppo return (DDI_FAILURE); 36771991Sheppo } 36781991Sheppo 36791991Sheppo /* Do no detach when serving any vdisks */ 36801991Sheppo mod_hash_walk(vds->vd_table, vds_check_for_vd, &vd_present); 36811991Sheppo if (vd_present) { 36821991Sheppo PR0("Not detaching because serving vdisks"); 36831991Sheppo return (DDI_FAILURE); 36841991Sheppo } 36851991Sheppo 36861991Sheppo PR0("Detaching"); 36873297Ssb155480 if (vds->initialized & VDS_MDEG) { 36881991Sheppo (void) mdeg_unregister(vds->mdeg); 36893297Ssb155480 kmem_free(vds->ispecp->specp, sizeof (vds_prop_template)); 36903297Ssb155480 kmem_free(vds->ispecp, sizeof (mdeg_node_spec_t)); 36913297Ssb155480 vds->ispecp = NULL; 36923297Ssb155480 vds->mdeg = NULL; 36933297Ssb155480 } 36943297Ssb155480 36951991Sheppo if (vds->initialized & VDS_LDI) 36961991Sheppo (void) ldi_ident_release(vds->ldi_ident); 36971991Sheppo mod_hash_destroy_hash(vds->vd_table); 36981991Sheppo ddi_soft_state_free(vds_state, instance); 36991991Sheppo return (DDI_SUCCESS); 37001991Sheppo } 37011991Sheppo 37021991Sheppo static boolean_t 37031991Sheppo is_pseudo_device(dev_info_t *dip) 37041991Sheppo { 37051991Sheppo dev_info_t *parent, *root = ddi_root_node(); 37061991Sheppo 37071991Sheppo 37081991Sheppo for (parent = ddi_get_parent(dip); (parent != NULL) && (parent != root); 37091991Sheppo parent = ddi_get_parent(parent)) { 37101991Sheppo if (strcmp(ddi_get_name(parent), DEVI_PSEUDO_NEXNAME) == 0) 37111991Sheppo return (B_TRUE); 37121991Sheppo } 37131991Sheppo 37141991Sheppo return (B_FALSE); 37151991Sheppo } 37161991Sheppo 3717*5365Slm66018 /* 3718*5365Slm66018 * Description: 3719*5365Slm66018 * This function checks to see if the file being used as a 3720*5365Slm66018 * virtual disk is an ISO image. An ISO image is a special 3721*5365Slm66018 * case which can be booted/installed from like a CD/DVD 3722*5365Slm66018 * 3723*5365Slm66018 * Parameters: 3724*5365Slm66018 * vd - disk on which the operation is performed. 3725*5365Slm66018 * 3726*5365Slm66018 * Return Code: 3727*5365Slm66018 * B_TRUE - The file is an ISO 9660 compliant image 3728*5365Slm66018 * B_FALSE - just a regular disk image file 3729*5365Slm66018 */ 3730*5365Slm66018 static boolean_t 3731*5365Slm66018 vd_file_is_iso_image(vd_t *vd) 3732*5365Slm66018 { 3733*5365Slm66018 char iso_buf[ISO_SECTOR_SIZE]; 3734*5365Slm66018 int i, rv; 3735*5365Slm66018 uint_t sec; 3736*5365Slm66018 3737*5365Slm66018 ASSERT(vd->file); 3738*5365Slm66018 3739*5365Slm66018 /* 3740*5365Slm66018 * If we have already discovered and saved this info we can 3741*5365Slm66018 * short-circuit the check and avoid reading the file. 3742*5365Slm66018 */ 3743*5365Slm66018 if (vd->vdisk_media == VD_MEDIA_DVD || vd->vdisk_media == VD_MEDIA_CD) 3744*5365Slm66018 return (B_TRUE); 3745*5365Slm66018 3746*5365Slm66018 /* 3747*5365Slm66018 * We wish to read the sector that should contain the 2nd ISO volume 3748*5365Slm66018 * descriptor. The second field in this descriptor is called the 3749*5365Slm66018 * Standard Identifier and is set to CD001 for a CD-ROM compliant 3750*5365Slm66018 * to the ISO 9660 standard. 3751*5365Slm66018 */ 3752*5365Slm66018 sec = (ISO_VOLDESC_SEC * ISO_SECTOR_SIZE) / vd->vdisk_block_size; 3753*5365Slm66018 rv = vd_file_rw(vd, VD_SLICE_NONE, VD_OP_BREAD, (caddr_t)iso_buf, 3754*5365Slm66018 sec, ISO_SECTOR_SIZE); 3755*5365Slm66018 3756*5365Slm66018 if (rv < 0) 3757*5365Slm66018 return (B_FALSE); 3758*5365Slm66018 3759*5365Slm66018 for (i = 0; i < ISO_ID_STRLEN; i++) { 3760*5365Slm66018 if (ISO_STD_ID(iso_buf)[i] != ISO_ID_STRING[i]) 3761*5365Slm66018 return (B_FALSE); 3762*5365Slm66018 } 3763*5365Slm66018 3764*5365Slm66018 return (B_TRUE); 3765*5365Slm66018 } 3766*5365Slm66018 3767*5365Slm66018 /* 3768*5365Slm66018 * Description: 3769*5365Slm66018 * This function checks to see if the virtual device is an ATAPI 3770*5365Slm66018 * device. ATAPI devices use Group 1 Read/Write commands, so 3771*5365Slm66018 * any USCSI calls vds makes need to take this into account. 3772*5365Slm66018 * 3773*5365Slm66018 * Parameters: 3774*5365Slm66018 * vd - disk on which the operation is performed. 3775*5365Slm66018 * 3776*5365Slm66018 * Return Code: 3777*5365Slm66018 * B_TRUE - The virtual disk is backed by an ATAPI device 3778*5365Slm66018 * B_FALSE - not an ATAPI device (presumably SCSI) 3779*5365Slm66018 */ 3780*5365Slm66018 static boolean_t 3781*5365Slm66018 vd_is_atapi_device(vd_t *vd) 3782*5365Slm66018 { 3783*5365Slm66018 boolean_t is_atapi = B_FALSE; 3784*5365Slm66018 char *variantp; 3785*5365Slm66018 int rv; 3786*5365Slm66018 3787*5365Slm66018 ASSERT(vd->ldi_handle[0] != NULL); 3788*5365Slm66018 ASSERT(!vd->file); 3789*5365Slm66018 3790*5365Slm66018 rv = ldi_prop_lookup_string(vd->ldi_handle[0], 3791*5365Slm66018 (LDI_DEV_T_ANY | DDI_PROP_DONTPASS), "variant", &variantp); 3792*5365Slm66018 if (rv == DDI_PROP_SUCCESS) { 3793*5365Slm66018 PR0("'variant' property exists for %s", vd->device_path); 3794*5365Slm66018 if (strcmp(variantp, "atapi") == 0) 3795*5365Slm66018 is_atapi = B_TRUE; 3796*5365Slm66018 ddi_prop_free(variantp); 3797*5365Slm66018 } 3798*5365Slm66018 3799*5365Slm66018 rv = ldi_prop_exists(vd->ldi_handle[0], LDI_DEV_T_ANY, "atapi"); 3800*5365Slm66018 if (rv) { 3801*5365Slm66018 PR0("'atapi' property exists for %s", vd->device_path); 3802*5365Slm66018 is_atapi = B_TRUE; 3803*5365Slm66018 } 3804*5365Slm66018 3805*5365Slm66018 return (is_atapi); 3806*5365Slm66018 } 3807*5365Slm66018 38081991Sheppo static int 38092032Slm66018 vd_setup_full_disk(vd_t *vd) 38102032Slm66018 { 38112032Slm66018 int rval, status; 38122032Slm66018 major_t major = getmajor(vd->dev[0]); 38132032Slm66018 minor_t minor = getminor(vd->dev[0]) - VD_ENTIRE_DISK_SLICE; 38142531Snarayan struct dk_minfo dk_minfo; 38152531Snarayan 38165081Sachartre ASSERT(vd->vdisk_type == VD_DISK_TYPE_DISK); 38175081Sachartre 38182531Snarayan /* 38192531Snarayan * At this point, vdisk_size is set to the size of partition 2 but 38202531Snarayan * this does not represent the size of the disk because partition 2 38212531Snarayan * may not cover the entire disk and its size does not include reserved 38222531Snarayan * blocks. So we update vdisk_size to be the size of the entire disk. 38232531Snarayan */ 38242531Snarayan if ((status = ldi_ioctl(vd->ldi_handle[0], DKIOCGMEDIAINFO, 38255081Sachartre (intptr_t)&dk_minfo, (vd->open_flags | FKIOCTL), 38262531Snarayan kcred, &rval)) != 0) { 38273782Sachartre PRN("ldi_ioctl(DKIOCGMEDIAINFO) returned errno %d", 38282531Snarayan status); 38292032Slm66018 return (status); 38302032Slm66018 } 38312531Snarayan vd->vdisk_size = dk_minfo.dki_capacity; 3832*5365Slm66018 vd->block_size = dk_minfo.dki_lbsize; 3833*5365Slm66018 vd->vdisk_media = DK_MEDIATYPE2VD_MEDIATYPE(dk_minfo.dki_media_type); 3834*5365Slm66018 vd->vdisk_block_size = DEV_BSIZE; 38352032Slm66018 38362032Slm66018 /* Move dev number and LDI handle to entire-disk-slice array elements */ 38372032Slm66018 vd->dev[VD_ENTIRE_DISK_SLICE] = vd->dev[0]; 38382032Slm66018 vd->dev[0] = 0; 38392032Slm66018 vd->ldi_handle[VD_ENTIRE_DISK_SLICE] = vd->ldi_handle[0]; 38402032Slm66018 vd->ldi_handle[0] = NULL; 38412032Slm66018 38422032Slm66018 /* Initialize device numbers for remaining slices and open them */ 38432032Slm66018 for (int slice = 0; slice < vd->nslices; slice++) { 38442032Slm66018 /* 38452032Slm66018 * Skip the entire-disk slice, as it's already open and its 38462032Slm66018 * device known 38472032Slm66018 */ 38482032Slm66018 if (slice == VD_ENTIRE_DISK_SLICE) 38492032Slm66018 continue; 38502032Slm66018 ASSERT(vd->dev[slice] == 0); 38512032Slm66018 ASSERT(vd->ldi_handle[slice] == NULL); 38522032Slm66018 38532032Slm66018 /* 38542032Slm66018 * Construct the device number for the current slice 38552032Slm66018 */ 38562032Slm66018 vd->dev[slice] = makedevice(major, (minor + slice)); 38572032Slm66018 38582032Slm66018 /* 38593166Ssg70180 * Open all slices of the disk to serve them to the client. 38603166Ssg70180 * Slices are opened exclusively to prevent other threads or 38613166Ssg70180 * processes in the service domain from performing I/O to 38623166Ssg70180 * slices being accessed by a client. Failure to open a slice 38633166Ssg70180 * results in vds not serving this disk, as the client could 38643166Ssg70180 * attempt (and should be able) to access any slice immediately. 38653166Ssg70180 * Any slices successfully opened before a failure will get 38663166Ssg70180 * closed by vds_destroy_vd() as a result of the error returned 38673166Ssg70180 * by this function. 38683166Ssg70180 * 38693166Ssg70180 * We need to do the open with FNDELAY so that opening an empty 38703166Ssg70180 * slice does not fail. 38712032Slm66018 */ 38722032Slm66018 PR0("Opening device major %u, minor %u = slice %u", 38732032Slm66018 major, minor, slice); 38745081Sachartre 38755081Sachartre /* 38765081Sachartre * Try to open the device. This can fail for example if we are 38775081Sachartre * opening an empty slice. So in case of a failure, we try the 38785081Sachartre * open again but this time with the FNDELAY flag. 38795081Sachartre */ 38805081Sachartre status = ldi_open_by_dev(&vd->dev[slice], OTYP_BLK, 38815081Sachartre vd->open_flags, kcred, &vd->ldi_handle[slice], 38825081Sachartre vd->vds->ldi_ident); 38835081Sachartre 38845081Sachartre if (status != 0) { 38855081Sachartre status = ldi_open_by_dev(&vd->dev[slice], OTYP_BLK, 38865081Sachartre vd->open_flags | FNDELAY, kcred, 38875081Sachartre &vd->ldi_handle[slice], vd->vds->ldi_ident); 38885081Sachartre } 38895081Sachartre 38905081Sachartre if (status != 0) { 38913782Sachartre PRN("ldi_open_by_dev() returned errno %d " 38922032Slm66018 "for slice %u", status, slice); 38932032Slm66018 /* vds_destroy_vd() will close any open slices */ 38943782Sachartre vd->ldi_handle[slice] = NULL; 38952032Slm66018 return (status); 38962032Slm66018 } 38972032Slm66018 } 38982032Slm66018 38992032Slm66018 return (0); 39002032Slm66018 } 39012032Slm66018 39022032Slm66018 static int 39034963Sachartre vd_setup_partition_vtoc(vd_t *vd) 39044963Sachartre { 39054963Sachartre int rval, status; 39064963Sachartre char *device_path = vd->device_path; 39074963Sachartre 39084963Sachartre status = ldi_ioctl(vd->ldi_handle[0], DKIOCGGEOM, 39095081Sachartre (intptr_t)&vd->dk_geom, (vd->open_flags | FKIOCTL), kcred, &rval); 39104963Sachartre 39114963Sachartre if (status != 0) { 39124963Sachartre PRN("ldi_ioctl(DKIOCGEOM) returned errno %d for %s", 39134963Sachartre status, device_path); 39144963Sachartre return (status); 39154963Sachartre } 39164963Sachartre 39174963Sachartre /* Initialize dk_geom structure for single-slice device */ 39184963Sachartre if (vd->dk_geom.dkg_nsect == 0) { 39194963Sachartre PRN("%s geometry claims 0 sectors per track", device_path); 39204963Sachartre return (EIO); 39214963Sachartre } 39224963Sachartre if (vd->dk_geom.dkg_nhead == 0) { 39234963Sachartre PRN("%s geometry claims 0 heads", device_path); 39244963Sachartre return (EIO); 39254963Sachartre } 39264963Sachartre vd->dk_geom.dkg_ncyl = vd->vdisk_size / vd->dk_geom.dkg_nsect / 39274963Sachartre vd->dk_geom.dkg_nhead; 39284963Sachartre vd->dk_geom.dkg_acyl = 0; 39294963Sachartre vd->dk_geom.dkg_pcyl = vd->dk_geom.dkg_ncyl + vd->dk_geom.dkg_acyl; 39304963Sachartre 39314963Sachartre 39324963Sachartre /* Initialize vtoc structure for single-slice device */ 39334963Sachartre bcopy(VD_VOLUME_NAME, vd->vtoc.v_volume, 39344963Sachartre MIN(sizeof (VD_VOLUME_NAME), sizeof (vd->vtoc.v_volume))); 39354963Sachartre bzero(vd->vtoc.v_part, sizeof (vd->vtoc.v_part)); 39364963Sachartre vd->vtoc.v_nparts = 1; 39374963Sachartre vd->vtoc.v_part[0].p_tag = V_UNASSIGNED; 39384963Sachartre vd->vtoc.v_part[0].p_flag = 0; 39394963Sachartre vd->vtoc.v_part[0].p_start = 0; 39404963Sachartre vd->vtoc.v_part[0].p_size = vd->vdisk_size; 39414963Sachartre bcopy(VD_ASCIILABEL, vd->vtoc.v_asciilabel, 39424963Sachartre MIN(sizeof (VD_ASCIILABEL), sizeof (vd->vtoc.v_asciilabel))); 39434963Sachartre 39444963Sachartre return (0); 39454963Sachartre } 39464963Sachartre 39474963Sachartre static int 39482531Snarayan vd_setup_partition_efi(vd_t *vd) 39492531Snarayan { 39502531Snarayan efi_gpt_t *gpt; 39512531Snarayan efi_gpe_t *gpe; 39522531Snarayan struct uuid uuid = EFI_RESERVED; 39532531Snarayan uint32_t crc; 39542531Snarayan int length; 39552531Snarayan 39562531Snarayan length = sizeof (efi_gpt_t) + sizeof (efi_gpe_t); 39572531Snarayan 39582531Snarayan gpt = kmem_zalloc(length, KM_SLEEP); 39592531Snarayan gpe = (efi_gpe_t *)(gpt + 1); 39602531Snarayan 39612531Snarayan gpt->efi_gpt_Signature = LE_64(EFI_SIGNATURE); 39622531Snarayan gpt->efi_gpt_Revision = LE_32(EFI_VERSION_CURRENT); 39632531Snarayan gpt->efi_gpt_HeaderSize = LE_32(sizeof (efi_gpt_t)); 39642531Snarayan gpt->efi_gpt_FirstUsableLBA = LE_64(0ULL); 39652531Snarayan gpt->efi_gpt_LastUsableLBA = LE_64(vd->vdisk_size - 1); 39662531Snarayan gpt->efi_gpt_NumberOfPartitionEntries = LE_32(1); 39672531Snarayan gpt->efi_gpt_SizeOfPartitionEntry = LE_32(sizeof (efi_gpe_t)); 39682531Snarayan 39692531Snarayan UUID_LE_CONVERT(gpe->efi_gpe_PartitionTypeGUID, uuid); 39702531Snarayan gpe->efi_gpe_StartingLBA = gpt->efi_gpt_FirstUsableLBA; 39712531Snarayan gpe->efi_gpe_EndingLBA = gpt->efi_gpt_LastUsableLBA; 39722531Snarayan 39732531Snarayan CRC32(crc, gpe, sizeof (efi_gpe_t), -1U, crc32_table); 39742531Snarayan gpt->efi_gpt_PartitionEntryArrayCRC32 = LE_32(~crc); 39752531Snarayan 39762531Snarayan CRC32(crc, gpt, sizeof (efi_gpt_t), -1U, crc32_table); 39772531Snarayan gpt->efi_gpt_HeaderCRC32 = LE_32(~crc); 39782531Snarayan 39792531Snarayan vd->dk_efi.dki_lba = 0; 39802531Snarayan vd->dk_efi.dki_length = length; 39812531Snarayan vd->dk_efi.dki_data = gpt; 39822531Snarayan 39832531Snarayan return (0); 39842531Snarayan } 39852531Snarayan 39865081Sachartre /* 39875081Sachartre * Setup for a virtual disk whose backend is a file (exported as a single slice 39885081Sachartre * or as a full disk) or a pseudo device (for example a ZFS, SVM or VxVM volume) 39895081Sachartre * exported as a full disk. In these cases, the backend is accessed using the 39905081Sachartre * vnode interface. 39915081Sachartre */ 39922531Snarayan static int 39935081Sachartre vd_setup_backend_vnode(vd_t *vd) 39943401Snarayan { 39954963Sachartre int rval, status; 39963401Snarayan vattr_t vattr; 39973401Snarayan dev_t dev; 39983401Snarayan char *file_path = vd->device_path; 39993401Snarayan char dev_path[MAXPATHLEN + 1]; 40003401Snarayan ldi_handle_t lhandle; 40013401Snarayan struct dk_cinfo dk_cinfo; 40023401Snarayan 40035081Sachartre if ((status = vn_open(file_path, UIO_SYSSPACE, vd->open_flags | FOFFMAX, 40043401Snarayan 0, &vd->file_vnode, 0, 0)) != 0) { 40053782Sachartre PRN("vn_open(%s) = errno %d", file_path, status); 40063401Snarayan return (status); 40073401Snarayan } 40083401Snarayan 40093782Sachartre /* 40103782Sachartre * We set vd->file now so that vds_destroy_vd will take care of 40113782Sachartre * closing the file and releasing the vnode in case of an error. 40123782Sachartre */ 40133782Sachartre vd->file = B_TRUE; 40143782Sachartre 40153401Snarayan vattr.va_mask = AT_SIZE; 40165331Samw if ((status = VOP_GETATTR(vd->file_vnode, &vattr, 0, kcred, NULL)) 40175331Samw != 0) { 40183782Sachartre PRN("VOP_GETATTR(%s) = errno %d", file_path, status); 40193401Snarayan return (EIO); 40203401Snarayan } 40213401Snarayan 40223401Snarayan vd->file_size = vattr.va_size; 40233401Snarayan /* size should be at least sizeof(dk_label) */ 40243401Snarayan if (vd->file_size < sizeof (struct dk_label)) { 40253401Snarayan PRN("Size of file has to be at least %ld bytes", 40263401Snarayan sizeof (struct dk_label)); 40273401Snarayan return (EIO); 40283401Snarayan } 40293401Snarayan 40303782Sachartre if (vd->file_vnode->v_flag & VNOMAP) { 40313782Sachartre PRN("File %s cannot be mapped", file_path); 40323401Snarayan return (EIO); 40333401Snarayan } 40343401Snarayan 40355081Sachartre /* 40365081Sachartre * Find and validate the geometry of a disk image. For a single slice 40375081Sachartre * disk image, this will build a fake geometry and vtoc. 40385081Sachartre */ 40394963Sachartre status = vd_file_validate_geometry(vd); 40404963Sachartre if (status != 0 && status != EINVAL) { 4041*5365Slm66018 PRN("Failed to read label from %s", file_path); 40423782Sachartre return (EIO); 40433782Sachartre } 40443401Snarayan 40453401Snarayan /* sector size = block size = DEV_BSIZE */ 4046*5365Slm66018 vd->block_size = DEV_BSIZE; 4047*5365Slm66018 vd->vdisk_block_size = DEV_BSIZE; 40484696Sachartre vd->vdisk_size = vd->file_size / DEV_BSIZE; 40493401Snarayan vd->max_xfer_sz = maxphys / DEV_BSIZE; /* default transfer size */ 40503401Snarayan 4051*5365Slm66018 if (vd_file_is_iso_image(vd)) { 4052*5365Slm66018 /* 4053*5365Slm66018 * Indicate whether to call this a CD or DVD from the size 4054*5365Slm66018 * of the ISO image (images for both drive types are stored 4055*5365Slm66018 * in the ISO-9600 format). CDs can store up to just under 1Gb 4056*5365Slm66018 */ 4057*5365Slm66018 if ((vd->vdisk_size * vd->vdisk_block_size) > 4058*5365Slm66018 (1024 * 1024 * 1024)) 4059*5365Slm66018 vd->vdisk_media = VD_MEDIA_DVD; 4060*5365Slm66018 else 4061*5365Slm66018 vd->vdisk_media = VD_MEDIA_CD; 4062*5365Slm66018 } else { 4063*5365Slm66018 vd->vdisk_media = VD_MEDIA_FIXED; 4064*5365Slm66018 } 4065*5365Slm66018 40665081Sachartre /* 40675081Sachartre * Get max_xfer_sz from the device where the file is or from the device 40685081Sachartre * itself if we have a pseudo device. 40695081Sachartre */ 40705081Sachartre dev_path[0] = '\0'; 40715081Sachartre 40725081Sachartre if (vd->pseudo) { 40735081Sachartre status = ldi_open_by_name(file_path, FREAD, kcred, &lhandle, 40745081Sachartre vd->vds->ldi_ident); 40755081Sachartre } else { 40765081Sachartre dev = vd->file_vnode->v_vfsp->vfs_dev; 40775081Sachartre if (ddi_dev_pathname(dev, S_IFBLK, dev_path) == DDI_SUCCESS) { 40785081Sachartre PR0("underlying device = %s\n", dev_path); 40795081Sachartre } 40805081Sachartre 40815081Sachartre status = ldi_open_by_dev(&dev, OTYP_BLK, FREAD, kcred, &lhandle, 40825081Sachartre vd->vds->ldi_ident); 40833401Snarayan } 40843401Snarayan 40855081Sachartre if (status != 0) { 40865081Sachartre PR0("ldi_open() returned errno %d for device %s", 40875081Sachartre status, (dev_path[0] == '\0')? file_path : dev_path); 40883401Snarayan } else { 40893401Snarayan if ((status = ldi_ioctl(lhandle, DKIOCINFO, 40905081Sachartre (intptr_t)&dk_cinfo, (vd->open_flags | FKIOCTL), kcred, 40913401Snarayan &rval)) != 0) { 40923401Snarayan PR0("ldi_ioctl(DKIOCINFO) returned errno %d for %s", 40933401Snarayan status, dev_path); 40943401Snarayan } else { 40953401Snarayan /* 40963401Snarayan * Store the device's max transfer size for 40973401Snarayan * return to the client 40983401Snarayan */ 40993401Snarayan vd->max_xfer_sz = dk_cinfo.dki_maxtransfer; 41003401Snarayan } 41013401Snarayan 41023401Snarayan PR0("close the device %s", dev_path); 41033401Snarayan (void) ldi_close(lhandle, FREAD, kcred); 41043401Snarayan } 41053401Snarayan 41064838Slm66018 PR0("using file %s, dev %s, max_xfer = %u blks", 41073401Snarayan file_path, dev_path, vd->max_xfer_sz); 41083401Snarayan 41094696Sachartre /* Setup devid for the disk image */ 41104696Sachartre 41115081Sachartre if (vd->vdisk_type == VD_DISK_TYPE_SLICE) 41125081Sachartre return (0); 41135081Sachartre 41144963Sachartre if (vd->vdisk_label != VD_DISK_LABEL_UNK) { 41154963Sachartre 41164963Sachartre status = vd_file_read_devid(vd, &vd->file_devid); 41174963Sachartre 41184963Sachartre if (status == 0) { 41194963Sachartre /* a valid devid was found */ 41204963Sachartre return (0); 41214963Sachartre } 41224963Sachartre 41234963Sachartre if (status != EINVAL) { 41244963Sachartre /* 41254963Sachartre * There was an error while trying to read the devid. 41264963Sachartre * So this disk image may have a devid but we are 41274963Sachartre * unable to read it. 41284963Sachartre */ 41294963Sachartre PR0("can not read devid for %s", file_path); 41304963Sachartre vd->file_devid = NULL; 41314963Sachartre return (0); 41324963Sachartre } 41334696Sachartre } 41344696Sachartre 41354696Sachartre /* 41364696Sachartre * No valid device id was found so we create one. Note that a failure 41374696Sachartre * to create a device id is not fatal and does not prevent the disk 41384696Sachartre * image from being attached. 41394696Sachartre */ 41404696Sachartre PR1("creating devid for %s", file_path); 41414696Sachartre 41424696Sachartre if (ddi_devid_init(vd->vds->dip, DEVID_FAB, NULL, 0, 41434696Sachartre &vd->file_devid) != DDI_SUCCESS) { 41444696Sachartre PR0("fail to create devid for %s", file_path); 41454696Sachartre vd->file_devid = NULL; 41464696Sachartre return (0); 41474696Sachartre } 41484696Sachartre 41494963Sachartre /* 41504963Sachartre * Write devid to the disk image. The devid is stored into the disk 41514963Sachartre * image if we have a valid label; otherwise the devid will be stored 41524963Sachartre * when the user writes a valid label. 41534963Sachartre */ 41544963Sachartre if (vd->vdisk_label != VD_DISK_LABEL_UNK) { 41554963Sachartre if (vd_file_write_devid(vd, vd->file_devid) != 0) { 41564963Sachartre PR0("fail to write devid for %s", file_path); 41574963Sachartre ddi_devid_free(vd->file_devid); 41584963Sachartre vd->file_devid = NULL; 41594963Sachartre } 41604696Sachartre } 41614696Sachartre 41623401Snarayan return (0); 41633401Snarayan } 41643401Snarayan 4165*5365Slm66018 4166*5365Slm66018 /* 4167*5365Slm66018 * Description: 4168*5365Slm66018 * Open a device using its device path (supplied by ldm(1m)) 4169*5365Slm66018 * 4170*5365Slm66018 * Parameters: 4171*5365Slm66018 * vd - pointer to structure containing the vDisk info 4172*5365Slm66018 * 4173*5365Slm66018 * Return Value 4174*5365Slm66018 * 0 - success 4175*5365Slm66018 * EIO - Invalid number of partitions 4176*5365Slm66018 * != 0 - some other non-zero return value from ldi(9F) functions 4177*5365Slm66018 */ 4178*5365Slm66018 static int 4179*5365Slm66018 vd_open_using_ldi_by_name(vd_t *vd) 4180*5365Slm66018 { 4181*5365Slm66018 int rval, status, open_flags; 4182*5365Slm66018 struct dk_cinfo dk_cinfo; 4183*5365Slm66018 char *device_path = vd->device_path; 4184*5365Slm66018 4185*5365Slm66018 /* 4186*5365Slm66018 * Try to open the device. If the flags indicate that the device should 4187*5365Slm66018 * be opened write-enabled, we first we try to open it "read-only" 4188*5365Slm66018 * to see if we have an optical device such as a CD-ROM which, for 4189*5365Slm66018 * now, we do not permit writes to and thus should not export write 4190*5365Slm66018 * operations to the client. 4191*5365Slm66018 * 4192*5365Slm66018 * Future: if/when we implement support for guest domains writing to 4193*5365Slm66018 * optical devices we will need to do further checking of the media type 4194*5365Slm66018 * to distinguish between read-only and writable discs. 4195*5365Slm66018 */ 4196*5365Slm66018 if (vd->open_flags & FWRITE) { 4197*5365Slm66018 open_flags = vd->open_flags & ~FWRITE; 4198*5365Slm66018 status = ldi_open_by_name(device_path, open_flags, kcred, 4199*5365Slm66018 &vd->ldi_handle[0], vd->vds->ldi_ident); 4200*5365Slm66018 4201*5365Slm66018 if (status == 0) { 4202*5365Slm66018 /* Verify backing device supports dk_cinfo */ 4203*5365Slm66018 status = ldi_ioctl(vd->ldi_handle[0], DKIOCINFO, 4204*5365Slm66018 (intptr_t)&dk_cinfo, (open_flags | FKIOCTL), 4205*5365Slm66018 kcred, &rval); 4206*5365Slm66018 if (status != 0) { 4207*5365Slm66018 PRN("ldi_ioctl(DKIOCINFO) returned errno %d for" 4208*5365Slm66018 " %s opened as RO", status, device_path); 4209*5365Slm66018 return (status); 4210*5365Slm66018 } 4211*5365Slm66018 4212*5365Slm66018 if (dk_cinfo.dki_partition >= V_NUMPAR) { 4213*5365Slm66018 PRN("slice %u >= maximum slice %u for %s", 4214*5365Slm66018 dk_cinfo.dki_partition, V_NUMPAR, 4215*5365Slm66018 device_path); 4216*5365Slm66018 return (EIO); 4217*5365Slm66018 } 4218*5365Slm66018 4219*5365Slm66018 /* 4220*5365Slm66018 * If this is an optical device then we disable 4221*5365Slm66018 * write access and return, otherwise we close 4222*5365Slm66018 * the device and try again with writes enabled. 4223*5365Slm66018 */ 4224*5365Slm66018 if (dk_cinfo.dki_ctype == DKC_CDROM) { 4225*5365Slm66018 vd->open_flags = open_flags; 4226*5365Slm66018 return (0); 4227*5365Slm66018 } else { 4228*5365Slm66018 (void) ldi_close(vd->ldi_handle[0], 4229*5365Slm66018 open_flags, kcred); 4230*5365Slm66018 } 4231*5365Slm66018 } 4232*5365Slm66018 } 4233*5365Slm66018 4234*5365Slm66018 /* Attempt to (re)open device */ 4235*5365Slm66018 status = ldi_open_by_name(device_path, open_flags, kcred, 4236*5365Slm66018 &vd->ldi_handle[0], vd->vds->ldi_ident); 4237*5365Slm66018 4238*5365Slm66018 /* 4239*5365Slm66018 * The open can fail for example if we are opening an empty slice. 4240*5365Slm66018 * In case of a failure, we try the open again but this time with 4241*5365Slm66018 * the FNDELAY flag. 4242*5365Slm66018 */ 4243*5365Slm66018 if (status != 0) 4244*5365Slm66018 status = ldi_open_by_name(device_path, vd->open_flags | FNDELAY, 4245*5365Slm66018 kcred, &vd->ldi_handle[0], vd->vds->ldi_ident); 4246*5365Slm66018 4247*5365Slm66018 if (status != 0) { 4248*5365Slm66018 PR0("ldi_open_by_name(%s) = errno %d", device_path, status); 4249*5365Slm66018 vd->ldi_handle[0] = NULL; 4250*5365Slm66018 return (status); 4251*5365Slm66018 } 4252*5365Slm66018 4253*5365Slm66018 /* Verify backing device supports dk_cinfo */ 4254*5365Slm66018 if ((status = ldi_ioctl(vd->ldi_handle[0], DKIOCINFO, 4255*5365Slm66018 (intptr_t)&dk_cinfo, (vd->open_flags | FKIOCTL), kcred, 4256*5365Slm66018 &rval)) != 0) { 4257*5365Slm66018 PRN("ldi_ioctl(DKIOCINFO) returned errno %d for %s", 4258*5365Slm66018 status, device_path); 4259*5365Slm66018 return (status); 4260*5365Slm66018 } 4261*5365Slm66018 if (dk_cinfo.dki_partition >= V_NUMPAR) { 4262*5365Slm66018 PRN("slice %u >= maximum slice %u for %s", 4263*5365Slm66018 dk_cinfo.dki_partition, V_NUMPAR, device_path); 4264*5365Slm66018 return (EIO); 4265*5365Slm66018 } 4266*5365Slm66018 4267*5365Slm66018 return (0); 4268*5365Slm66018 } 4269*5365Slm66018 4270*5365Slm66018 42715081Sachartre /* 42725081Sachartre * Setup for a virtual disk which backend is a device (a physical disk, 42735081Sachartre * slice or pseudo device) that is directly exported either as a full disk 42745081Sachartre * for a physical disk or as a slice for a pseudo device or a disk slice. 42755081Sachartre * In these cases, the backend is accessed using the LDI interface. 42765081Sachartre */ 42773401Snarayan static int 42785081Sachartre vd_setup_backend_ldi(vd_t *vd) 42791991Sheppo { 42802410Slm66018 int rval, status; 42811991Sheppo struct dk_cinfo dk_cinfo; 42823401Snarayan char *device_path = vd->device_path; 42831991Sheppo 4284*5365Slm66018 status = vd_open_using_ldi_by_name(vd); 42855081Sachartre if (status != 0) { 4286*5365Slm66018 PR0("Failed to open (%s) = errno %d", device_path, status); 42872032Slm66018 return (status); 42882032Slm66018 } 42892032Slm66018 42903401Snarayan vd->file = B_FALSE; 42912531Snarayan 42925081Sachartre /* Get device number of backing device */ 42932032Slm66018 if ((status = ldi_get_dev(vd->ldi_handle[0], &vd->dev[0])) != 0) { 42941991Sheppo PRN("ldi_get_dev() returned errno %d for %s", 42952410Slm66018 status, device_path); 42961991Sheppo return (status); 42971991Sheppo } 42982410Slm66018 42994963Sachartre /* Verify backing device supports dk_cinfo */ 43002410Slm66018 if ((status = ldi_ioctl(vd->ldi_handle[0], DKIOCINFO, 43015081Sachartre (intptr_t)&dk_cinfo, (vd->open_flags | FKIOCTL), kcred, 43024696Sachartre &rval)) != 0) { 43032410Slm66018 PRN("ldi_ioctl(DKIOCINFO) returned errno %d for %s", 43042410Slm66018 status, device_path); 43051991Sheppo return (status); 43061991Sheppo } 43072410Slm66018 if (dk_cinfo.dki_partition >= V_NUMPAR) { 43082410Slm66018 PRN("slice %u >= maximum slice %u for %s", 43092410Slm66018 dk_cinfo.dki_partition, V_NUMPAR, device_path); 43101991Sheppo return (EIO); 43111991Sheppo } 43122531Snarayan 43135081Sachartre vd->vdisk_label = vd_read_vtoc(vd, &vd->vtoc); 43142410Slm66018 43152410Slm66018 /* Store the device's max transfer size for return to the client */ 43162410Slm66018 vd->max_xfer_sz = dk_cinfo.dki_maxtransfer; 43172410Slm66018 43185081Sachartre /* 4319*5365Slm66018 * We need to work out if it's an ATAPI (IDE CD-ROM) or SCSI device so 4320*5365Slm66018 * that we can use the correct CDB group when sending USCSI commands. 4321*5365Slm66018 */ 4322*5365Slm66018 vd->is_atapi_dev = vd_is_atapi_device(vd); 4323*5365Slm66018 4324*5365Slm66018 /* 43255081Sachartre * Export a full disk. 43265081Sachartre * 43275081Sachartre * When we use the LDI interface, we export a device as a full disk 43285081Sachartre * if we have an entire disk slice (slice 2) and if this slice is 43295081Sachartre * exported as a full disk and not as a single slice disk. 4330*5365Slm66018 * Similarly, we want to use LDI if we are accessing a CD or DVD 4331*5365Slm66018 * device (even if it isn't s2) 43325081Sachartre * 43335081Sachartre * Note that pseudo devices are exported as full disks using the vnode 43345081Sachartre * interface, not the LDI interface. 43355081Sachartre */ 4336*5365Slm66018 if ((dk_cinfo.dki_partition == VD_ENTIRE_DISK_SLICE && 4337*5365Slm66018 vd->vdisk_type == VD_DISK_TYPE_DISK) || 4338*5365Slm66018 dk_cinfo.dki_ctype == DKC_CDROM) { 43395081Sachartre ASSERT(!vd->pseudo); 43405081Sachartre return (vd_setup_full_disk(vd)); 43415081Sachartre } 43425081Sachartre 43435081Sachartre /* 43445081Sachartre * Export a single slice disk. 43455081Sachartre * 43465081Sachartre * The exported device can be either a pseudo device or a disk slice. If 43475081Sachartre * it is a disk slice different from slice 2 then it is always exported 43485081Sachartre * as a single slice disk even if the "slice" option is not specified. 43495081Sachartre * If it is disk slice 2 or a pseudo device then it is exported as a 43505081Sachartre * single slice disk only if the "slice" option is specified. 43515081Sachartre */ 43525081Sachartre ASSERT(vd->vdisk_type == VD_DISK_TYPE_SLICE || 43535081Sachartre dk_cinfo.dki_partition == VD_ENTIRE_DISK_SLICE); 43545081Sachartre return (vd_setup_single_slice_disk(vd)); 43555081Sachartre } 43565081Sachartre 43575081Sachartre static int 43585081Sachartre vd_setup_single_slice_disk(vd_t *vd) 43595081Sachartre { 43605081Sachartre int status; 43615081Sachartre char *device_path = vd->device_path; 43625081Sachartre 43635081Sachartre /* Get size of backing device */ 43645081Sachartre if (ldi_get_size(vd->ldi_handle[0], &vd->vdisk_size) != DDI_SUCCESS) { 43655081Sachartre PRN("ldi_get_size() failed for %s", device_path); 43661991Sheppo return (EIO); 43671991Sheppo } 43685081Sachartre vd->vdisk_size = lbtodb(vd->vdisk_size); /* convert to blocks */ 4369*5365Slm66018 vd->block_size = DEV_BSIZE; 4370*5365Slm66018 vd->vdisk_block_size = DEV_BSIZE; 4371*5365Slm66018 vd->vdisk_media = VD_MEDIA_FIXED; 43725081Sachartre 43731991Sheppo if (vd->pseudo) { 43745081Sachartre 43755081Sachartre ASSERT(vd->vdisk_type == VD_DISK_TYPE_SLICE); 43765081Sachartre 43774963Sachartre /* 43784963Sachartre * Currently we only support exporting pseudo devices which 43794963Sachartre * provide a valid disk label. 43804963Sachartre */ 43814963Sachartre if (vd->vdisk_label == VD_DISK_LABEL_UNK) { 43824963Sachartre PRN("%s is a pseudo device with an invalid disk " 43834963Sachartre "label\n", device_path); 43844963Sachartre return (EINVAL); 43854963Sachartre } 43861991Sheppo return (0); /* ...and we're done */ 43871991Sheppo } 43881991Sheppo 43894963Sachartre /* We can only export a slice if the disk has a valid label */ 43904963Sachartre if (vd->vdisk_label == VD_DISK_LABEL_UNK) { 43914963Sachartre PRN("%s is a slice from a disk with an unknown disk label\n", 43924963Sachartre device_path); 43934963Sachartre return (EINVAL); 43944963Sachartre } 43952032Slm66018 43965081Sachartre /* 43975081Sachartre * We export the slice as a single slice disk even if the "slice" 43985081Sachartre * option was not specified. 43995081Sachartre */ 44001991Sheppo vd->vdisk_type = VD_DISK_TYPE_SLICE; 44011991Sheppo vd->nslices = 1; 44021991Sheppo 44032531Snarayan if (vd->vdisk_label == VD_DISK_LABEL_EFI) { 44044963Sachartre /* Slice from a disk with an EFI label */ 44052531Snarayan status = vd_setup_partition_efi(vd); 44064963Sachartre } else { 44074963Sachartre /* Slice from a disk with a VTOC label */ 44084963Sachartre ASSERT(vd->vdisk_label == VD_DISK_LABEL_VTOC); 44094963Sachartre status = vd_setup_partition_vtoc(vd); 44101991Sheppo } 44114963Sachartre 44124963Sachartre return (status); 44131991Sheppo } 44141991Sheppo 44151991Sheppo static int 44165081Sachartre vd_setup_vd(vd_t *vd) 44175081Sachartre { 44185081Sachartre int status; 44195081Sachartre dev_info_t *dip; 44205081Sachartre vnode_t *vnp; 44215081Sachartre char *path = vd->device_path; 44225081Sachartre 44235081Sachartre /* make sure the vdisk backend is valid */ 44245081Sachartre if ((status = lookupname(path, UIO_SYSSPACE, 44255081Sachartre FOLLOW, NULLVPP, &vnp)) != 0) { 44265081Sachartre PR0("Cannot lookup %s errno %d", path, status); 44275081Sachartre goto done; 44285081Sachartre } 44295081Sachartre 44305081Sachartre switch (vnp->v_type) { 44315081Sachartre case VREG: 44325081Sachartre /* 44335081Sachartre * Backend is a file so it is exported as a full disk or as a 44345081Sachartre * single slice disk using the vnode interface. 44355081Sachartre */ 44365081Sachartre VN_RELE(vnp); 44375081Sachartre vd->pseudo = B_FALSE; 44385081Sachartre status = vd_setup_backend_vnode(vd); 44395081Sachartre break; 44405081Sachartre 44415081Sachartre case VBLK: 44425081Sachartre case VCHR: 44435081Sachartre /* 44445081Sachartre * Backend is a device. The way it is exported depends on the 44455081Sachartre * type of the device. 44465081Sachartre * 44475081Sachartre * - A pseudo device is exported as a full disk using the vnode 44485081Sachartre * interface or as a single slice disk using the LDI 44495081Sachartre * interface. 44505081Sachartre * 44515081Sachartre * - A disk (represented by the slice 2 of that disk) is 44525081Sachartre * exported as a full disk using the LDI interface. 44535081Sachartre * 44545081Sachartre * - A disk slice (different from slice 2) is always exported 44555081Sachartre * as a single slice disk using the LDI interface. 44565081Sachartre * 44575081Sachartre * - The slice 2 of a disk is exported as a single slice disk 44585081Sachartre * if the "slice" option is specified, otherwise the entire 44595081Sachartre * disk will be exported. In any case, the LDI interface is 44605081Sachartre * used. 44615081Sachartre */ 44625081Sachartre 44635081Sachartre /* check if this is a pseudo device */ 44645081Sachartre if ((dip = ddi_hold_devi_by_instance(getmajor(vnp->v_rdev), 44655081Sachartre dev_to_instance(vnp->v_rdev), 0)) == NULL) { 44665081Sachartre PRN("%s is no longer accessible", path); 44675081Sachartre VN_RELE(vnp); 44685081Sachartre status = EIO; 44695081Sachartre break; 44705081Sachartre } 44715081Sachartre vd->pseudo = is_pseudo_device(dip); 44725081Sachartre ddi_release_devi(dip); 44735081Sachartre VN_RELE(vnp); 44745081Sachartre 44755081Sachartre /* 44765081Sachartre * If this is a pseudo device then its usage depends if the 44775081Sachartre * "slice" option is set or not. If the "slice" option is set 44785081Sachartre * then the pseudo device will be exported as a single slice, 44795081Sachartre * otherwise it will be exported as a full disk. 44805081Sachartre */ 44815081Sachartre if (vd->pseudo && vd->vdisk_type == VD_DISK_TYPE_DISK) 44825081Sachartre status = vd_setup_backend_vnode(vd); 44835081Sachartre else 44845081Sachartre status = vd_setup_backend_ldi(vd); 44855081Sachartre break; 44865081Sachartre 44875081Sachartre default: 44885081Sachartre PRN("Unsupported vdisk backend %s", path); 44895081Sachartre VN_RELE(vnp); 44905081Sachartre status = EBADF; 44915081Sachartre } 44925081Sachartre 44935081Sachartre done: 44945081Sachartre if (status != 0) { 44955081Sachartre /* 44965081Sachartre * If the error is retryable print an error message only 44975081Sachartre * during the first try. 44985081Sachartre */ 44995081Sachartre if (status == ENXIO || status == ENODEV || 45005081Sachartre status == ENOENT || status == EROFS) { 45015081Sachartre if (!(vd->initialized & VD_SETUP_ERROR)) { 45025081Sachartre PRN("%s is currently inaccessible (error %d)", 45035081Sachartre path, status); 45045081Sachartre } 45055081Sachartre status = EAGAIN; 45065081Sachartre } else { 45075081Sachartre PRN("%s can not be exported as a virtual disk " 45085081Sachartre "(error %d)", path, status); 45095081Sachartre } 45105081Sachartre vd->initialized |= VD_SETUP_ERROR; 45115081Sachartre 45125081Sachartre } else if (vd->initialized & VD_SETUP_ERROR) { 45135081Sachartre /* print a message only if we previously had an error */ 45145081Sachartre PRN("%s is now online", path); 45155081Sachartre vd->initialized &= ~VD_SETUP_ERROR; 45165081Sachartre } 45175081Sachartre 45185081Sachartre return (status); 45195081Sachartre } 45205081Sachartre 45215081Sachartre static int 45225081Sachartre vds_do_init_vd(vds_t *vds, uint64_t id, char *device_path, uint64_t options, 45235081Sachartre uint64_t ldc_id, vd_t **vdp) 45241991Sheppo { 45251991Sheppo char tq_name[TASKQ_NAMELEN]; 45262032Slm66018 int status; 45271991Sheppo ddi_iblock_cookie_t iblock = NULL; 45281991Sheppo ldc_attr_t ldc_attr; 45291991Sheppo vd_t *vd; 45301991Sheppo 45311991Sheppo 45321991Sheppo ASSERT(vds != NULL); 45332410Slm66018 ASSERT(device_path != NULL); 45341991Sheppo ASSERT(vdp != NULL); 45352410Slm66018 PR0("Adding vdisk for %s", device_path); 45361991Sheppo 45371991Sheppo if ((vd = kmem_zalloc(sizeof (*vd), KM_NOSLEEP)) == NULL) { 45381991Sheppo PRN("No memory for virtual disk"); 45391991Sheppo return (EAGAIN); 45401991Sheppo } 45411991Sheppo *vdp = vd; /* assign here so vds_destroy_vd() can cleanup later */ 45421991Sheppo vd->vds = vds; 45433401Snarayan (void) strncpy(vd->device_path, device_path, MAXPATHLEN); 45441991Sheppo 45455081Sachartre /* Setup open flags */ 45465081Sachartre vd->open_flags = FREAD; 45475081Sachartre 45485081Sachartre if (!(options & VD_OPT_RDONLY)) 45495081Sachartre vd->open_flags |= FWRITE; 45505081Sachartre 45515081Sachartre if (options & VD_OPT_EXCLUSIVE) 45525081Sachartre vd->open_flags |= FEXCL; 45535081Sachartre 45545081Sachartre /* Setup disk type */ 45555081Sachartre if (options & VD_OPT_SLICE) { 45565081Sachartre vd->vdisk_type = VD_DISK_TYPE_SLICE; 45575081Sachartre vd->nslices = 1; 45585081Sachartre } else { 45595081Sachartre vd->vdisk_type = VD_DISK_TYPE_DISK; 45605081Sachartre vd->nslices = V_NUMPAR; 45615081Sachartre } 45625081Sachartre 45635081Sachartre /* default disk label */ 45645081Sachartre vd->vdisk_label = VD_DISK_LABEL_UNK; 45655081Sachartre 45662032Slm66018 /* Open vdisk and initialize parameters */ 45673401Snarayan if ((status = vd_setup_vd(vd)) == 0) { 45683401Snarayan vd->initialized |= VD_DISK_READY; 45693401Snarayan 45703401Snarayan ASSERT(vd->nslices > 0 && vd->nslices <= V_NUMPAR); 45713401Snarayan PR0("vdisk_type = %s, pseudo = %s, file = %s, nslices = %u", 45723401Snarayan ((vd->vdisk_type == VD_DISK_TYPE_DISK) ? "disk" : "slice"), 45733401Snarayan (vd->pseudo ? "yes" : "no"), (vd->file ? "yes" : "no"), 45743401Snarayan vd->nslices); 45753401Snarayan } else { 45763401Snarayan if (status != EAGAIN) 45773401Snarayan return (status); 45783401Snarayan } 45791991Sheppo 45801991Sheppo /* Initialize locking */ 45811991Sheppo if (ddi_get_soft_iblock_cookie(vds->dip, DDI_SOFTINT_MED, 45824696Sachartre &iblock) != DDI_SUCCESS) { 45831991Sheppo PRN("Could not get iblock cookie."); 45841991Sheppo return (EIO); 45851991Sheppo } 45861991Sheppo 45871991Sheppo mutex_init(&vd->lock, NULL, MUTEX_DRIVER, iblock); 45881991Sheppo vd->initialized |= VD_LOCKING; 45891991Sheppo 45901991Sheppo 45912336Snarayan /* Create start and completion task queues for the vdisk */ 45922336Snarayan (void) snprintf(tq_name, sizeof (tq_name), "vd_startq%lu", id); 45931991Sheppo PR1("tq_name = %s", tq_name); 45942336Snarayan if ((vd->startq = ddi_taskq_create(vds->dip, tq_name, 1, 45954696Sachartre TASKQ_DEFAULTPRI, 0)) == NULL) { 45961991Sheppo PRN("Could not create task queue"); 45971991Sheppo return (EIO); 45981991Sheppo } 45992336Snarayan (void) snprintf(tq_name, sizeof (tq_name), "vd_completionq%lu", id); 46002336Snarayan PR1("tq_name = %s", tq_name); 46012336Snarayan if ((vd->completionq = ddi_taskq_create(vds->dip, tq_name, 1, 46024696Sachartre TASKQ_DEFAULTPRI, 0)) == NULL) { 46032336Snarayan PRN("Could not create task queue"); 46042336Snarayan return (EIO); 46052336Snarayan } 46062336Snarayan vd->enabled = 1; /* before callback can dispatch to startq */ 46071991Sheppo 46081991Sheppo 46091991Sheppo /* Bring up LDC */ 46101991Sheppo ldc_attr.devclass = LDC_DEV_BLK_SVC; 46111991Sheppo ldc_attr.instance = ddi_get_instance(vds->dip); 46121991Sheppo ldc_attr.mode = LDC_MODE_UNRELIABLE; 46132410Slm66018 ldc_attr.mtu = VD_LDC_MTU; 46141991Sheppo if ((status = ldc_init(ldc_id, &ldc_attr, &vd->ldc_handle)) != 0) { 4615*5365Slm66018 PRN("Could not initialize LDC channel %lx, " 46163782Sachartre "init failed with error %d", ldc_id, status); 46171991Sheppo return (status); 46181991Sheppo } 46191991Sheppo vd->initialized |= VD_LDC; 46201991Sheppo 46211991Sheppo if ((status = ldc_reg_callback(vd->ldc_handle, vd_handle_ldc_events, 46224696Sachartre (caddr_t)vd)) != 0) { 46233782Sachartre PRN("Could not initialize LDC channel %lu," 46243782Sachartre "reg_callback failed with error %d", ldc_id, status); 46251991Sheppo return (status); 46261991Sheppo } 46271991Sheppo 46281991Sheppo if ((status = ldc_open(vd->ldc_handle)) != 0) { 46293782Sachartre PRN("Could not initialize LDC channel %lu," 46303782Sachartre "open failed with error %d", ldc_id, status); 46311991Sheppo return (status); 46321991Sheppo } 46331991Sheppo 46342793Slm66018 if ((status = ldc_up(vd->ldc_handle)) != 0) { 46353166Ssg70180 PR0("ldc_up() returned errno %d", status); 46362793Slm66018 } 46372793Slm66018 46382531Snarayan /* Allocate the inband task memory handle */ 46392531Snarayan status = ldc_mem_alloc_handle(vd->ldc_handle, &(vd->inband_task.mhdl)); 46402531Snarayan if (status) { 46413782Sachartre PRN("Could not initialize LDC channel %lu," 46423782Sachartre "alloc_handle failed with error %d", ldc_id, status); 46432531Snarayan return (ENXIO); 46442531Snarayan } 46451991Sheppo 46461991Sheppo /* Add the successfully-initialized vdisk to the server's table */ 46471991Sheppo if (mod_hash_insert(vds->vd_table, (mod_hash_key_t)id, vd) != 0) { 46481991Sheppo PRN("Error adding vdisk ID %lu to table", id); 46491991Sheppo return (EIO); 46501991Sheppo } 46511991Sheppo 46522793Slm66018 /* Allocate the staging buffer */ 46532793Slm66018 vd->max_msglen = sizeof (vio_msg_t); /* baseline vio message size */ 46542793Slm66018 vd->vio_msgp = kmem_alloc(vd->max_msglen, KM_SLEEP); 46552793Slm66018 46562793Slm66018 /* store initial state */ 46572793Slm66018 vd->state = VD_STATE_INIT; 46582793Slm66018 46591991Sheppo return (0); 46601991Sheppo } 46611991Sheppo 46622793Slm66018 static void 46632793Slm66018 vd_free_dring_task(vd_t *vdp) 46642793Slm66018 { 46652793Slm66018 if (vdp->dring_task != NULL) { 46662793Slm66018 ASSERT(vdp->dring_len != 0); 46672793Slm66018 /* Free all dring_task memory handles */ 46682793Slm66018 for (int i = 0; i < vdp->dring_len; i++) { 46692793Slm66018 (void) ldc_mem_free_handle(vdp->dring_task[i].mhdl); 46702793Slm66018 kmem_free(vdp->dring_task[i].msg, vdp->max_msglen); 46712793Slm66018 vdp->dring_task[i].msg = NULL; 46722793Slm66018 } 46732793Slm66018 kmem_free(vdp->dring_task, 46742793Slm66018 (sizeof (*vdp->dring_task)) * vdp->dring_len); 46752793Slm66018 vdp->dring_task = NULL; 46762793Slm66018 } 46772793Slm66018 } 46782793Slm66018 46791991Sheppo /* 46801991Sheppo * Destroy the state associated with a virtual disk 46811991Sheppo */ 46821991Sheppo static void 46831991Sheppo vds_destroy_vd(void *arg) 46841991Sheppo { 46851991Sheppo vd_t *vd = (vd_t *)arg; 46863166Ssg70180 int retry = 0, rv; 46871991Sheppo 46881991Sheppo if (vd == NULL) 46891991Sheppo return; 46901991Sheppo 46912336Snarayan PR0("Destroying vdisk state"); 46922336Snarayan 46932531Snarayan if (vd->dk_efi.dki_data != NULL) 46942531Snarayan kmem_free(vd->dk_efi.dki_data, vd->dk_efi.dki_length); 46952531Snarayan 46961991Sheppo /* Disable queuing requests for the vdisk */ 46971991Sheppo if (vd->initialized & VD_LOCKING) { 46981991Sheppo mutex_enter(&vd->lock); 46991991Sheppo vd->enabled = 0; 47001991Sheppo mutex_exit(&vd->lock); 47011991Sheppo } 47021991Sheppo 47032336Snarayan /* Drain and destroy start queue (*before* destroying completionq) */ 47042336Snarayan if (vd->startq != NULL) 47052336Snarayan ddi_taskq_destroy(vd->startq); /* waits for queued tasks */ 47062336Snarayan 47072336Snarayan /* Drain and destroy completion queue (*before* shutting down LDC) */ 47082336Snarayan if (vd->completionq != NULL) 47092336Snarayan ddi_taskq_destroy(vd->completionq); /* waits for tasks */ 47102336Snarayan 47112793Slm66018 vd_free_dring_task(vd); 47122793Slm66018 47133166Ssg70180 /* Free the inband task memory handle */ 47143166Ssg70180 (void) ldc_mem_free_handle(vd->inband_task.mhdl); 47153166Ssg70180 47163166Ssg70180 /* Shut down LDC */ 47173166Ssg70180 if (vd->initialized & VD_LDC) { 47183166Ssg70180 /* unmap the dring */ 47193166Ssg70180 if (vd->initialized & VD_DRING) 47203166Ssg70180 (void) ldc_mem_dring_unmap(vd->dring_handle); 47213166Ssg70180 47223166Ssg70180 /* close LDC channel - retry on EAGAIN */ 47233166Ssg70180 while ((rv = ldc_close(vd->ldc_handle)) == EAGAIN) { 47243166Ssg70180 if (++retry > vds_ldc_retries) { 47253166Ssg70180 PR0("Timed out closing channel"); 47263166Ssg70180 break; 47273166Ssg70180 } 47283166Ssg70180 drv_usecwait(vds_ldc_delay); 47293166Ssg70180 } 47303166Ssg70180 if (rv == 0) { 47313166Ssg70180 (void) ldc_unreg_callback(vd->ldc_handle); 47323166Ssg70180 (void) ldc_fini(vd->ldc_handle); 47333166Ssg70180 } else { 47343166Ssg70180 /* 47353166Ssg70180 * Closing the LDC channel has failed. Ideally we should 47363166Ssg70180 * fail here but there is no Zeus level infrastructure 47373166Ssg70180 * to handle this. The MD has already been changed and 47383166Ssg70180 * we have to do the close. So we try to do as much 47393166Ssg70180 * clean up as we can. 47403166Ssg70180 */ 47413166Ssg70180 (void) ldc_set_cb_mode(vd->ldc_handle, LDC_CB_DISABLE); 47423166Ssg70180 while (ldc_unreg_callback(vd->ldc_handle) == EAGAIN) 47433166Ssg70180 drv_usecwait(vds_ldc_delay); 47443166Ssg70180 } 47453166Ssg70180 } 47463166Ssg70180 47472793Slm66018 /* Free the staging buffer for msgs */ 47482793Slm66018 if (vd->vio_msgp != NULL) { 47492793Slm66018 kmem_free(vd->vio_msgp, vd->max_msglen); 47502793Slm66018 vd->vio_msgp = NULL; 47512793Slm66018 } 47522793Slm66018 47532793Slm66018 /* Free the inband message buffer */ 47542793Slm66018 if (vd->inband_task.msg != NULL) { 47552793Slm66018 kmem_free(vd->inband_task.msg, vd->max_msglen); 47562793Slm66018 vd->inband_task.msg = NULL; 47572336Snarayan } 47585331Samw 47593782Sachartre if (vd->file) { 47603782Sachartre /* Close file */ 47615081Sachartre (void) VOP_CLOSE(vd->file_vnode, vd->open_flags, 1, 47625331Samw 0, kcred, NULL); 47633782Sachartre VN_RELE(vd->file_vnode); 47644696Sachartre if (vd->file_devid != NULL) 47654696Sachartre ddi_devid_free(vd->file_devid); 47663782Sachartre } else { 47673782Sachartre /* Close any open backing-device slices */ 47683782Sachartre for (uint_t slice = 0; slice < vd->nslices; slice++) { 47693782Sachartre if (vd->ldi_handle[slice] != NULL) { 47703782Sachartre PR0("Closing slice %u", slice); 47713782Sachartre (void) ldi_close(vd->ldi_handle[slice], 47725081Sachartre vd->open_flags, kcred); 47733401Snarayan } 47741991Sheppo } 47751991Sheppo } 47761991Sheppo 47771991Sheppo /* Free lock */ 47781991Sheppo if (vd->initialized & VD_LOCKING) 47791991Sheppo mutex_destroy(&vd->lock); 47801991Sheppo 47811991Sheppo /* Finally, free the vdisk structure itself */ 47821991Sheppo kmem_free(vd, sizeof (*vd)); 47831991Sheppo } 47841991Sheppo 47851991Sheppo static int 47865081Sachartre vds_init_vd(vds_t *vds, uint64_t id, char *device_path, uint64_t options, 47875081Sachartre uint64_t ldc_id) 47881991Sheppo { 47891991Sheppo int status; 47901991Sheppo vd_t *vd = NULL; 47911991Sheppo 47921991Sheppo 47935081Sachartre if ((status = vds_do_init_vd(vds, id, device_path, options, 47945081Sachartre ldc_id, &vd)) != 0) 47951991Sheppo vds_destroy_vd(vd); 47961991Sheppo 47971991Sheppo return (status); 47981991Sheppo } 47991991Sheppo 48001991Sheppo static int 48011991Sheppo vds_do_get_ldc_id(md_t *md, mde_cookie_t vd_node, mde_cookie_t *channel, 48021991Sheppo uint64_t *ldc_id) 48031991Sheppo { 48041991Sheppo int num_channels; 48051991Sheppo 48061991Sheppo 48071991Sheppo /* Look for channel endpoint child(ren) of the vdisk MD node */ 48081991Sheppo if ((num_channels = md_scan_dag(md, vd_node, 48094696Sachartre md_find_name(md, VD_CHANNEL_ENDPOINT), 48104696Sachartre md_find_name(md, "fwd"), channel)) <= 0) { 48111991Sheppo PRN("No \"%s\" found for virtual disk", VD_CHANNEL_ENDPOINT); 48121991Sheppo return (-1); 48131991Sheppo } 48141991Sheppo 48151991Sheppo /* Get the "id" value for the first channel endpoint node */ 48161991Sheppo if (md_get_prop_val(md, channel[0], VD_ID_PROP, ldc_id) != 0) { 48171991Sheppo PRN("No \"%s\" property found for \"%s\" of vdisk", 48181991Sheppo VD_ID_PROP, VD_CHANNEL_ENDPOINT); 48191991Sheppo return (-1); 48201991Sheppo } 48211991Sheppo 48221991Sheppo if (num_channels > 1) { 48231991Sheppo PRN("Using ID of first of multiple channels for this vdisk"); 48241991Sheppo } 48251991Sheppo 48261991Sheppo return (0); 48271991Sheppo } 48281991Sheppo 48291991Sheppo static int 48301991Sheppo vds_get_ldc_id(md_t *md, mde_cookie_t vd_node, uint64_t *ldc_id) 48311991Sheppo { 48321991Sheppo int num_nodes, status; 48331991Sheppo size_t size; 48341991Sheppo mde_cookie_t *channel; 48351991Sheppo 48361991Sheppo 48371991Sheppo if ((num_nodes = md_node_count(md)) <= 0) { 48381991Sheppo PRN("Invalid node count in Machine Description subtree"); 48391991Sheppo return (-1); 48401991Sheppo } 48411991Sheppo size = num_nodes*(sizeof (*channel)); 48421991Sheppo channel = kmem_zalloc(size, KM_SLEEP); 48431991Sheppo status = vds_do_get_ldc_id(md, vd_node, channel, ldc_id); 48441991Sheppo kmem_free(channel, size); 48451991Sheppo 48461991Sheppo return (status); 48471991Sheppo } 48481991Sheppo 48495081Sachartre /* 48505081Sachartre * Function: 48515081Sachartre * vds_get_options 48525081Sachartre * 48535081Sachartre * Description: 48545081Sachartre * Parse the options of a vds node. Options are defined as an array 48555081Sachartre * of strings in the vds-block-device-opts property of the vds node 48565081Sachartre * in the machine description. Options are returned as a bitmask. The 48575081Sachartre * mapping between the bitmask options and the options strings from the 48585081Sachartre * machine description is defined in the vd_bdev_options[] array. 48595081Sachartre * 48605081Sachartre * The vds-block-device-opts property is optional. If a vds has no such 48615081Sachartre * property then no option is defined. 48625081Sachartre * 48635081Sachartre * Parameters: 48645081Sachartre * md - machine description. 48655081Sachartre * vd_node - vds node in the machine description for which 48665081Sachartre * options have to be parsed. 48675081Sachartre * options - the returned options. 48685081Sachartre * 48695081Sachartre * Return Code: 48705081Sachartre * none. 48715081Sachartre */ 48725081Sachartre static void 48735081Sachartre vds_get_options(md_t *md, mde_cookie_t vd_node, uint64_t *options) 48745081Sachartre { 48755081Sachartre char *optstr, *opt; 48765081Sachartre int len, n, i; 48775081Sachartre 48785081Sachartre *options = 0; 48795081Sachartre 48805081Sachartre if (md_get_prop_data(md, vd_node, VD_BLOCK_DEVICE_OPTS, 48815081Sachartre (uint8_t **)&optstr, &len) != 0) { 48825081Sachartre PR0("No options found"); 48835081Sachartre return; 48845081Sachartre } 48855081Sachartre 48865081Sachartre /* parse options */ 48875081Sachartre opt = optstr; 48885081Sachartre n = sizeof (vd_bdev_options) / sizeof (vd_option_t); 48895081Sachartre 48905081Sachartre while (opt < optstr + len) { 48915081Sachartre for (i = 0; i < n; i++) { 48925081Sachartre if (strncmp(vd_bdev_options[i].vdo_name, 48935081Sachartre opt, VD_OPTION_NLEN) == 0) { 48945081Sachartre *options |= vd_bdev_options[i].vdo_value; 48955081Sachartre break; 48965081Sachartre } 48975081Sachartre } 48985081Sachartre 48995081Sachartre if (i < n) { 49005081Sachartre PR0("option: %s", opt); 49015081Sachartre } else { 49025081Sachartre PRN("option %s is unknown or unsupported", opt); 49035081Sachartre } 49045081Sachartre 49055081Sachartre opt += strlen(opt) + 1; 49065081Sachartre } 49075081Sachartre } 49085081Sachartre 49091991Sheppo static void 49101991Sheppo vds_add_vd(vds_t *vds, md_t *md, mde_cookie_t vd_node) 49111991Sheppo { 49122410Slm66018 char *device_path = NULL; 49135081Sachartre uint64_t id = 0, ldc_id = 0, options = 0; 49141991Sheppo 49151991Sheppo if (md_get_prop_val(md, vd_node, VD_ID_PROP, &id) != 0) { 49161991Sheppo PRN("Error getting vdisk \"%s\"", VD_ID_PROP); 49171991Sheppo return; 49181991Sheppo } 49191991Sheppo PR0("Adding vdisk ID %lu", id); 49201991Sheppo if (md_get_prop_str(md, vd_node, VD_BLOCK_DEVICE_PROP, 49214696Sachartre &device_path) != 0) { 49221991Sheppo PRN("Error getting vdisk \"%s\"", VD_BLOCK_DEVICE_PROP); 49231991Sheppo return; 49241991Sheppo } 49251991Sheppo 49265081Sachartre vds_get_options(md, vd_node, &options); 49275081Sachartre 49281991Sheppo if (vds_get_ldc_id(md, vd_node, &ldc_id) != 0) { 49291991Sheppo PRN("Error getting LDC ID for vdisk %lu", id); 49301991Sheppo return; 49311991Sheppo } 49321991Sheppo 49335081Sachartre if (vds_init_vd(vds, id, device_path, options, ldc_id) != 0) { 49341991Sheppo PRN("Failed to add vdisk ID %lu", id); 4935*5365Slm66018 if (mod_hash_destroy(vds->vd_table, (mod_hash_key_t)id) != 0) 4936*5365Slm66018 PRN("No vDisk entry found for vdisk ID %lu", id); 49371991Sheppo return; 49381991Sheppo } 49391991Sheppo } 49401991Sheppo 49411991Sheppo static void 49421991Sheppo vds_remove_vd(vds_t *vds, md_t *md, mde_cookie_t vd_node) 49431991Sheppo { 49441991Sheppo uint64_t id = 0; 49451991Sheppo 49461991Sheppo 49471991Sheppo if (md_get_prop_val(md, vd_node, VD_ID_PROP, &id) != 0) { 49481991Sheppo PRN("Unable to get \"%s\" property from vdisk's MD node", 49491991Sheppo VD_ID_PROP); 49501991Sheppo return; 49511991Sheppo } 49521991Sheppo PR0("Removing vdisk ID %lu", id); 49531991Sheppo if (mod_hash_destroy(vds->vd_table, (mod_hash_key_t)id) != 0) 49541991Sheppo PRN("No vdisk entry found for vdisk ID %lu", id); 49551991Sheppo } 49561991Sheppo 49571991Sheppo static void 49581991Sheppo vds_change_vd(vds_t *vds, md_t *prev_md, mde_cookie_t prev_vd_node, 49591991Sheppo md_t *curr_md, mde_cookie_t curr_vd_node) 49601991Sheppo { 49611991Sheppo char *curr_dev, *prev_dev; 49625081Sachartre uint64_t curr_id = 0, curr_ldc_id = 0, curr_options = 0; 49635081Sachartre uint64_t prev_id = 0, prev_ldc_id = 0, prev_options = 0; 49641991Sheppo size_t len; 49651991Sheppo 49661991Sheppo 49671991Sheppo /* Validate that vdisk ID has not changed */ 49681991Sheppo if (md_get_prop_val(prev_md, prev_vd_node, VD_ID_PROP, &prev_id) != 0) { 49691991Sheppo PRN("Error getting previous vdisk \"%s\" property", 49701991Sheppo VD_ID_PROP); 49711991Sheppo return; 49721991Sheppo } 49731991Sheppo if (md_get_prop_val(curr_md, curr_vd_node, VD_ID_PROP, &curr_id) != 0) { 49741991Sheppo PRN("Error getting current vdisk \"%s\" property", VD_ID_PROP); 49751991Sheppo return; 49761991Sheppo } 49771991Sheppo if (curr_id != prev_id) { 49781991Sheppo PRN("Not changing vdisk: ID changed from %lu to %lu", 49791991Sheppo prev_id, curr_id); 49801991Sheppo return; 49811991Sheppo } 49821991Sheppo 49831991Sheppo /* Validate that LDC ID has not changed */ 49841991Sheppo if (vds_get_ldc_id(prev_md, prev_vd_node, &prev_ldc_id) != 0) { 49851991Sheppo PRN("Error getting LDC ID for vdisk %lu", prev_id); 49861991Sheppo return; 49871991Sheppo } 49881991Sheppo 49891991Sheppo if (vds_get_ldc_id(curr_md, curr_vd_node, &curr_ldc_id) != 0) { 49901991Sheppo PRN("Error getting LDC ID for vdisk %lu", curr_id); 49911991Sheppo return; 49921991Sheppo } 49931991Sheppo if (curr_ldc_id != prev_ldc_id) { 49942032Slm66018 _NOTE(NOTREACHED); /* lint is confused */ 49951991Sheppo PRN("Not changing vdisk: " 49961991Sheppo "LDC ID changed from %lu to %lu", prev_ldc_id, curr_ldc_id); 49971991Sheppo return; 49981991Sheppo } 49991991Sheppo 50001991Sheppo /* Determine whether device path has changed */ 50011991Sheppo if (md_get_prop_str(prev_md, prev_vd_node, VD_BLOCK_DEVICE_PROP, 50024696Sachartre &prev_dev) != 0) { 50031991Sheppo PRN("Error getting previous vdisk \"%s\"", 50041991Sheppo VD_BLOCK_DEVICE_PROP); 50051991Sheppo return; 50061991Sheppo } 50071991Sheppo if (md_get_prop_str(curr_md, curr_vd_node, VD_BLOCK_DEVICE_PROP, 50084696Sachartre &curr_dev) != 0) { 50091991Sheppo PRN("Error getting current vdisk \"%s\"", VD_BLOCK_DEVICE_PROP); 50101991Sheppo return; 50111991Sheppo } 50121991Sheppo if (((len = strlen(curr_dev)) == strlen(prev_dev)) && 50131991Sheppo (strncmp(curr_dev, prev_dev, len) == 0)) 50141991Sheppo return; /* no relevant (supported) change */ 50151991Sheppo 50165081Sachartre /* Validate that options have not changed */ 50175081Sachartre vds_get_options(prev_md, prev_vd_node, &prev_options); 50185081Sachartre vds_get_options(curr_md, curr_vd_node, &curr_options); 50195081Sachartre if (prev_options != curr_options) { 50205081Sachartre PRN("Not changing vdisk: options changed from %lx to %lx", 50215081Sachartre prev_options, curr_options); 50225081Sachartre return; 50235081Sachartre } 50245081Sachartre 50251991Sheppo PR0("Changing vdisk ID %lu", prev_id); 50262793Slm66018 50271991Sheppo /* Remove old state, which will close vdisk and reset */ 50281991Sheppo if (mod_hash_destroy(vds->vd_table, (mod_hash_key_t)prev_id) != 0) 50291991Sheppo PRN("No entry found for vdisk ID %lu", prev_id); 50302793Slm66018 50311991Sheppo /* Re-initialize vdisk with new state */ 50325081Sachartre if (vds_init_vd(vds, curr_id, curr_dev, curr_options, 50335081Sachartre curr_ldc_id) != 0) { 50341991Sheppo PRN("Failed to change vdisk ID %lu", curr_id); 50351991Sheppo return; 50361991Sheppo } 50371991Sheppo } 50381991Sheppo 50391991Sheppo static int 50401991Sheppo vds_process_md(void *arg, mdeg_result_t *md) 50411991Sheppo { 50421991Sheppo int i; 50431991Sheppo vds_t *vds = arg; 50441991Sheppo 50451991Sheppo 50461991Sheppo if (md == NULL) 50471991Sheppo return (MDEG_FAILURE); 50481991Sheppo ASSERT(vds != NULL); 50491991Sheppo 50501991Sheppo for (i = 0; i < md->removed.nelem; i++) 50511991Sheppo vds_remove_vd(vds, md->removed.mdp, md->removed.mdep[i]); 50521991Sheppo for (i = 0; i < md->match_curr.nelem; i++) 50531991Sheppo vds_change_vd(vds, md->match_prev.mdp, md->match_prev.mdep[i], 50541991Sheppo md->match_curr.mdp, md->match_curr.mdep[i]); 50551991Sheppo for (i = 0; i < md->added.nelem; i++) 50561991Sheppo vds_add_vd(vds, md->added.mdp, md->added.mdep[i]); 50571991Sheppo 50581991Sheppo return (MDEG_SUCCESS); 50591991Sheppo } 50601991Sheppo 50613401Snarayan 50621991Sheppo static int 50631991Sheppo vds_do_attach(dev_info_t *dip) 50641991Sheppo { 50653297Ssb155480 int status, sz; 50663297Ssb155480 int cfg_handle; 50671991Sheppo minor_t instance = ddi_get_instance(dip); 50681991Sheppo vds_t *vds; 50693297Ssb155480 mdeg_prop_spec_t *pspecp; 50703297Ssb155480 mdeg_node_spec_t *ispecp; 50711991Sheppo 50721991Sheppo /* 50731991Sheppo * The "cfg-handle" property of a vds node in an MD contains the MD's 50741991Sheppo * notion of "instance", or unique identifier, for that node; OBP 50751991Sheppo * stores the value of the "cfg-handle" MD property as the value of 50761991Sheppo * the "reg" property on the node in the device tree it builds from 50771991Sheppo * the MD and passes to Solaris. Thus, we look up the devinfo node's 50781991Sheppo * "reg" property value to uniquely identify this device instance when 50791991Sheppo * registering with the MD event-generation framework. If the "reg" 50801991Sheppo * property cannot be found, the device tree state is presumably so 50811991Sheppo * broken that there is no point in continuing. 50821991Sheppo */ 50833297Ssb155480 if (!ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 50844696Sachartre VD_REG_PROP)) { 50853297Ssb155480 PRN("vds \"%s\" property does not exist", VD_REG_PROP); 50861991Sheppo return (DDI_FAILURE); 50871991Sheppo } 50881991Sheppo 50891991Sheppo /* Get the MD instance for later MDEG registration */ 50901991Sheppo cfg_handle = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 50913297Ssb155480 VD_REG_PROP, -1); 50921991Sheppo 50931991Sheppo if (ddi_soft_state_zalloc(vds_state, instance) != DDI_SUCCESS) { 50941991Sheppo PRN("Could not allocate state for instance %u", instance); 50951991Sheppo return (DDI_FAILURE); 50961991Sheppo } 50971991Sheppo 50981991Sheppo if ((vds = ddi_get_soft_state(vds_state, instance)) == NULL) { 50991991Sheppo PRN("Could not get state for instance %u", instance); 51001991Sheppo ddi_soft_state_free(vds_state, instance); 51011991Sheppo return (DDI_FAILURE); 51021991Sheppo } 51031991Sheppo 51041991Sheppo vds->dip = dip; 51051991Sheppo vds->vd_table = mod_hash_create_ptrhash("vds_vd_table", VDS_NCHAINS, 51064696Sachartre vds_destroy_vd, sizeof (void *)); 51074696Sachartre 51081991Sheppo ASSERT(vds->vd_table != NULL); 51091991Sheppo 51101991Sheppo if ((status = ldi_ident_from_dip(dip, &vds->ldi_ident)) != 0) { 51111991Sheppo PRN("ldi_ident_from_dip() returned errno %d", status); 51121991Sheppo return (DDI_FAILURE); 51131991Sheppo } 51141991Sheppo vds->initialized |= VDS_LDI; 51151991Sheppo 51161991Sheppo /* Register for MD updates */ 51173297Ssb155480 sz = sizeof (vds_prop_template); 51183297Ssb155480 pspecp = kmem_alloc(sz, KM_SLEEP); 51193297Ssb155480 bcopy(vds_prop_template, pspecp, sz); 51203297Ssb155480 51213297Ssb155480 VDS_SET_MDEG_PROP_INST(pspecp, cfg_handle); 51223297Ssb155480 51233297Ssb155480 /* initialize the complete prop spec structure */ 51243297Ssb155480 ispecp = kmem_zalloc(sizeof (mdeg_node_spec_t), KM_SLEEP); 51253297Ssb155480 ispecp->namep = "virtual-device"; 51263297Ssb155480 ispecp->specp = pspecp; 51273297Ssb155480 51283297Ssb155480 if (mdeg_register(ispecp, &vd_match, vds_process_md, vds, 51294696Sachartre &vds->mdeg) != MDEG_SUCCESS) { 51301991Sheppo PRN("Unable to register for MD updates"); 51313297Ssb155480 kmem_free(ispecp, sizeof (mdeg_node_spec_t)); 51323297Ssb155480 kmem_free(pspecp, sz); 51331991Sheppo return (DDI_FAILURE); 51341991Sheppo } 51353297Ssb155480 51363297Ssb155480 vds->ispecp = ispecp; 51371991Sheppo vds->initialized |= VDS_MDEG; 51381991Sheppo 51392032Slm66018 /* Prevent auto-detaching so driver is available whenever MD changes */ 51402032Slm66018 if (ddi_prop_update_int(DDI_DEV_T_NONE, dip, DDI_NO_AUTODETACH, 1) != 51412032Slm66018 DDI_PROP_SUCCESS) { 51422032Slm66018 PRN("failed to set \"%s\" property for instance %u", 51432032Slm66018 DDI_NO_AUTODETACH, instance); 51442032Slm66018 } 51452032Slm66018 51461991Sheppo ddi_report_dev(dip); 51471991Sheppo return (DDI_SUCCESS); 51481991Sheppo } 51491991Sheppo 51501991Sheppo static int 51511991Sheppo vds_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 51521991Sheppo { 51531991Sheppo int status; 51541991Sheppo 51551991Sheppo switch (cmd) { 51561991Sheppo case DDI_ATTACH: 51572336Snarayan PR0("Attaching"); 51581991Sheppo if ((status = vds_do_attach(dip)) != DDI_SUCCESS) 51591991Sheppo (void) vds_detach(dip, DDI_DETACH); 51601991Sheppo return (status); 51611991Sheppo case DDI_RESUME: 51622336Snarayan PR0("No action required for DDI_RESUME"); 51631991Sheppo return (DDI_SUCCESS); 51641991Sheppo default: 51651991Sheppo return (DDI_FAILURE); 51661991Sheppo } 51671991Sheppo } 51681991Sheppo 51691991Sheppo static struct dev_ops vds_ops = { 51701991Sheppo DEVO_REV, /* devo_rev */ 51711991Sheppo 0, /* devo_refcnt */ 51721991Sheppo ddi_no_info, /* devo_getinfo */ 51731991Sheppo nulldev, /* devo_identify */ 51741991Sheppo nulldev, /* devo_probe */ 51751991Sheppo vds_attach, /* devo_attach */ 51761991Sheppo vds_detach, /* devo_detach */ 51771991Sheppo nodev, /* devo_reset */ 51781991Sheppo NULL, /* devo_cb_ops */ 51791991Sheppo NULL, /* devo_bus_ops */ 51801991Sheppo nulldev /* devo_power */ 51811991Sheppo }; 51821991Sheppo 51831991Sheppo static struct modldrv modldrv = { 51841991Sheppo &mod_driverops, 51854838Slm66018 "virtual disk server", 51861991Sheppo &vds_ops, 51871991Sheppo }; 51881991Sheppo 51891991Sheppo static struct modlinkage modlinkage = { 51901991Sheppo MODREV_1, 51911991Sheppo &modldrv, 51921991Sheppo NULL 51931991Sheppo }; 51941991Sheppo 51951991Sheppo 51961991Sheppo int 51971991Sheppo _init(void) 51981991Sheppo { 5199*5365Slm66018 int status; 52002336Snarayan 52011991Sheppo if ((status = ddi_soft_state_init(&vds_state, sizeof (vds_t), 1)) != 0) 52021991Sheppo return (status); 5203*5365Slm66018 52041991Sheppo if ((status = mod_install(&modlinkage)) != 0) { 52051991Sheppo ddi_soft_state_fini(&vds_state); 52061991Sheppo return (status); 52071991Sheppo } 52081991Sheppo 52091991Sheppo return (0); 52101991Sheppo } 52111991Sheppo 52121991Sheppo int 52131991Sheppo _info(struct modinfo *modinfop) 52141991Sheppo { 52151991Sheppo return (mod_info(&modlinkage, modinfop)); 52161991Sheppo } 52171991Sheppo 52181991Sheppo int 52191991Sheppo _fini(void) 52201991Sheppo { 52211991Sheppo int status; 52221991Sheppo 52231991Sheppo if ((status = mod_remove(&modlinkage)) != 0) 52241991Sheppo return (status); 52251991Sheppo ddi_soft_state_fini(&vds_state); 52261991Sheppo return (0); 52271991Sheppo } 5228