sys/dev/mfi/mfi_tbolt.c
/*
 * The ThunderBolt HW has an option for the driver to directly
 * access the underlying disks and operate on the RAID.  To
 * do this there needs to be a capability to keep the RAID controller
 * and driver in sync.  The FreeBSD driver does not take advantage
 * of this feature since it adds a lot of complexity and slows down
 * performance.  Performance is gained by using the controller's
 * cache etc.
 *
 * Even though this driver doesn't access the disks directly, an
 * AEN like command is used to inform the RAID firmware to "sync"
 * with all LD's via the MFI_DCMD_LD_MAP_GET_INFO command.  This
 * command in write mode will return when the RAID firmware has
 * detected a change to the RAID state.  Examples of this type
 * of change are removing a disk.  Once the command returns then
 * the driver needs to acknowledge this and "sync" all LD's again.
 * This repeats until we shutdown.  Then we need to cancel this
 * pending command.
 *
 * If this is not done right the RAID firmware will not remove a
 * pulled drive and the RAID won't go degraded etc.  Effectively,
 * stopping any RAID mangement to functions.
 *
 * Doing another LD sync, requires the use of an event since the
 * driver needs to do a mfi_wait_command and can't do that in an
 * interrupt thread.
 *
 * The driver could get the RAID state via the MFI_DCMD_LD_MAP_GET_INFO
 * That requires a bunch of structure and it is simplier to just do
 * the MFI_DCMD_LD_GET_LIST versus walking the RAID map.
 */
The ThunderBolt HW has an option for the driver to directly
access the underlying disks and operate on the RAID.  To
do this there needs to be a capability to keep the RAID controller
and driver in sync.  The FreeBSD driver does not take advantage
of this feature since it adds a lot of complexity and slows down
performance.  Performance is gained by using the controller's
cache etc.

Even though this driver doesn't access the disks directly, an
AEN like command is used to inform the RAID firmware to "sync"
with all LD's via the MFI_DCMD_LD_MAP_GET_INFO command.  This
command in write mode will return when the RAID firmware has
detected a change to the RAID state.  Examples of this type
of change are removing a disk.  Once the command returns then
the driver needs to acknowledge this and "sync" all LD's again.
This repeats until we shutdown.  Then we need to cancel this
pending command.

If this is not done right the RAID firmware will not remove a
pulled drive and the RAID won't go degraded etc.  Effectively,
stopping any RAID mangement to functions.

Doing another LD sync, requires the use of an event since the
driver needs to do a mfi_wait_command and can't do that in an
interrupt thread.

The driver could get the RAID state via the MFI_DCMD_LD_MAP_GET_INFO
That requires a bunch of structure and it is simplier to just do
the MFI_DCMD_LD_GET_LIST versus walking the RAID map.
The ThunderBolt HW has an option for the driver to directly access the underlying disks and operate on the RAID . To do this there needs to be a capability to keep the RAID controller and driver in sync . The FreeBSD driver does not take advantage of this feature since it adds a lot of complexity and slows down performance . Performance is gained by using the controller ' s cache etc . Even though this driver doesn ' t access the disks directly an AEN like command is used to inform the RAID firmware to " sync " with all LD ' s via the MFI_DCMD_LD_MAP_GET_INFO command . This command in write mode will return when the RAID firmware has detected a change to the RAID state . Examples of this type of change are removing a disk . Once the command returns then the driver needs to acknowledge this and " sync " all LD ' s again . This repeats until we shutdown . Then we need to cancel this pending command . If this is not done right the RAID firmware will not remove a pulled drive and the RAID won ' t go degraded etc . Effectively stopping any RAID mangement to functions . Doing another LD sync requires the use of an event since the driver needs to do a mfi_wait_command and can ' t do that in an interrupt thread . The driver could get the RAID state via the MFI_DCMD_LD_MAP_GET_INFO That requires a bunch of structure and it is simplier to just do the MFI_DCMD_LD_GET_LIST versus walking the RAID map .
void
mfi_tbolt_sync_map_info(struct mfi_softc *sc)
{
	int error = 0, i;
	struct mfi_command *cmd = NULL;
	struct mfi_dcmd_frame *dcmd = NULL;
	uint32_t context = 0;
	union mfi_ld_ref *ld_sync = NULL;
	size_t ld_size;
	struct mfi_frame_header *hdr;
	struct mfi_command *cm = NULL;
	struct mfi_ld_list *list = NULL;
	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
	if (sc->mfi_map_sync_cm != NULL || sc->cm_map_abort)
		return;
	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_LD_GET_LIST,
	    (void **)&list, sizeof(*list));
	if (error)
		goto out;
	cm->cm_flags = MFI_CMD_POLLED | MFI_CMD_DATAIN;
	if (mfi_wait_command(sc, cm) != 0) {
		device_printf(sc->mfi_dev, "Failed to get device listing\n");
		goto out;
	}
	hdr = &cm->cm_frame->header;
	if (hdr->cmd_status != MFI_STAT_OK) {
		device_printf(sc->mfi_dev, "MFI_DCMD_LD_GET_LIST failed %x\n",
			      hdr->cmd_status);
		goto out;
	}
	ld_size = sizeof(*ld_sync) * list->ld_count;
	ld_sync = (union mfi_ld_ref *) malloc(ld_size, M_MFIBUF,
	     M_NOWAIT | M_ZERO);
	if (ld_sync == NULL) {
		device_printf(sc->mfi_dev, "Failed to allocate sync\n");
		goto out;
	}
	for (i = 0; i < list->ld_count; i++)
		ld_sync[i].ref = list->ld_list[i].ld.ref;
	if ((cmd = mfi_dequeue_free(sc)) == NULL) {
		device_printf(sc->mfi_dev, "Failed to get command\n");
		free(ld_sync, M_MFIBUF);
		goto out;
	}
	context = cmd->cm_frame->header.context;
	bzero(cmd->cm_frame, sizeof(union mfi_frame));
	cmd->cm_frame->header.context = context;
	dcmd = &cmd->cm_frame->dcmd;
	bzero(dcmd->mbox, MFI_MBOX_SIZE);
	dcmd->header.cmd = MFI_CMD_DCMD;
	dcmd->header.flags = MFI_FRAME_DIR_WRITE;
	dcmd->header.timeout = 0;
	dcmd->header.data_len = ld_size;
	dcmd->header.scsi_status = 0;
	dcmd->opcode = MFI_DCMD_LD_MAP_GET_INFO;
	cmd->cm_sg = &dcmd->sgl;
	cmd->cm_total_frame_size = MFI_DCMD_FRAME_SIZE;
	cmd->cm_data = ld_sync;
	cmd->cm_private = ld_sync;
	cmd->cm_len = ld_size;
	cmd->cm_complete = mfi_sync_map_complete;
	sc->mfi_map_sync_cm = cmd;
	cmd->cm_flags = MFI_CMD_DATAOUT;
	cmd->cm_frame->dcmd.mbox[0] = list->ld_count;
	cmd->cm_frame->dcmd.mbox[1] = MFI_DCMD_MBOX_PEND_FLAG;
	if ((error = mfi_mapcmd(sc, cmd)) != 0) {
		device_printf(sc->mfi_dev, "failed to send map sync\n");
		free(ld_sync, M_MFIBUF);
		sc->mfi_map_sync_cm = NULL;
		mfi_release_command(cmd);
		goto out;
	}
out:
	if (list)
		free(list, M_MFIBUF);
	if (cm)
		mfi_release_command(cm);
}
void mfi_tbolt_sync_map_info ( struct mfi_softc * sc ) { int error = 0 , i ; struct mfi_command * cmd = NULL ; struct mfi_dcmd_frame * dcmd = NULL ; uint32_t context = 0 ; union mfi_ld_ref * ld_sync = NULL ; size_t ld_size ; struct mfi_frame_header * hdr ; struct mfi_command * cm = NULL ; struct mfi_ld_list * list = NULL ; mtx_assert ( & sc -> mfi_io_lock , MA_OWNED ) ; if ( sc -> mfi_map_sync_cm != NULL || sc -> cm_map_abort ) return ; error = mfi_dcmd_command ( sc , & cm , MFI_DCMD_LD_GET_LIST , ( void * * ) & list , sizeof ( * list ) ) ; if ( error ) goto out ; cm -> cm_flags = MFI_CMD_POLLED | MFI_CMD_DATAIN ; if ( mfi_wait_command ( sc , cm ) != 0 ) { device_printf ( sc -> mfi_dev , " \n " ) ; goto out ; } hdr = & cm -> cm_frame -> header ; if ( hdr -> cmd_status != MFI_STAT_OK ) { device_printf ( sc -> mfi_dev , " \n " , hdr -> cmd_status ) ; goto out ; } ld_size = sizeof ( * ld_sync ) * list -> ld_count ; ld_sync = ( union mfi_ld_ref * ) malloc ( ld_size , M_MFIBUF , M_NOWAIT | M_ZERO ) ; if ( ld_sync == NULL ) { device_printf ( sc -> mfi_dev , " \n " ) ; goto out ; } for ( i = 0 ; i < list -> ld_count ; i ++ ) ld_sync [ i ] . ref = list -> ld_list [ i ] . ld . ref ; if ( ( cmd = mfi_dequeue_free ( sc ) ) == NULL ) { device_printf ( sc -> mfi_dev , " \n " ) ; free ( ld_sync , M_MFIBUF ) ; goto out ; } context = cmd -> cm_frame -> header . context ; bzero ( cmd -> cm_frame , sizeof ( union mfi_frame ) ) ; cmd -> cm_frame -> header . context = context ; dcmd = & cmd -> cm_frame -> dcmd ; bzero ( dcmd -> mbox , MFI_MBOX_SIZE ) ; dcmd -> header . cmd = MFI_CMD_DCMD ; dcmd -> header . flags = MFI_FRAME_DIR_WRITE ; dcmd -> header . timeout = 0 ; dcmd -> header . data_len = ld_size ; dcmd -> header . scsi_status = 0 ; dcmd -> opcode = MFI_DCMD_LD_MAP_GET_INFO ; cmd -> cm_sg = & dcmd -> sgl ; cmd -> cm_total_frame_size = MFI_DCMD_FRAME_SIZE ; cmd -> cm_data = ld_sync ; cmd -> cm_private = ld_sync ; cmd -> cm_len = ld_size ; cmd -> cm_complete = mfi_sync_map_complete ; sc -> mfi_map_sync_cm = cmd ; cmd -> cm_flags = MFI_CMD_DATAOUT ; cmd -> cm_frame -> dcmd . mbox [ 0 ] = list -> ld_count ; cmd -> cm_frame -> dcmd . mbox [ 1 ] = MFI_DCMD_MBOX_PEND_FLAG ; if ( ( error = mfi_mapcmd ( sc , cmd ) ) != 0 ) { device_printf ( sc -> mfi_dev , " \n " ) ; free ( ld_sync , M_MFIBUF ) ; sc -> mfi_map_sync_cm = NULL ; mfi_release_command ( cmd ) ; goto out ; } out : if ( list ) free ( list , M_MFIBUF ) ; if ( cm ) mfi_release_command ( cm ) ; }
The ThunderBolt HW has an option for the driver to directly
 access the underlying disks and operate on the RAID.
The ThunderBolt HW has an option for the driver to directly access the underlying disks and operate on the RAID .