cmdk.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Local Static Data
*/
#ifdef CMDK_DEBUG
#define DENT 0x0001
#define DIO 0x0002
static int cmdk_debug = DIO;
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/*
* NDKMAP is the base number for accessing the fdisk partitions.
* c?d?p0 --> cmdk@?,?:q
*/
#define PARTITION0_INDEX (NDKMAP + 0)
static void *cmdk_state;
static int cmdk_max_instance = 0;
/*
* Panic dumpsys state
* There is only a single flag that is not mutex locked since
* the system is prevented from thread switching and cmdk_dump
* will only be called in a single threaded operation.
*/
static int cmdk_indump;
/*
* the cmdk_attach_mutex protects cmdk_max_instance in multi-threaded
* attach situations
*/
static kmutex_t cmdk_attach_mutex;
static struct driver_minor_data {
char *name;
int minor;
int type;
} cmdk_minor_data[] = {
{"a", 0, S_IFBLK},
{"a,raw", 0, S_IFCHR},
{0}
};
/*
* Local Function Prototypes
*/
#ifdef NOT_USED
#endif /* NOT_USED */
/*
* Bad Block Handling Functions Prototypes
*/
static struct bbh_objops cmdk_bbh_ops = {
0, 0
};
static struct bbh_obj cmdk_bbh_obj = {
NULL,
};
/*
* Configuration Data
*/
/*
* Device driver ops vector
*/
static struct cb_ops cmdk_cb_ops = {
cmdkopen, /* open */
cmdkclose, /* close */
cmdkstrategy, /* strategy */
nodev, /* print */
cmdkdump, /* dump */
cmdkread, /* read */
cmdkwrite, /* write */
cmdkioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
cmdk_prop_op, /* cb_prop_op */
0, /* streamtab */
CB_REV, /* cb_rev */
cmdkaread, /* async read */
cmdkawrite /* async write */
};
void **result);
DEVO_REV, /* devo_rev, */
0, /* refcnt */
cmdkinfo, /* info */
nulldev, /* identify */
cmdkprobe, /* probe */
cmdkattach, /* attach */
cmdkdetach, /* detach */
nodev, /* reset */
&cmdk_cb_ops, /* driver operations */
(struct bus_ops *)0 /* bus operations */
};
/*
* This is the loadable module wrapper.
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module. This one is a driver */
"Common Direct Access Disk Driver %I%",
&cmdk_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
};
char _depends_on[] =
int
_init(void)
{
int rval;
return (rval);
}
return (rval);
}
int
_fini(void)
{
return (EBUSY);
/*
* This has been commented out until cmdk is a true
* unloadable module. Right now x86's are panicking on
* a diskless reconfig boot.
*/
#if 0 /* bugid 1186679 */
int rval;
if (rval != 0)
return (rval);
return (0);
#endif
}
int
{
}
/* pseudo BBH functions */
/*ARGSUSED*/
static opaque_t
{
return (NULL);
}
/*ARGSUSED*/
static bbh_cookie_t
{
return (NULL);
}
/*ARGSUSED*/
static void
{
}
/*
* Autoconfiguration Routines
*/
static int
{
int instance;
int status;
return (DDI_PROBE_PARTIAL);
return (DDI_PROBE_PARTIAL);
/* for property create inside DKLB_*() */
return (DDI_PROBE_PARTIAL);
}
if (status != DDI_PROBE_SUCCESS) {
return (status);
}
#ifdef CMDK_DEBUG
if (cmdk_debug & DENT)
PRF("cmdkprobe: instance= %d name= `%s`\n",
#endif
return (status);
}
static int
{
int instance;
struct driver_minor_data *dmdp;
char *node_type;
char name[48];
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
return (DDI_FAILURE);
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
}
if (instance > cmdk_max_instance)
/*
* Add a zero-length attribute to tell the world we support
* kernel ioctls (for layered drivers)
*/
DDI_KERNEL_IOCTL, NULL, 0);
/* Need to open the label and register devid early */
return (DDI_SUCCESS);
}
static int
{
int instance;
int max_instance;
switch (cmd) {
case DDI_DETACH:
if (!dkp)
continue;
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
/*
* The cmdk_part_info call at the end of cmdkattach may have
* caused cmdk_reopen to do a TGDK_OPEN, make sure we close on
*/
}
return (DDI_SUCCESS);
default:
#ifdef CMDK_DEBUG
if (cmdk_debug & DIO) {
}
#endif
return (DDI_FAILURE);
}
}
static int
{
int instance;
#ifdef lint
#endif
#ifdef CMDK_DEBUG
if (cmdk_debug & DENT)
PRF("cmdkinfo: call\n");
#endif
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_FAILURE);
break;
case DDI_INFO_DEVT2INSTANCE:
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
{
long lblocks;
#ifdef CMDK_DEBUG
if (cmdk_debug & DENT)
PRF("cmdk_prop_op: call\n");
#endif
/*
* Our dynamic properties are all device specific and size oriented.
* Requests issued under conditions where size is valid are passed
* to ddi_prop_op_nblocks with the size information, otherwise the
* request is passed to ddi_prop_op. Size depends on valid label.
*/
} else {
/* force re-read of MBR and label and partition info */
goto pass;
/* get nblocks value */
}
}
/*
* dump routine
*/
static int
{
int instance;
long p_lblkcnt;
#ifdef CMDK_DEBUG
if (cmdk_debug & DENT)
PRF("cmdkdump: call\n");
#endif
return (ENXIO);
return (EINVAL);
}
/*
* Copy in the dadkio_rwcmd according to the user's data model. If needed,
* convert it for our internal use.
*/
static int
{
switch (ddi_model_convert_from(flag)) {
case DDI_MODEL_ILP32: {
struct dadkio_rwcmd32 cmd32;
sizeof (struct dadkio_rwcmd32), flag)) {
return (EFAULT);
}
/*
* Note: we do not convert the 'status' field,
* as it should not contain valid data at this
* point.
*/
break;
}
case DDI_MODEL_NONE: {
sizeof (struct dadkio_rwcmd), flag)) {
return (EFAULT);
}
}
}
return (0);
}
/*
* If necessary, convert the internal rwcmdp and status to the appropriate
* data model and copy it out to the user.
*/
static int
{
switch (ddi_model_convert_from(flag)) {
case DDI_MODEL_ILP32: {
struct dadkio_rwcmd32 cmd32;
sizeof (struct dadkio_rwcmd32), flag))
return (EFAULT);
break;
}
case DDI_MODEL_NONE: {
sizeof (struct dadkio_rwcmd), flag))
return (EFAULT);
}
}
return (0);
}
/*
* ioctl routine
*/
static int
int *rval_p)
{
int instance;
struct scsi_device *devp;
return (ENXIO);
switch (cmd) {
case DKIOCGMEDIAINFO: {
struct dk_minfo media_info;
return (EFAULT);
} else {
return (0);
}
}
case DKIOCINFO: {
/* controller information */
/* Unit Information */
return (EFAULT);
else
return (0);
}
case DKIOCPARTINFO: {
long len;
STRUCT_DECL(part_info, p);
/*
* force re-read of MBR and label and partition info
*/
return (ENXIO);
return (EOVERFLOW);
flag))
return (EFAULT);
return (0);
}
case DKIOCSTATE: {
int state;
int rval;
int part;
long p_lblkcnt;
return (EFAULT);
return (rval);
if (state == DKIO_INSERTED) {
/*
* force re-read of MBR and label and partition info
*/
part))
return (ENXIO);
return (ENXIO);
}
return (EFAULT);
return (0);
}
/*
* is media removable?
*/
case DKIOCREMOVABLE: {
int i;
return (EFAULT);
return (0);
}
case DKIOCG_VIRTGEOM:
case DKIOCG_PHYGEOM:
case DKIOCGGEOM:
case DKIOCSGEOM:
case DKIOCSVTOC:
case DKIOCGVTOC:
case DKIOCGAPART:
case DKIOCSAPART:
case DKIOCADDBAD:
/* If we don't have a label obj we can't call its ioctl */
return (EIO);
case DIOCTL_RWCMD: {
struct dadkio_rwcmd *rwcmdp;
int status;
if (status == 0) {
}
if (status == 0)
return (status);
}
default:
}
}
/*ARGSUSED1*/
static int
{
int part;
int instance;
int i;
return (ENXIO);
/* check for device has been opened */
return (ENXIO);
}
if (part < 0) {
return (ENXIO);
}
/* account for close */
} else
/* check for last close */
for (i = 0; i < OTYPCNT; i++) {
break;
}
if (i >= OTYPCNT) {
for (i = 0; i < CMDK_MAXPART; i++) {
break;
}
if (i >= CMDK_MAXPART) {
/* OK, last close */
}
}
return (DDI_SUCCESS);
}
/*ARGSUSED3*/
static int
{
int part;
int instance;
long p_lblkcnt;
int i;
return (ENXIO);
return (EINVAL);
return (ENXIO);
/* re-do the target open */
if (p_lblkcnt <= 0 &&
return (ENXIO);
}
} else {
/* fail if not doing non block open */
return (ENXIO);
}
}
return (EROFS);
}
/* check for part already opend exclusively */
goto excl_open_fail;
/* check if we can establish exclusive open */
goto excl_open_fail;
for (i = 0; i < OTYPCNT; i++) {
goto excl_open_fail;
}
}
/* open will succeed, acount for open */
else
return (DDI_SUCCESS);
return (EBUSY);
}
static int
{
struct cmdk_label *dklbp;
int i;
/* open the target disk */
return (FALSE);
/* mark as having opened target */
/* check for valid label object */
return (FALSE);
} else {
/* reset back to pseudo bbh */
}
/* search for proper disk label object */
continue;
== DDI_SUCCESS)
break;
}
/*
* the last opened label object will become the installed label
* this allows whole raw disk access for disk preparations
*/
return (TRUE);
}
/*
* read routine
*/
/*ARGSUSED2*/
static int
{
}
/*
* async read routine
*/
/*ARGSUSED2*/
static int
{
}
/*
* write routine
*/
/*ARGSUSED2*/
static int
{
}
/*
* async write routine
*/
/*ARGSUSED2*/
static int
{
}
static void
{
}
static int
{
}
static int
{
}
/*
* strategy routine
*/
static int
{
int instance;
long d_cnt;
long p_lblkcnt;
return (0);
}
/*
* only re-read the vtoc if necessary (force == FALSE)
*/
}
return (0);
}
}
}
return (0);
}
static int
{
struct scsi_device *devp;
char que_keyvalp[OBJNAMELEN];
int que_keylen;
char flc_keyvalp[OBJNAMELEN];
int flc_keylen;
char dsk_keyvalp[OBJNAMELEN];
int dsk_keylen;
que_keylen = sizeof (que_keyvalp);
return (DDI_FAILURE);
}
que_keyvalp[que_keylen] = (char)0;
flc_keylen = sizeof (flc_keyvalp);
"cmdk_create_obj: flow-control property undefined");
return (DDI_FAILURE);
}
flc_keyvalp[flc_keylen] = (char)0;
dsk_keylen = sizeof (dsk_keyvalp);
"cmdk_create_obj: target disk property undefined");
return (DDI_FAILURE);
}
dsk_keyvalp[dsk_keylen] = (char)0;
"cmdk_create_obj: ERROR creating queue method %s\n",
return (DDI_FAILURE);
}
(void) objmgr_destroy_obj(que_keyvalp);
"cmdk_create_obj: ERROR creating flow control %s\n",
return (DDI_FAILURE);
}
(void) objmgr_destroy_obj(que_keyvalp);
(void) objmgr_destroy_obj(flc_keyvalp);
"cmdk_create_obj: ERROR creating target disk %s\n",
return (DDI_FAILURE);
}
NULL);
return (DDI_SUCCESS);
}
static void
{
char que_keyvalp[OBJNAMELEN];
int que_keylen;
char flc_keyvalp[OBJNAMELEN];
int flc_keylen;
char dsk_keyvalp[OBJNAMELEN];
int dsk_keylen;
que_keylen = sizeof (que_keyvalp);
return;
}
que_keyvalp[que_keylen] = (char)0;
flc_keylen = sizeof (flc_keyvalp);
"cmdk_destroy_obj: flow-control property undefined");
return;
}
flc_keyvalp[flc_keylen] = (char)0;
dsk_keylen = sizeof (dsk_keyvalp);
"cmdk_destroy_obj: target disk property undefined");
return;
}
dsk_keyvalp[dsk_keylen] = (char)0;
(void) objmgr_destroy_obj(que_keyvalp);
(void) objmgr_destroy_obj(flc_keyvalp);
(void) objmgr_destroy_obj(dsk_keyvalp);
if (unload) {
}
}
static int
{
struct cmdk_label *dklbp;
char **contents;
int i;
int mystatus;
} else {
for (i = 0; (i < CMDK_LABEL_MAX) && (i < numelem);
i++, dklbp++) {
}
}
break;
"cmdk_create_lbobj: ERROR creating disklabel %s",
} else {
}
}
return (mystatus);
}
/*ARGSUSED*/
static void
{
struct cmdk_label *dklbp;
int i;
return;
for (i = 0; i < CMDK_LABEL_MAX; i++, dklbp++) {
continue;
if (unload)
}
}
/*
* cmdk_part_info()
*
* Make the device valid if possible. The dk_pinfo_lock is only
* held for very short periods so that there's very little
* contention with the cmdk_devstatus() function which can
* be called from interrupt context.
*
* This function implements a simple state machine which looks
* like this:
*
*
* +---------------------------------+
* | |
* +--> invalid --> busy --> valid --+
* ^|
* |v
* busy2
*
* This function can change the state from invalid to busy, or from
* busy2 to busy, or from busy to valid.
*
* The cmdk_devstatus() function can change the state from valid
* to invalid or from busy to busy2.
*
*/
static int
int part)
{
/*
* The dk_pinfo_state variable (and by implication the partition
* info) is always protected by the dk_pinfo_lock mutex.
*/
for (;;) {
switch (dkp->dk_pinfo_state) {
case CMDK_PARTINFO_VALID:
/* it's already valid */
if (!force) {
goto done;
}
/*FALLTHROUGH*/
case CMDK_PARTINFO_INVALID:
/*
* It's invalid or we're being forced to reread
*/
goto reopen;
case CMDK_PARTINFO_BUSY:
case CMDK_PARTINFO_BUSY2:
/*
* Some other thread has already called
* cmdk_reopen(), wait for it to complete and then
* start over from the top.
*/
}
}
/*
* ASSERT: only one thread at a time can possibly reach this point
* and invoke cmdk_reopen()
*/
for (;;) {
int rc;
/*
* drop the mutex while in cmdk_reopen() because
* it may take a long time to return
*/
/*
* bailout, probably due to no device,
* or invalid label
*/
goto error;
}
switch (dkp->dk_pinfo_state) {
case CMDK_PARTINFO_BUSY:
goto done;
case CMDK_PARTINFO_BUSY2:
/*
* device status changed by cmdk_devstatus(),
* redo the reopen
*/
break;
}
}
done:
/*
* finished cmdk_reopen() without any device status change
*/
return (TRUE);
return (FALSE);
}
#ifdef NOT_USED
static void
{
switch (dkp->dk_pinfo_state) {
case CMDK_PARTINFO_VALID:
break;
case CMDK_PARTINFO_INVALID:
break;
case CMDK_PARTINFO_BUSY:
break;
case CMDK_PARTINFO_BUSY2:
break;
}
}
#endif /* NOT_USED */
/*
* initialize the state for cmdk_part_info()
*/
static void
{
}
static void
{
}