nvme_fc_ctlr_inactive_on_rport
static int
nvme_fc_ctlr_inactive_on_rport(struct nvme_fc_ctrl *ctrl)
{
struct nvme_fc_rport *rport = ctrl->rport;
struct nvme_fc_lport *lport = rport->lport;
u32 cnt;
/* clearing of ctrl->flags ASSOC_ACTIVE bit is in association delete */
cnt = atomic_dec_return(&rport->act_ctrl_cnt);
if (cnt == 0) {
if (rport->remoteport.port_state == FC_OBJSTATE_DELETED)
lport->ops->remoteport_delete(&rport->remoteport);
nvme_fc_rport_inactive_on_lport(rport);
}
return 0;
}
这段代码定义了一个函数 nvme_fc_ctlr_inactive_on_rport
,用于在 NVMe FC 连接中将控制器(ctrl)标记为非活动状态。以下是该函数的主要内容:
- 从传入的控制器(ctrl)中获取与之关联的远程端口(rport)和本地端口(lport)。
- 使用原子操作
atomic_dec_return
减少远程端口的活动控制器计数,并将减少后的值保存在cnt
中。 - 如果活动控制器计数
cnt
变为 0,表示已经没有活动的控制器,执行以下操作:- 检查远程端口的状态是否为
FC_OBJSTATE_DELETED
,如果是,则调用本地端口的remoteport_delete
函数来删除远程端口。 - 调用函数
nvme_fc_rport_inactive_on_lport
将远程端口标记为非活动状态,并在没有活动控制器时检查本地端口的状态,如果本地端口的状态也为FC_OBJSTATE_DELETED
,则调用本地端口的localport_delete
函数来删除本地端口。
- 检查远程端口的状态是否为
这个函数的作用是在 NVMe FC 连接中标记控制器为非活动状态,并维护与远程端口和本地端口的关联信息。在没有活动的控制器时,可能需要删除相关的端口。函数返回值为 0 表示控制器被成功标记为非活动状态。
nvme_fc_create_association
/*
* This routine restarts the controller on the host side, and
* on the link side, recreates the controller association.
*/
static int
nvme_fc_create_association(struct nvme_fc_ctrl *ctrl)
{
struct nvmf_ctrl_options *opts = ctrl->ctrl.opts;
struct nvmefc_ls_rcv_op *disls = NULL;
unsigned long flags;
int ret;
bool changed;
++ctrl->ctrl.nr_reconnects;
if (ctrl->rport->remoteport.port_state != FC_OBJSTATE_ONLINE)
return -ENODEV;
if (nvme_fc_ctlr_active_on_rport(ctrl))
return -ENOTUNIQ;
dev_info(ctrl->ctrl.device,
"NVME-FC{%d}: create association : host wwpn 0x%016llx "
" rport wwpn 0x%016llx: NQN \"%s\"\n",
ctrl->cnum, ctrl->lport->localport.port_name,
ctrl->rport->remoteport.port_name, ctrl->ctrl.opts->subsysnqn);
clear_bit(ASSOC_FAILED, &ctrl->flags);
/*
* Create the admin queue
*/
ret = __nvme_fc_create_hw_queue(ctrl, &ctrl->queues[0], 0,
NVME_AQ_DEPTH);
if (ret)
goto out_free_queue;
ret = nvme_fc_connect_admin_queue(ctrl, &ctrl->queues[0],
NVME_AQ_DEPTH, (NVME_AQ_DEPTH / 4));
if (ret)
goto out_delete_hw_queue;
ret = nvmf_connect_admin_queue(&ctrl->ctrl);
if (ret)
goto out_disconnect_admin_queue;
set_bit(NVME_FC_Q_LIVE, &ctrl->queues[0].flags);
/*
* Check controller capabilities
*
* todo:- add code to check if ctrl attributes changed from
* prior connection values
*/
ret = nvme_enable_ctrl(&ctrl->ctrl);
if (!ret && test_bit(ASSOC_FAILED, &ctrl->flags))
ret = -EIO;
if (ret)
goto out_disconnect_admin_queue;
ctrl->ctrl.max_segments = ctrl->lport->ops->max_sgl_segments;
ctrl->ctrl.max_hw_sectors = ctrl->ctrl.max_segments <<
(ilog2(SZ_4K) - 9);
nvme_unquiesce_admin_queue(&ctrl->ctrl);
ret = nvme_init_ctrl_finish(&ctrl->ctrl, false);
if (!ret && test_bit(ASSOC_FAILED, &ctrl->flags))
ret = -EIO;
if (ret)
goto out_disconnect_admin_queue;
/* sanity checks */
/* FC-NVME does not have other data in the capsule */
if (ctrl->ctrl.icdoff) {
dev_err(ctrl->ctrl.device, "icdoff %d is not supported!\n",
ctrl->ctrl.icdoff);
ret = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
goto out_disconnect_admin_queue;
}
/* FC-NVME supports normal SGL Data Block Descriptors */
if (!nvme_ctrl_sgl_supported(&ctrl->ctrl)) {
dev_err(ctrl->ctrl.device,
"Mandatory sgls are not supported!\n");
ret = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
goto out_disconnect_admin_queue;
}
if (opts->queue_size > ctrl->ctrl.maxcmd) {
/* warn if maxcmd is lower than queue_size */
dev_warn(ctrl->ctrl.device,
"queue_size %zu > ctrl maxcmd %u, reducing "
"to maxcmd\n",
opts->queue_size, ctrl->ctrl.maxcmd);
opts->queue_size = ctrl->ctrl.maxcmd;
ctrl->ctrl.sqsize = opts->queue_size - 1;
}
ret = nvme_fc_init_aen_ops(ctrl);
if (ret)
goto out_term_aen_ops;
/*
* Create the io queues
*/
if (ctrl->ctrl.queue_count > 1) {
if (!ctrl->ioq_live)
ret = nvme_fc_create_io_queues(ctrl);
else
ret = nvme_fc_recreate_io_queues(ctrl);
}
spin_lock_irqsave(&ctrl->lock, flags);
if (!ret && test_bit(ASSOC_FAILED, &ctrl->flags))
ret = -EIO;
if (ret) {
spin_unlock_irqrestore(&ctrl->lock, flags);
goto out_term_aen_ops;
}
changed = nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_LIVE);
spin_unlock_irqrestore(&ctrl->lock, flags);
ctrl->ctrl.nr_reconnects = 0;
if (changed)
nvme_start_ctrl(&ctrl->ctrl);
return 0; /* Success */
out_term_aen_ops:
nvme_fc_term_aen_ops(ctrl);
out_disconnect_admin_queue:
dev_warn(ctrl->ctrl.device,
"NVME-FC{%d}: create_assoc failed, assoc_id %llx ret %d\n",
ctrl->cnum, ctrl->association_id, ret);
/* send a Disconnect(association) LS to fc-nvme target */
nvme_fc_xmt_disconnect_assoc(ctrl);
spin_lock_irqsave(&ctrl->lock, flags);
ctrl->association_id = 0;
disls = ctrl->rcv_disconn;
ctrl->rcv_disconn = NULL;
spin_unlock_irqrestore(&ctrl->lock, flags);
if (disls)
nvme_fc_xmt_ls_rsp(disls);
out_delete_hw_queue:
__nvme_fc_delete_hw_queue(ctrl, &ctrl->queues[0], 0);
out_free_queue:
nvme_fc_free_queue(&ctrl->queues[0]);
clear_bit(ASSOC_ACTIVE, &ctrl->flags);
nvme_fc_ctlr_inactive_on_rport(ctrl);
return ret;
}
这段代码定义了一个名为 nvme_fc_create_association
的函数,用于在 NVMe FC 连接中创建关联。以下是该函数的主要内容:
- 增加控制器(ctrl)的重新连接次数计数。
- 检查远程端口的状态,如果不是
FC_OBJSTATE_ONLINE
,则返回错误码 -ENODEV。 - 使用原子操作
nvme_fc_ctlr_active_on_rport
将控制器标记为活动状态,并检查返回值是否为 -ENOTUNIQ,表示控制器已经标记为活动状态。 - 打印有关创建关联的日志信息,包括本地端口和远程端口的 WWPN 以及 NQN。
- 清除控制器的
ASSOC_FAILED
标志。 - 创建管理队列,包括硬件队列的创建、管理队列的连接以及与 NVMe 协议相关的连接。
- 设置
NVME_FC_Q_LIVE
标志,表示管理队列已经建立。 - 检查控制器的功能。
- 启用控制器。
- 更新控制器的最大段数和最大硬件扇区数。
- 解锁管理队列。
- 初始化控制器的异步事件操作。
- 如果控制器的队列数大于 1,则根据
ioq_live
标志创建或重新创建 IO 队列。 - 获取锁,并根据控制器状态的变化更新控制器状态为
NVME_CTRL_LIVE
,并启动控制器。 - 清零控制器的重新连接次数。
- 如果状态有变化,启动控制器。
- 如果创建关联失败,执行回滚操作,包括终止异步事件操作、发送断开关联的 LS(Link Service)消息、删除硬件队列、释放队列资源、清除
ASSOC_ACTIVE
标志以及标记控制器为非活动状态。 - 返回操作结果。
该函数的作用是在 NVMe FC 连接中创建关联,涵盖了连接的初始化、队列的创建、控制器的初始化和启动等操作,并在失败时执行回滚操作,确保关联创建的完整性。函数返回值为 0 表示关联创建成功。
nvme_fc_delete_association
/*
* This routine stops operation of the controller on the host side.
* On the host os stack side: Admin and IO queues are stopped,
* outstanding ios on them terminated via FC ABTS.
* On the link side: the association is terminated.
*/
static void
nvme_fc_delete_association(struct nvme_fc_ctrl *ctrl)
{
struct nvmefc_ls_rcv_op *disls = NULL;
unsigned long flags;
if (!test_and_clear_bit(ASSOC_ACTIVE, &ctrl->flags))
return;
spin_lock_irqsave(&ctrl->lock, flags);
set_bit(FCCTRL_TERMIO, &ctrl->flags);
ctrl->iocnt = 0;
spin_unlock_irqrestore(&ctrl->lock, flags);
__nvme_fc_abort_outstanding_ios(ctrl, false);
/* kill the aens as they are a separate path */
nvme_fc_abort_aen_ops(ctrl);
/* wait for all io that had to be aborted */
spin_lock_irq(&ctrl->lock);
wait_event_lock_irq(ctrl->ioabort_wait, ctrl->iocnt == 0, ctrl->lock);
clear_bit(FCCTRL_TERMIO, &ctrl->flags);
spin_unlock_irq(&ctrl->lock);
nvme_fc_term_aen_ops(ctrl);
/*
* send a Disconnect(association) LS to fc-nvme target
* Note: could have been sent at top of process, but
* cleaner on link traffic if after the aborts complete.
* Note: if association doesn't exist, association_id will be 0
*/
if (ctrl->association_id)
nvme_fc_xmt_disconnect_assoc(ctrl);
spin_lock_irqsave(&ctrl->lock, flags);
ctrl->association_id = 0;
disls = ctrl->rcv_disconn;
ctrl->rcv_disconn = NULL;
spin_unlock_irqrestore(&ctrl->lock, flags);
if (disls)
/*
* if a Disconnect Request was waiting for a response, send
* now that all ABTS's have been issued (and are complete).
*/
nvme_fc_xmt_ls_rsp(disls);
if (ctrl->ctrl.tagset) {
nvme_fc_delete_hw_io_queues(ctrl);
nvme_fc_free_io_queues(ctrl);
}
__nvme_fc_delete_hw_queue(ctrl, &ctrl->queues[0], 0);
nvme_fc_free_queue(&ctrl->queues[0]);
/* re-enable the admin_q so anything new can fast fail */
nvme_unquiesce_admin_queue(&ctrl->ctrl);
/* resume the io queues so that things will fast fail */
nvme_unquiesce_io_queues(&ctrl->ctrl);
nvme_fc_ctlr_inactive_on_rport(ctrl);
}
这段代码定义了一个名为 nvme_fc_delete_association
的函数,用于删除 NVMe FC 连接中的关联。以下是该函数的主要内容:
- 检查
ASSOC_ACTIVE
标志,如果未设置,则直接返回,表示关联未处于活动状态。 - 获取控制器的锁,设置
FCCTRL_TERMIO
标志,将iocnt
计数清零,并释放锁。 - 使用函数
__nvme_fc_abort_outstanding_ios
中断所有未完成的 I/O 操作。 - 中断异步事件操作。
- 等待所有被中断的 I/O 操作完成,通过等待
ioabort_wait
事件。 - 终止异步事件操作。
- 如果关联 ID 存在(非零),发送断开关联的 LS(Link Service)消息,即将关联终止。
- 获取控制器的锁,清零关联 ID,保存可能的接收断开请求的操作(disls),并清空接收断开请求的操作指针。
- 如果存在接收的断开请求操作(disls),发送断开请求的响应。
- 如果控制器的标签集存在,则删除 IO 队列,释放 IO 队列资源,并删除管理队列。
- 删除管理队列资源。
- 解除管理队列和 IO 队列的暂停状态,以便后续的操作可以快速失败。
- 标记控制器为非活动状态。
- 返回。没有明确的返回值,因为该函数执行的是资源释放和清理操作。
该函数用于删除 NVMe FC 连接中的关联,包括中断未完成的 I/O 操作、终止异步事件操作、发送断开关联消息以及释放关联相关的资源。函数在连接终止后确保所有相关资源得到适当的清理和释放。
nvme_fc_delete_ctrl
static void
nvme_fc_delete_ctrl(struct nvme_ctrl *nctrl)
{
struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl);
cancel_work_sync(&ctrl->ioerr_work);
cancel_delayed_work_sync(&ctrl->connect_work);
/*
* kill the association on the link side. this will block
* waiting for io to terminate
*/
nvme_fc_delete_association(ctrl);
}
这段代码定义了一个名为 nvme_fc_delete_ctrl
的函数,用于删除 NVMe 控制器。以下是该函数的主要内容:
- 从参数
nctrl
中获取nvme_fc_ctrl
结构体的指针。 - 使用函数
cancel_work_sync
和cancel_delayed_work_sync
取消正在执行的工作项(work items)和延迟工作项(delayed work items),包括ioerr_work
和connect_work
。 - 调用函数
nvme_fc_delete_association
删除 NVMe FC 关联,即终止关联并释放相关资源。这一步将等待所有的 I/O 操作完成,并在关联被终止后清理资源。
总之,这个函数用于删除 NVMe 控制器,包括取消正在执行的工作项、终止关联并释放相关资源。在删除控制器之前,需要确保所有相关资源都得到适当的清理和释放。
nvme_fc_reconnect_or_delete
static void
nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status)
{
struct nvme_fc_rport *rport = ctrl->rport;
struct nvme_fc_remote_port *portptr = &rport->remoteport;
unsigned long recon_delay = ctrl->ctrl.opts->reconnect_delay * HZ;
bool recon = true;
if (ctrl->ctrl.state != NVME_CTRL_CONNECTING)
return;
if (portptr->port_state == FC_OBJSTATE_ONLINE) {
dev_info(ctrl->ctrl.device,
"NVME-FC{%d}: reset: Reconnect attempt failed (%d)\n",
ctrl->cnum, status);
if (status > 0 && (status & NVME_SC_DNR))
recon = false;
} else if (time_after_eq(jiffies, rport->dev_loss_end))
recon = false;
if (recon && nvmf_should_reconnect(&ctrl->ctrl)) {
if (portptr->port_state == FC_OBJSTATE_ONLINE)
dev_info(ctrl->ctrl.device,
"NVME-FC{%d}: Reconnect attempt in %ld "
"seconds\n",
ctrl->cnum, recon_delay / HZ);
else if (time_after(jiffies + recon_delay, rport->dev_loss_end))
recon_delay = rport->dev_loss_end - jiffies;
queue_delayed_work(nvme_wq, &ctrl->connect_work, recon_delay);
} else {
if (portptr->port_state == FC_OBJSTATE_ONLINE) {
if (status > 0 && (status & NVME_SC_DNR))
dev_warn(ctrl->ctrl.device,
"NVME-FC{%d}: reconnect failure\n",
ctrl->cnum);
else
dev_warn(ctrl->ctrl.device,
"NVME-FC{%d}: Max reconnect attempts "
"(%d) reached.\n",
ctrl->cnum, ctrl->ctrl.nr_reconnects);
} else
dev_warn(ctrl->ctrl.device,
"NVME-FC{%d}: dev_loss_tmo (%d) expired "
"while waiting for remoteport connectivity.\n",
ctrl->cnum, min_t(int, portptr->dev_loss_tmo,
(ctrl->ctrl.opts->max_reconnects *
ctrl->ctrl.opts->reconnect_delay)));
WARN_ON(nvme_delete_ctrl(&ctrl->ctrl));
}
}
这段代码实现了一个名为 nvme_fc_reconnect_or_delete
的函数,用于处理 NVMe FC 控制器的重新连接或删除逻辑。以下是函数的主要逻辑:
- 从参数
ctrl
中获取nvme_fc_ctrl
结构体的指针。 - 如果控制器的状态不是
NVME_CTRL_CONNECTING
,则直接返回,不执行任何操作。 - 判断远程端口的状态和当前的时间,来决定是否尝试重新连接。
- 如果远程端口的状态是
FC_OBJSTATE_ONLINE
,并且连接尝试失败(status > 0
并且包含NVME_SC_DNR
错误码),则不再尝试重新连接。 - 如果当前时间超过了
rport->dev_loss_end
,则不再尝试重新连接。
- 如果远程端口的状态是
- 根据重新连接的判定,计算下一次重新连接的延迟时间
recon_delay
。 - 如果需要尝试重新连接,将重新连接的工作项
connect_work
推入工作队列,以在recon_delay
秒后执行重新连接操作。 - 如果不需要重新连接或者最大的重新连接尝试次数已达到,则在控制台打印相应的警告信息,并调用
nvme_delete_ctrl
函数删除 NVMe 控制器。
总之,这个函数用于处理 NVMe FC 控制器的重新连接或删除逻辑。它根据控制器的状态、远程端口的状态以及时间来判断是否进行重新连接,并在一定条件下执行重新连接操作或者删除控制器。如果重新连接尝试失败达到最大次数,或者不需要进行重新连接,则会删除控制器。
nvme_fc_reset_ctrl_work
nvme_fc_reset_ctrl_work(struct work_struct *work)
{
struct nvme_fc_ctrl *ctrl =
container_of(work, struct nvme_fc_ctrl, ctrl.reset_work);
nvme_stop_ctrl(&ctrl->ctrl);
/* will block will waiting for io to terminate */
nvme_fc_delete_association(ctrl);
if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING))
dev_err(ctrl->ctrl.device,
"NVME-FC{%d}: error_recovery: Couldn't change state "
"to CONNECTING\n", ctrl->cnum);
if (ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE) {
if (!queue_delayed_work(nvme_wq, &ctrl->connect_work, 0)) {
dev_err(ctrl->ctrl.device,
"NVME-FC{%d}: failed to schedule connect "
"after reset\n", ctrl->cnum);
} else {
flush_delayed_work(&ctrl->connect_work);
}
} else {
nvme_fc_reconnect_or_delete(ctrl, -ENOTCONN);
}
}
这段代码实现了一个名为 nvme_fc_reset_ctrl_work
的函数,用于处理 NVMe FC 控制器的复位操作。以下是函数的主要逻辑:
- 从参数
work
中获取nvme_fc_ctrl
结构体的指针。 - 停止 NVMe 控制器的操作,调用
nvme_stop_ctrl
函数停止控制器的运行。 - 调用
nvme_fc_delete_association
函数删除控制器的关联。此操作将会等待正在进行的 IO 终止。 - 尝试将控制器的状态切换为
NVME_CTRL_CONNECTING
,如果切换失败,在控制台打印错误信息。 - 如果远程端口的状态是
FC_OBJSTATE_ONLINE
,则尝试重新连接控制器:- 调用
queue_delayed_work
将重新连接的工作项connect_work
推入工作队列立即执行,或者在需要的情况下立即执行工作项(通过flush_delayed_work
)。
- 调用
- 如果远程端口的状态不是
FC_OBJSTATE_ONLINE
,则调用nvme_fc_reconnect_or_delete
函数,处理重新连接或删除控制器的逻辑,传递错误码-ENOTCONN
。
总之,这个函数用于处理 NVMe FC 控制器的复位操作。它停止控制器的操作,删除控制器的关联,尝试重新连接控制器,或者根据情况处理重新连接或删除的逻辑。