raid 3108 的初始化

3108 raid卡驱动路径在drivers/scsi/megaraid。其入口函数在megaraid_sas_base.c中megasas_init。
从megasas_init中可以看到首先注册了一个字符设备,用于从user space控制raid卡
	/*
	 * Register character device node
	 */
	rval = register_chrdev(0, "megaraid_sas_ioctl", &megasas_mgmt_fops);

	if (rval < 0) {
		printk(KERN_DEBUG "megasas: failed to open device node\n");
		return rval;
	}
其次会注册一个pci driver
	/*
	 * Register ourselves as PCI hotplug module
	 */
	rval = pci_register_driver(&megasas_pci_driver);

	if (rval) {
		printk(KERN_DEBUG "megasas: PCI hotplug registration failed \n");
		goto err_pcidrv;
	}

最终会调用这个pci设备的probe函数megasas_probe_one
在megasas_probe_one 中会调用会这个pcie设备做一些初始化,然后会调用megasas_io_attach 来向SCSI mid-layer注册host
	/*
	 * Register with SCSI mid-layer
	 */
	if (megasas_io_attach(instance))
		goto fail_io_attach;

megasas_io_attach 源码如下:
static int megasas_io_attach(struct megasas_instance *instance)
{
	struct Scsi_Host *host = instance->host;

	/*
	 * Notify the mid-layer about the new controller
	 */
	if (scsi_add_host(host, &instance->pdev->dev)) {
		dev_err(&instance->pdev->dev,
			"Failed to add host from %s %d\n",
			__func__, __LINE__);
		return -ENODEV;
	}

	return 0;
}
调用scsi_add_host 添加host后就调用
	 * Trigger SCSI to scan our drives
	 */
	scsi_scan_host(host);

来scan。
所以对kernel来说3108 raid卡就是一个硬盘的控制器
其次在3108 驱动中通过一定的格式和fw通讯
3108总共支持的命令如下:
/*
 * MFI command opcodes
 */
#define MFI_CMD_INIT				0x00
#define MFI_CMD_LD_READ				0x01
#define MFI_CMD_LD_WRITE			0x02
#define MFI_CMD_LD_SCSI_IO			0x03
#define MFI_CMD_PD_SCSI_IO			0x04
#define MFI_CMD_DCMD				0x05
#define MFI_CMD_ABORT				0x06
#define MFI_CMD_SMP				0x07
#define MFI_CMD_STP				0x08
#define MFI_CMD_INVALID				0xff


这里以发送MFI_CMD_DCMD命令且操作数为MR_DCMD_LD_LIST_QUERY为例
static int
megasas_ld_list_query(struct megasas_instance *instance, u8 query_type)
{
	int ret = 0, ld_index = 0, ids = 0;
	struct megasas_cmd *cmd;
	struct megasas_dcmd_frame *dcmd;
	struct MR_LD_TARGETID_LIST *ci;
	dma_addr_t ci_h = 0;
	u32 tgtid_count;
//得到一个需要保存发送命令的struct megasas_cmd *cmd
	cmd = megasas_get_cmd(instance);

	if (!cmd) {
		dev_warn(&instance->pdev->dev,
		         "megasas_ld_list_query: Failed to get cmd\n");
		return -ENOMEM;
	}

	dcmd = &cmd->frame->dcmd;

	ci = pci_alloc_consistent(instance->pdev,
				  sizeof(struct MR_LD_TARGETID_LIST), &ci_h);

	if (!ci) {
		dev_warn(&instance->pdev->dev,
		         "Failed to alloc mem for ld_list_query\n");
		megasas_return_cmd(instance, cmd);
		return -ENOMEM;
	}

	memset(ci, 0, sizeof(*ci));
	memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);

	dcmd->mbox.b[0] = query_type;
	if (instance->supportmax256vd)
		dcmd->mbox.b[2] = 1;

//初始化需要发送的命令
	dcmd->cmd = MFI_CMD_DCMD;
	dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
	dcmd->sge_count = 1;
	dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ);
	dcmd->timeout = 0;
	dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_LD_TARGETID_LIST));
	dcmd->opcode = cpu_to_le32(MR_DCMD_LD_LIST_QUERY);
	dcmd->sgl.sge32[0].phys_addr = cpu_to_le32(ci_h);
	dcmd->sgl.sge32[0].length = cpu_to_le32(sizeof(struct MR_LD_TARGETID_LIST));
	dcmd->pad_0  = 0;
//发送命令有两种,一种是调用megasas_issue_blocked_cmd 这个函数会采用等待队列得到fw对这个命令的回馈,期间这个进程可能会sleep,另外一种是megasas_issue_polled 会一直poll,直到结果返回
	if (instance->ctrl_context && !instance->mask_interrupts)
		ret = megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS);
	else
		ret = megasas_issue_polled(instance, cmd);

下面这个switch 是执行命令反馈的结果,有三种,DCMD_FAILED/DCMD_TIMEOUT/DCMD_SUCCESS ,正常case应该是DCMD_SUCCESS
	switch (ret) {
	case DCMD_FAILED:
		dev_info(&instance->pdev->dev,
			"DCMD not supported by firmware - %s %d\n",
				__func__, __LINE__);
		ret = megasas_get_ld_list(instance);
		break;
	case DCMD_TIMEOUT:
		switch (dcmd_timeout_ocr_possible(instance)) {
		case INITIATE_OCR:
			cmd->flags |= DRV_DCMD_SKIP_REFIRE;
			/*
			 * DCMD failed from AEN path.
			 * AEN path already hold reset_mutex to avoid PCI access
			 * while OCR is in progress.
			 */
			mutex_unlock(&instance->reset_mutex);
			megasas_reset_fusion(instance->host,
						MFI_IO_TIMEOUT_OCR);
			mutex_lock(&instance->reset_mutex);
			break;
		case KILL_ADAPTER:
			megaraid_sas_kill_hba(instance);
			break;
		case IGNORE_TIMEOUT:
			dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n",
				__func__, __LINE__);
			break;
		}

		break;
	case DCMD_SUCCESS:
		tgtid_count = le32_to_cpu(ci->count);

		if ((tgtid_count > (instance->fw_supported_vd_count)))
			break;

		memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
		for (ld_index = 0; ld_index < tgtid_count; ld_index++) {
			ids = ci->targetId[ld_index];
			instance->ld_ids[ids] = ci->targetId[ld_index];
		}

		break;
	}

	pci_free_consistent(instance->pdev, sizeof(struct MR_LD_TARGETID_LIST),
		    ci, ci_h);
//执行结果是ok的话,需要将之前申请的cmd返回。
	if (ret != DCMD_TIMEOUT)
		megasas_return_cmd(instance, cmd);

	return ret;
}

每次3108在发送命令前都会通过megasas_get_cmd得到一个空闲的命令
struct megasas_cmd *megasas_get_cmd(struct megasas_instance
						  *instance)
{
	unsigned long flags;
	struct megasas_cmd *cmd = NULL;

	spin_lock_irqsave(&instance->mfi_pool_lock, flags);

	if (!list_empty(&instance->cmd_pool)) {
		cmd = list_entry((&instance->cmd_pool)->next,
				 struct megasas_cmd, list);
		list_del_init(&cmd->list);
	} else {
		dev_err(&instance->pdev->dev, "Command pool empty!\n");
	}

	spin_unlock_irqrestore(&instance->mfi_pool_lock, flags);
	return cmd;
}
可以知道挂在instance->cmd_pool 这个pool上的cmd都是空闲的,如果要人要用的话,就从instance->cmd_pool 中找到地一个空闲的,并从instance->cmd_pool 中删掉
与之对应的就是执行完成命令后将cmd返回到instance->cmd_pool这个list中
void
megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
{
	unsigned long flags;
	u32 blk_tags;
	struct megasas_cmd_fusion *cmd_fusion;
	struct fusion_context *fusion = instance->ctrl_context;

	/* This flag is used only for fusion adapter.
	 * Wait for Interrupt for Polled mode DCMD
	 */
	if (cmd->flags & DRV_DCMD_POLLED_MODE)
		return;

	spin_lock_irqsave(&instance->mfi_pool_lock, flags);
//假定fusion为null的话,可以看到这个函数做的事情和megasas_get_cmd 刚好像相反
	if (fusion) {
		blk_tags = instance->max_scsi_cmds + cmd->index;
		cmd_fusion = fusion->cmd_list[blk_tags];
		megasas_return_cmd_fusion(instance, cmd_fusion);
	}
	cmd->scmd = NULL;
	cmd->frame_count = 0;
	cmd->flags = 0;
	memset(cmd->frame, 0, instance->mfi_frame_size);
	cmd->frame->io.context = cpu_to_le32(cmd->index);
	if (!fusion && reset_devices)
		cmd->frame->hdr.cmd = MFI_CMD_INVALID;
	list_add(&cmd->list, (&instance->cmd_pool)->next);

	spin_unlock_irqrestore(&instance->mfi_pool_lock, flags);

}



你可能感兴趣的:(Linux,源码分析)