1936b7af6Sjw149990 /* 2936b7af6Sjw149990 * CDDL HEADER START 3936b7af6Sjw149990 * 4936b7af6Sjw149990 * The contents of this file are subject to the terms of the 5936b7af6Sjw149990 * Common Development and Distribution License (the "License"). 6936b7af6Sjw149990 * You may not use this file except in compliance with the License. 7936b7af6Sjw149990 * 8936b7af6Sjw149990 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9936b7af6Sjw149990 * or http://www.opensolaris.org/os/licensing. 10936b7af6Sjw149990 * See the License for the specific language governing permissions 11936b7af6Sjw149990 * and limitations under the License. 12936b7af6Sjw149990 * 13936b7af6Sjw149990 * When distributing Covered Code, include this CDDL HEADER in each 14936b7af6Sjw149990 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15936b7af6Sjw149990 * If applicable, add the following below this CDDL HEADER, with the 16936b7af6Sjw149990 * fields enclosed by brackets "[]" replaced with your own identifying 17936b7af6Sjw149990 * information: Portions Copyright [yyyy] [name of copyright owner] 18936b7af6Sjw149990 * 19936b7af6Sjw149990 * CDDL HEADER END 20936b7af6Sjw149990 */ 21936b7af6Sjw149990 22936b7af6Sjw149990 /* 234c06356bSdh142964 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24936b7af6Sjw149990 * Use is subject to license terms. 25936b7af6Sjw149990 */ 26936b7af6Sjw149990 27936b7af6Sjw149990 28936b7af6Sjw149990 /* 29936b7af6Sjw149990 * SMP - Serial Management Protocol Device Driver 30936b7af6Sjw149990 * 31936b7af6Sjw149990 * The SMP driver provides user programs access to SAS Serial Management 32936b7af6Sjw149990 * Protocol devices by providing ioctl interface. 33936b7af6Sjw149990 */ 34936b7af6Sjw149990 35936b7af6Sjw149990 #include <sys/modctl.h> 36936b7af6Sjw149990 #include <sys/file.h> 37936b7af6Sjw149990 #include <sys/scsi/scsi.h> 38936b7af6Sjw149990 #include <sys/scsi/targets/smp.h> 39936b7af6Sjw149990 #include <sys/sdt.h> 40936b7af6Sjw149990 41936b7af6Sjw149990 /* 42936b7af6Sjw149990 * Standard entrypoints 43936b7af6Sjw149990 */ 44936b7af6Sjw149990 static int smp_attach(dev_info_t *, ddi_attach_cmd_t); 45936b7af6Sjw149990 static int smp_detach(dev_info_t *, ddi_detach_cmd_t); 46936b7af6Sjw149990 static int smp_open(dev_t *, int, int, cred_t *); 47936b7af6Sjw149990 static int smp_close(dev_t, int, int, cred_t *); 48936b7af6Sjw149990 static int smp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 49936b7af6Sjw149990 50936b7af6Sjw149990 /* 51936b7af6Sjw149990 * Configuration routines 52936b7af6Sjw149990 */ 53936b7af6Sjw149990 static int smp_do_attach(dev_info_t *); 54936b7af6Sjw149990 static int smp_do_detach(dev_info_t *); 55936b7af6Sjw149990 56936b7af6Sjw149990 /* 57936b7af6Sjw149990 * Command handle routing 58936b7af6Sjw149990 */ 59936b7af6Sjw149990 static int smp_handle_func(dev_t, intptr_t, int, cred_t *, int *); 60936b7af6Sjw149990 61936b7af6Sjw149990 /* 62936b7af6Sjw149990 * Logging/debugging routines 63936b7af6Sjw149990 */ 64936b7af6Sjw149990 static void smp_log(smp_state_t *, int, const char *, ...); 65936b7af6Sjw149990 66936b7af6Sjw149990 int smp_retry_times = SMP_DEFAULT_RETRY_TIMES; 674c06356bSdh142964 int smp_retry_delay = 10000; /* 10msec */ 684c06356bSdh142964 int smp_delay_cmd = 1; /* 1usec */ 694c06356bSdh142964 int smp_single_command = 1; /* one command at a time */ 704c06356bSdh142964 714c06356bSdh142964 static int smp_retry_recovered = 0; /* retry recovery counter */ 724c06356bSdh142964 static int smp_retry_failed = 0; /* retry failed counter */ 734c06356bSdh142964 static int smp_failed = 0; 74936b7af6Sjw149990 75936b7af6Sjw149990 static struct cb_ops smp_cb_ops = { 76936b7af6Sjw149990 smp_open, /* open */ 77936b7af6Sjw149990 smp_close, /* close */ 78936b7af6Sjw149990 nodev, /* strategy */ 79936b7af6Sjw149990 nodev, /* print */ 80936b7af6Sjw149990 nodev, /* dump */ 81936b7af6Sjw149990 nodev, /* read */ 82936b7af6Sjw149990 nodev, /* write */ 83936b7af6Sjw149990 smp_ioctl, /* ioctl */ 84936b7af6Sjw149990 nodev, /* devmap */ 85936b7af6Sjw149990 nodev, /* mmap */ 86936b7af6Sjw149990 nodev, /* segmap */ 87936b7af6Sjw149990 nochpoll, /* poll */ 88936b7af6Sjw149990 ddi_prop_op, /* cb_prop_op */ 89936b7af6Sjw149990 0, /* streamtab */ 90936b7af6Sjw149990 D_MP | D_NEW | D_HOTPLUG /* Driver compatibility flag */ 91936b7af6Sjw149990 }; 92936b7af6Sjw149990 93936b7af6Sjw149990 static struct dev_ops smp_dev_ops = { 94936b7af6Sjw149990 DEVO_REV, /* devo_rev, */ 95936b7af6Sjw149990 0, /* refcnt */ 96*96c4a178SChris Horne ddi_getinfo_1to1, /* info */ 97936b7af6Sjw149990 nulldev, /* identify */ 98*96c4a178SChris Horne NULL, /* probe */ 99936b7af6Sjw149990 smp_attach, /* attach */ 100936b7af6Sjw149990 smp_detach, /* detach */ 101936b7af6Sjw149990 nodev, /* reset */ 102936b7af6Sjw149990 &smp_cb_ops, /* driver operations */ 103936b7af6Sjw149990 (struct bus_ops *)0, /* bus operations */ 10419397407SSherry Moore NULL, /* power */ 10519397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 106936b7af6Sjw149990 }; 107936b7af6Sjw149990 108936b7af6Sjw149990 static void *smp_soft_state = NULL; 109936b7af6Sjw149990 110936b7af6Sjw149990 static struct modldrv modldrv = { 11119397407SSherry Moore &mod_driverops, "smp device driver", &smp_dev_ops 112936b7af6Sjw149990 }; 113936b7af6Sjw149990 114936b7af6Sjw149990 static struct modlinkage modlinkage = { 115936b7af6Sjw149990 MODREV_1, &modldrv, NULL 116936b7af6Sjw149990 }; 117936b7af6Sjw149990 118936b7af6Sjw149990 int 119936b7af6Sjw149990 _init(void) 120936b7af6Sjw149990 { 121936b7af6Sjw149990 int err; 122936b7af6Sjw149990 123936b7af6Sjw149990 if ((err = ddi_soft_state_init(&smp_soft_state, 124936b7af6Sjw149990 sizeof (smp_state_t), SMP_ESTIMATED_NUM_DEVS)) != 0) { 125936b7af6Sjw149990 return (err); 126936b7af6Sjw149990 } 127936b7af6Sjw149990 128936b7af6Sjw149990 if ((err = mod_install(&modlinkage)) != 0) { 129936b7af6Sjw149990 ddi_soft_state_fini(&smp_soft_state); 130936b7af6Sjw149990 } 131936b7af6Sjw149990 132936b7af6Sjw149990 return (err); 133936b7af6Sjw149990 } 134936b7af6Sjw149990 135936b7af6Sjw149990 int 136936b7af6Sjw149990 _fini(void) 137936b7af6Sjw149990 { 138936b7af6Sjw149990 int err; 139936b7af6Sjw149990 140936b7af6Sjw149990 if ((err = mod_remove(&modlinkage)) == 0) { 141936b7af6Sjw149990 ddi_soft_state_fini(&smp_soft_state); 142936b7af6Sjw149990 } 143936b7af6Sjw149990 144936b7af6Sjw149990 return (err); 145936b7af6Sjw149990 } 146936b7af6Sjw149990 147936b7af6Sjw149990 int 148936b7af6Sjw149990 _info(struct modinfo *modinfop) 149936b7af6Sjw149990 { 150936b7af6Sjw149990 return (mod_info(&modlinkage, modinfop)); 151936b7af6Sjw149990 } 152936b7af6Sjw149990 153936b7af6Sjw149990 /* 154936b7af6Sjw149990 * smp_attach() 155936b7af6Sjw149990 * attach(9e) entrypoint. 156936b7af6Sjw149990 */ 157936b7af6Sjw149990 static int 158936b7af6Sjw149990 smp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 159936b7af6Sjw149990 { 160936b7af6Sjw149990 int err; 161936b7af6Sjw149990 162936b7af6Sjw149990 switch (cmd) { 163936b7af6Sjw149990 case DDI_ATTACH: 164936b7af6Sjw149990 err = smp_do_attach(dip); 165936b7af6Sjw149990 break; 166936b7af6Sjw149990 case DDI_RESUME: 167936b7af6Sjw149990 err = DDI_SUCCESS; 168936b7af6Sjw149990 break; 169936b7af6Sjw149990 default: 170936b7af6Sjw149990 err = DDI_FAILURE; 171936b7af6Sjw149990 break; 172936b7af6Sjw149990 } 173936b7af6Sjw149990 174936b7af6Sjw149990 if (err != DDI_SUCCESS) { 175936b7af6Sjw149990 smp_log(NULL, CE_NOTE, "!smp_attach(), " 176936b7af6Sjw149990 "device unit-address @%s failed", 177936b7af6Sjw149990 ddi_get_name_addr(dip)); 178936b7af6Sjw149990 } 179936b7af6Sjw149990 return (err); 180936b7af6Sjw149990 } 181936b7af6Sjw149990 182936b7af6Sjw149990 /* 183936b7af6Sjw149990 * smp_do_attach() 184936b7af6Sjw149990 * handle the nitty details of attach. 185936b7af6Sjw149990 */ 186936b7af6Sjw149990 static int 187936b7af6Sjw149990 smp_do_attach(dev_info_t *dip) 188936b7af6Sjw149990 { 189936b7af6Sjw149990 int instance; 190*96c4a178SChris Horne struct smp_device *smp_sd; 191*96c4a178SChris Horne uchar_t *srmir = NULL; 192*96c4a178SChris Horne uint_t srmirlen = 0; 193*96c4a178SChris Horne ddi_devid_t devid = NULL; 194936b7af6Sjw149990 smp_state_t *smp_state; 195936b7af6Sjw149990 196936b7af6Sjw149990 instance = ddi_get_instance(dip); 197*96c4a178SChris Horne smp_sd = ddi_get_driver_private(dip); 198*96c4a178SChris Horne ASSERT(smp_sd != NULL); 199936b7af6Sjw149990 200936b7af6Sjw149990 DTRACE_PROBE2(smp__attach__detach, int, instance, char *, 201936b7af6Sjw149990 ddi_get_name_addr(dip)); 202936b7af6Sjw149990 203*96c4a178SChris Horne /* make sure device is there, and establish srmir identity property */ 204*96c4a178SChris Horne if (smp_probe(smp_sd) != DDI_PROBE_SUCCESS) { 205*96c4a178SChris Horne smp_log(NULL, CE_NOTE, 206*96c4a178SChris Horne "!smp_do_attach: failed smp_probe, " 207*96c4a178SChris Horne "device unit-address @%s", ddi_get_name_addr(dip)); 208*96c4a178SChris Horne return (DDI_FAILURE); 209*96c4a178SChris Horne } 210*96c4a178SChris Horne 211*96c4a178SChris Horne /* if we have not already registered a devid, then do so now */ 212*96c4a178SChris Horne if (ddi_devid_get(dip, &devid) != DDI_SUCCESS) { 213*96c4a178SChris Horne /* get the srmir identity information for use in devid */ 214*96c4a178SChris Horne (void) ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, 215*96c4a178SChris Horne DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, 216*96c4a178SChris Horne SMP_PROP_REPORT_MANUFACTURER, &srmir, &srmirlen); 217*96c4a178SChris Horne 218*96c4a178SChris Horne /* Convert smp unit-address and srmir into devid */ 219*96c4a178SChris Horne if (ddi_devid_smp_encode(DEVID_SMP_ENCODE_VERSION_LATEST, 220*96c4a178SChris Horne (char *)ddi_driver_name(dip), ddi_get_name_addr(dip), 221*96c4a178SChris Horne srmir, srmirlen, &devid) == DDI_SUCCESS) { 222*96c4a178SChris Horne /* register the devid */ 223*96c4a178SChris Horne (void) ddi_devid_register(dip, devid); 224*96c4a178SChris Horne } 225*96c4a178SChris Horne ddi_prop_free(srmir); 226*96c4a178SChris Horne } 227*96c4a178SChris Horne 228*96c4a178SChris Horne /* We don't need the devid for our own operation, so free now. */ 229*96c4a178SChris Horne if (devid) 230*96c4a178SChris Horne ddi_devid_free(devid); 231*96c4a178SChris Horne 232*96c4a178SChris Horne /* we are now done with srmir identity property defined by smp_probe */ 233*96c4a178SChris Horne (void) ndi_prop_remove(DDI_DEV_T_NONE, 234*96c4a178SChris Horne dip, SMP_PROP_REPORT_MANUFACTURER); 235*96c4a178SChris Horne 236936b7af6Sjw149990 if (ddi_soft_state_zalloc(smp_soft_state, instance) != DDI_SUCCESS) { 237936b7af6Sjw149990 smp_log(NULL, CE_NOTE, 238936b7af6Sjw149990 "!smp_do_attach: failed to allocate softstate, " 239936b7af6Sjw149990 "device unit-address @%s", ddi_get_name_addr(dip)); 240936b7af6Sjw149990 return (DDI_FAILURE); 241936b7af6Sjw149990 } 242936b7af6Sjw149990 243936b7af6Sjw149990 smp_state = ddi_get_soft_state(smp_soft_state, instance); 244*96c4a178SChris Horne smp_state->smp_sd = smp_sd; 245936b7af6Sjw149990 246936b7af6Sjw149990 /* 247936b7af6Sjw149990 * For simplicity, the minor number == the instance number 248936b7af6Sjw149990 */ 249936b7af6Sjw149990 if (ddi_create_minor_node(dip, "smp", S_IFCHR, 250936b7af6Sjw149990 instance, DDI_NT_SMP, NULL) == DDI_FAILURE) { 251936b7af6Sjw149990 smp_log(smp_state, CE_NOTE, 252936b7af6Sjw149990 "!smp_do_attach: minor node creation failed, " 253936b7af6Sjw149990 "device unit-address @%s", ddi_get_name_addr(dip)); 254936b7af6Sjw149990 ddi_soft_state_free(smp_soft_state, instance); 255936b7af6Sjw149990 return (DDI_FAILURE); 256936b7af6Sjw149990 } 257936b7af6Sjw149990 258936b7af6Sjw149990 mutex_init(&smp_state->smp_mutex, NULL, MUTEX_DRIVER, NULL); 259936b7af6Sjw149990 smp_state->smp_open_flag = SMP_CLOSED; 260936b7af6Sjw149990 261936b7af6Sjw149990 ddi_report_dev(dip); 262936b7af6Sjw149990 return (DDI_SUCCESS); 263936b7af6Sjw149990 } 264936b7af6Sjw149990 265936b7af6Sjw149990 /* 266936b7af6Sjw149990 * smp_detach() 267936b7af6Sjw149990 * detach(9E) entrypoint 268936b7af6Sjw149990 */ 269936b7af6Sjw149990 static int 270936b7af6Sjw149990 smp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 271936b7af6Sjw149990 { 272936b7af6Sjw149990 int instance; 273936b7af6Sjw149990 smp_state_t *smp_state; 274936b7af6Sjw149990 275936b7af6Sjw149990 instance = ddi_get_instance(dip); 276936b7af6Sjw149990 smp_state = ddi_get_soft_state(smp_soft_state, instance); 277936b7af6Sjw149990 278936b7af6Sjw149990 if (smp_state == NULL) { 279936b7af6Sjw149990 smp_log(NULL, CE_NOTE, 280936b7af6Sjw149990 "!smp_detach: failed, no softstate found (%d), " 281936b7af6Sjw149990 "device unit-address @%s", 282936b7af6Sjw149990 instance, ddi_get_name_addr(dip)); 283936b7af6Sjw149990 return (DDI_FAILURE); 284936b7af6Sjw149990 } 285936b7af6Sjw149990 286936b7af6Sjw149990 switch (cmd) { 287936b7af6Sjw149990 case DDI_DETACH: 288936b7af6Sjw149990 return (smp_do_detach(dip)); 289936b7af6Sjw149990 case DDI_SUSPEND: 290936b7af6Sjw149990 return (DDI_SUCCESS); 291936b7af6Sjw149990 default: 292936b7af6Sjw149990 return (DDI_FAILURE); 293936b7af6Sjw149990 } 294936b7af6Sjw149990 } 295936b7af6Sjw149990 296936b7af6Sjw149990 /* 297936b7af6Sjw149990 * smp_do_detach() 298936b7af6Sjw149990 * detach the driver, tearing down resources. 299936b7af6Sjw149990 */ 300936b7af6Sjw149990 static int 301936b7af6Sjw149990 smp_do_detach(dev_info_t *dip) 302936b7af6Sjw149990 { 303936b7af6Sjw149990 int instance; 304936b7af6Sjw149990 smp_state_t *smp_state; 305936b7af6Sjw149990 306936b7af6Sjw149990 instance = ddi_get_instance(dip); 307936b7af6Sjw149990 smp_state = ddi_get_soft_state(smp_soft_state, instance); 308936b7af6Sjw149990 309936b7af6Sjw149990 DTRACE_PROBE2(smp__attach__detach, int, instance, char *, 310936b7af6Sjw149990 ddi_get_name_addr(dip)); 311936b7af6Sjw149990 312936b7af6Sjw149990 mutex_destroy(&smp_state->smp_mutex); 313936b7af6Sjw149990 ddi_soft_state_free(smp_soft_state, instance); 314936b7af6Sjw149990 ddi_remove_minor_node(dip, NULL); 315936b7af6Sjw149990 return (DDI_SUCCESS); 316936b7af6Sjw149990 } 317936b7af6Sjw149990 318936b7af6Sjw149990 /*ARGSUSED*/ 319936b7af6Sjw149990 static int 320936b7af6Sjw149990 smp_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p) 321936b7af6Sjw149990 { 322936b7af6Sjw149990 smp_state_t *smp_state; 323936b7af6Sjw149990 int instance; 324936b7af6Sjw149990 int rv = 0; 325936b7af6Sjw149990 326936b7af6Sjw149990 instance = getminor(*dev_p); 327936b7af6Sjw149990 if ((smp_state = ddi_get_soft_state(smp_soft_state, instance)) 328936b7af6Sjw149990 == NULL) { 329936b7af6Sjw149990 return (ENXIO); 330936b7af6Sjw149990 } 331936b7af6Sjw149990 332936b7af6Sjw149990 mutex_enter(&smp_state->smp_mutex); 333936b7af6Sjw149990 if (flag & FEXCL) { 334936b7af6Sjw149990 if (smp_state->smp_open_flag != SMP_CLOSED) { 335936b7af6Sjw149990 rv = EBUSY; 336936b7af6Sjw149990 } else { 337936b7af6Sjw149990 smp_state->smp_open_flag = SMP_EXOPENED; 338936b7af6Sjw149990 } 339936b7af6Sjw149990 } else { 340936b7af6Sjw149990 if (smp_state->smp_open_flag == SMP_EXOPENED) { 341936b7af6Sjw149990 rv = EBUSY; 342936b7af6Sjw149990 } else { 343936b7af6Sjw149990 smp_state->smp_open_flag = SMP_SOPENED; 344936b7af6Sjw149990 } 345936b7af6Sjw149990 } 346936b7af6Sjw149990 mutex_exit(&smp_state->smp_mutex); 347936b7af6Sjw149990 348936b7af6Sjw149990 return (rv); 349936b7af6Sjw149990 } 350936b7af6Sjw149990 351936b7af6Sjw149990 /*ARGSUSED*/ 352936b7af6Sjw149990 static int 353936b7af6Sjw149990 smp_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 354936b7af6Sjw149990 { 355936b7af6Sjw149990 smp_state_t *smp_state; 356936b7af6Sjw149990 int instance; 357936b7af6Sjw149990 int rv = 0; 358936b7af6Sjw149990 359936b7af6Sjw149990 instance = getminor(dev); 360936b7af6Sjw149990 if ((smp_state = ddi_get_soft_state(smp_soft_state, instance)) 361936b7af6Sjw149990 == NULL) { 362936b7af6Sjw149990 return (ENXIO); 363936b7af6Sjw149990 } 364936b7af6Sjw149990 365936b7af6Sjw149990 mutex_enter(&smp_state->smp_mutex); 3668eadeb34Sml198626 if (smp_state->smp_open_flag == SMP_CLOSED) { 3678eadeb34Sml198626 smp_log(smp_state, CE_NOTE, "!smp device is already in close"); 3688eadeb34Sml198626 } else { 369936b7af6Sjw149990 smp_state->smp_open_flag = SMP_CLOSED; 370936b7af6Sjw149990 } 371936b7af6Sjw149990 mutex_exit(&smp_state->smp_mutex); 372936b7af6Sjw149990 return (rv); 373936b7af6Sjw149990 } 374936b7af6Sjw149990 375936b7af6Sjw149990 /*ARGSUSED*/ 376936b7af6Sjw149990 static int 377936b7af6Sjw149990 smp_handle_func(dev_t dev, 378936b7af6Sjw149990 intptr_t arg, int flag, cred_t *cred_p, int *rval_p) 379936b7af6Sjw149990 { 380936b7af6Sjw149990 usmp_cmd_t usmp_cmd_data, *usmp_cmd = &usmp_cmd_data; 381936b7af6Sjw149990 smp_pkt_t smp_pkt_data, *smp_pkt = &smp_pkt_data; 382936b7af6Sjw149990 smp_state_t *smp_state; 383936b7af6Sjw149990 int instance, retrycount; 384936b7af6Sjw149990 cred_t *cr; 385936b7af6Sjw149990 uint64_t cmd_flags = 0; 386936b7af6Sjw149990 int rval = 0; 387936b7af6Sjw149990 388936b7af6Sjw149990 #ifdef _MULTI_DATAMODEL 389936b7af6Sjw149990 usmp_cmd32_t usmp_cmd32_data, *usmp_cmd32 = &usmp_cmd32_data; 390936b7af6Sjw149990 #endif 391936b7af6Sjw149990 392936b7af6Sjw149990 /* require PRIV_SYS_DEVICES privilege */ 393936b7af6Sjw149990 cr = ddi_get_cred(); 394936b7af6Sjw149990 if ((drv_priv(cred_p) != 0) && (drv_priv(cr) != 0)) { 395936b7af6Sjw149990 return (EPERM); 396936b7af6Sjw149990 } 397936b7af6Sjw149990 398936b7af6Sjw149990 bzero(smp_pkt, sizeof (smp_pkt_t)); 399936b7af6Sjw149990 400936b7af6Sjw149990 instance = getminor(dev); 401936b7af6Sjw149990 if ((smp_state = ddi_get_soft_state(smp_soft_state, instance)) 402936b7af6Sjw149990 == NULL) { 403936b7af6Sjw149990 return (ENXIO); 404936b7af6Sjw149990 } 405936b7af6Sjw149990 406936b7af6Sjw149990 #ifdef _MULTI_DATAMODEL 407936b7af6Sjw149990 switch (ddi_model_convert_from(flag & FMODELS)) { 408936b7af6Sjw149990 case DDI_MODEL_ILP32: 409936b7af6Sjw149990 if (ddi_copyin((void *)arg, usmp_cmd32, sizeof (usmp_cmd32_t), 410936b7af6Sjw149990 flag)) { 411936b7af6Sjw149990 return (EFAULT); 412936b7af6Sjw149990 } 413936b7af6Sjw149990 414936b7af6Sjw149990 usmp_cmd32tousmp_cmd(usmp_cmd32, usmp_cmd); 415936b7af6Sjw149990 break; 416936b7af6Sjw149990 case DDI_MODEL_NONE: 417936b7af6Sjw149990 if (ddi_copyin((void *)arg, usmp_cmd, sizeof (usmp_cmd_t), 418936b7af6Sjw149990 flag)) { 419936b7af6Sjw149990 return (EFAULT); 420936b7af6Sjw149990 } 421936b7af6Sjw149990 break; 422936b7af6Sjw149990 } 423936b7af6Sjw149990 #else /* ! _MULTI_DATAMODEL */ 424936b7af6Sjw149990 if (ddi_copyin((void *)arg, usmp_cmd, sizeof (usmp_cmd_t), flag)) { 425936b7af6Sjw149990 return (EFAULT); 426936b7af6Sjw149990 } 427936b7af6Sjw149990 #endif /* _MULTI_DATAMODEL */ 428936b7af6Sjw149990 429936b7af6Sjw149990 if ((usmp_cmd->usmp_reqsize < SMP_MIN_REQUEST_SIZE) || 430936b7af6Sjw149990 (usmp_cmd->usmp_reqsize > SMP_MAX_REQUEST_SIZE) || 431936b7af6Sjw149990 (usmp_cmd->usmp_rspsize < SMP_MIN_RESPONSE_SIZE) || 432936b7af6Sjw149990 (usmp_cmd->usmp_rspsize > SMP_MAX_RESPONSE_SIZE)) { 433936b7af6Sjw149990 rval = EINVAL; 434936b7af6Sjw149990 goto done; 435936b7af6Sjw149990 } 436936b7af6Sjw149990 437*96c4a178SChris Horne smp_pkt->smp_pkt_reqsize = usmp_cmd->usmp_reqsize; 438*96c4a178SChris Horne smp_pkt->smp_pkt_rspsize = usmp_cmd->usmp_rspsize; 439936b7af6Sjw149990 440936b7af6Sjw149990 /* allocate memory space for smp request and response frame in kernel */ 441*96c4a178SChris Horne smp_pkt->smp_pkt_req = kmem_zalloc((size_t)usmp_cmd->usmp_reqsize, 442936b7af6Sjw149990 KM_SLEEP); 443936b7af6Sjw149990 cmd_flags |= SMP_FLAG_REQBUF; 444936b7af6Sjw149990 445*96c4a178SChris Horne smp_pkt->smp_pkt_rsp = kmem_zalloc((size_t)usmp_cmd->usmp_rspsize, 446936b7af6Sjw149990 KM_SLEEP); 447936b7af6Sjw149990 cmd_flags |= SMP_FLAG_RSPBUF; 448936b7af6Sjw149990 449936b7af6Sjw149990 /* copy smp request frame to kernel space */ 450*96c4a178SChris Horne if (ddi_copyin(usmp_cmd->usmp_req, smp_pkt->smp_pkt_req, 451936b7af6Sjw149990 (size_t)usmp_cmd->usmp_reqsize, flag) != 0) { 452936b7af6Sjw149990 rval = EFAULT; 453936b7af6Sjw149990 goto done; 454936b7af6Sjw149990 } 455936b7af6Sjw149990 456*96c4a178SChris Horne DTRACE_PROBE1(smp__transport__start, caddr_t, smp_pkt->smp_pkt_req); 457936b7af6Sjw149990 458*96c4a178SChris Horne smp_pkt->smp_pkt_address = &smp_state->smp_sd->smp_sd_address; 459936b7af6Sjw149990 if (usmp_cmd->usmp_timeout <= 0) { 460*96c4a178SChris Horne smp_pkt->smp_pkt_timeout = SMP_DEFAULT_TIMEOUT; 461936b7af6Sjw149990 } else { 462*96c4a178SChris Horne smp_pkt->smp_pkt_timeout = usmp_cmd->usmp_timeout; 463936b7af6Sjw149990 } 464936b7af6Sjw149990 465*96c4a178SChris Horne /* call smp_transport entry and send smp_pkt to HBA driver */ 466936b7af6Sjw149990 cmd_flags |= SMP_FLAG_XFER; 467936b7af6Sjw149990 for (retrycount = 0; retrycount <= smp_retry_times; retrycount++) { 4684c06356bSdh142964 4694c06356bSdh142964 /* 4704c06356bSdh142964 * To improve transport reliability, only allow one command 471*96c4a178SChris Horne * outstanding at a time in smp_transport(). 4724c06356bSdh142964 * 4734c06356bSdh142964 * NOTE: Some expanders have issues with heavy smp load. 4744c06356bSdh142964 */ 4754c06356bSdh142964 if (smp_single_command) { 4764c06356bSdh142964 mutex_enter(&smp_state->smp_mutex); 4774c06356bSdh142964 while (smp_state->smp_busy) 4784c06356bSdh142964 cv_wait(&smp_state->smp_cv, 4794c06356bSdh142964 &smp_state->smp_mutex); 4804c06356bSdh142964 smp_state->smp_busy = 1; 4814c06356bSdh142964 mutex_exit(&smp_state->smp_mutex); 4824c06356bSdh142964 } 4834c06356bSdh142964 4844c06356bSdh142964 /* Let the transport know if more retries are possible. */ 485*96c4a178SChris Horne smp_pkt->smp_pkt_will_retry = 4864c06356bSdh142964 (retrycount < smp_retry_times) ? 1 : 0; 4874c06356bSdh142964 488*96c4a178SChris Horne smp_pkt->smp_pkt_reason = 0; 489*96c4a178SChris Horne rval = smp_transport(smp_pkt); /* put on the wire */ 4904c06356bSdh142964 4914c06356bSdh142964 if (smp_delay_cmd) 4924c06356bSdh142964 delay(drv_usectohz(smp_delay_cmd)); 4934c06356bSdh142964 4944c06356bSdh142964 if (smp_single_command) { 4954c06356bSdh142964 mutex_enter(&smp_state->smp_mutex); 4964c06356bSdh142964 smp_state->smp_busy = 0; 4974c06356bSdh142964 cv_signal(&smp_state->smp_cv); 4984c06356bSdh142964 mutex_exit(&smp_state->smp_mutex); 4994c06356bSdh142964 } 5004c06356bSdh142964 5014c06356bSdh142964 if (rval == DDI_SUCCESS) { 5024c06356bSdh142964 if (retrycount) 5034c06356bSdh142964 smp_retry_recovered++; 5044c06356bSdh142964 rval = 0; 505936b7af6Sjw149990 break; 506936b7af6Sjw149990 } 507936b7af6Sjw149990 508*96c4a178SChris Horne switch (smp_pkt->smp_pkt_reason) { 509936b7af6Sjw149990 case EAGAIN: 510936b7af6Sjw149990 if (retrycount < smp_retry_times) { 511*96c4a178SChris Horne bzero(smp_pkt->smp_pkt_rsp, 512936b7af6Sjw149990 (size_t)usmp_cmd->usmp_rspsize); 5134c06356bSdh142964 if (smp_retry_delay) 5144c06356bSdh142964 delay(drv_usectohz(smp_retry_delay)); 515936b7af6Sjw149990 continue; 516936b7af6Sjw149990 } else { 5174c06356bSdh142964 smp_retry_failed++; 518936b7af6Sjw149990 smp_log(smp_state, CE_NOTE, 519*96c4a178SChris Horne "!smp_transport failed, smp_pkt_reason %d", 520*96c4a178SChris Horne smp_pkt->smp_pkt_reason); 521*96c4a178SChris Horne rval = smp_pkt->smp_pkt_reason; 522936b7af6Sjw149990 goto copyout; 523936b7af6Sjw149990 } 524936b7af6Sjw149990 default: 525936b7af6Sjw149990 smp_log(smp_state, CE_NOTE, 526*96c4a178SChris Horne "!smp_transport failed, smp_pkt_reason %d", 527*96c4a178SChris Horne smp_pkt->smp_pkt_reason); 528*96c4a178SChris Horne rval = smp_pkt->smp_pkt_reason; 529936b7af6Sjw149990 goto copyout; 530936b7af6Sjw149990 } 531936b7af6Sjw149990 } 532936b7af6Sjw149990 533936b7af6Sjw149990 copyout: 534936b7af6Sjw149990 /* copy out smp response to user process */ 535*96c4a178SChris Horne if (ddi_copyout(smp_pkt->smp_pkt_rsp, usmp_cmd->usmp_rsp, 536936b7af6Sjw149990 (size_t)usmp_cmd->usmp_rspsize, flag) != 0) { 537936b7af6Sjw149990 rval = EFAULT; 538936b7af6Sjw149990 } 539936b7af6Sjw149990 540936b7af6Sjw149990 done: 541936b7af6Sjw149990 if ((cmd_flags & SMP_FLAG_XFER) != 0) { 542*96c4a178SChris Horne DTRACE_PROBE2(smp__transport__done, caddr_t, 543*96c4a178SChris Horne smp_pkt->smp_pkt_rsp, uchar_t, smp_pkt->smp_pkt_reason); 544936b7af6Sjw149990 } 545936b7af6Sjw149990 if ((cmd_flags & SMP_FLAG_REQBUF) != 0) { 546*96c4a178SChris Horne kmem_free(smp_pkt->smp_pkt_req, smp_pkt->smp_pkt_reqsize); 547936b7af6Sjw149990 } 548936b7af6Sjw149990 if ((cmd_flags & SMP_FLAG_RSPBUF) != 0) { 549*96c4a178SChris Horne kmem_free(smp_pkt->smp_pkt_rsp, smp_pkt->smp_pkt_rspsize); 550936b7af6Sjw149990 } 5514c06356bSdh142964 5524c06356bSdh142964 if (rval) 5534c06356bSdh142964 smp_failed++; 554936b7af6Sjw149990 return (rval); 555936b7af6Sjw149990 } 556936b7af6Sjw149990 557936b7af6Sjw149990 /*ARGSUSED*/ 558936b7af6Sjw149990 static int 559936b7af6Sjw149990 smp_ioctl(dev_t dev, 560936b7af6Sjw149990 int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p) 561936b7af6Sjw149990 { 562936b7af6Sjw149990 int rval = 0; 563936b7af6Sjw149990 564936b7af6Sjw149990 switch (cmd) { 565936b7af6Sjw149990 case USMPFUNC: 566936b7af6Sjw149990 /* 567936b7af6Sjw149990 * The response payload is valid only if return value is 0 568936b7af6Sjw149990 * or EOVERFLOW. 569936b7af6Sjw149990 */ 570936b7af6Sjw149990 rval = smp_handle_func(dev, arg, flag, cred_p, rval_p); 571936b7af6Sjw149990 break; 572936b7af6Sjw149990 default: 573936b7af6Sjw149990 rval = EINVAL; 574936b7af6Sjw149990 } 575936b7af6Sjw149990 return (rval); 576936b7af6Sjw149990 } 577936b7af6Sjw149990 578936b7af6Sjw149990 static void 579936b7af6Sjw149990 smp_log(smp_state_t *smp_state, int level, const char *fmt, ...) 580936b7af6Sjw149990 { 581936b7af6Sjw149990 va_list ap; 582936b7af6Sjw149990 char buf[256]; 583936b7af6Sjw149990 dev_info_t *dip; 584936b7af6Sjw149990 585936b7af6Sjw149990 if (smp_state == (smp_state_t *)NULL) { 586936b7af6Sjw149990 dip = NULL; 587936b7af6Sjw149990 } else { 588*96c4a178SChris Horne dip = smp_state->smp_sd->smp_sd_dev; 589936b7af6Sjw149990 } 590936b7af6Sjw149990 591936b7af6Sjw149990 va_start(ap, fmt); 592936b7af6Sjw149990 (void) vsnprintf(buf, sizeof (buf), fmt, ap); 593936b7af6Sjw149990 va_end(ap); 594936b7af6Sjw149990 595936b7af6Sjw149990 scsi_log(dip, "smp", level, "%s", buf); 596936b7af6Sjw149990 } 597