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);
}
raid 3108 的初始化
最新推荐文章于 2023-08-30 11:00:42 发布