NVMe Linux驱动系列一:host端[fc.c]<83>

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)标记为非活动状态。以下是该函数的主要内容:

  1. 从传入的控制器(ctrl)中获取与之关联的远程端口(rport)和本地端口(lport)。
  2. 使用原子操作 atomic_dec_return 减少远程端口的活动控制器计数,并将减少后的值保存在 cnt 中。
  3. 如果活动控制器计数 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 连接中创建关联。以下是该函数的主要内容:

  1. 增加控制器(ctrl)的重新连接次数计数。
  2. 检查远程端口的状态,如果不是 FC_OBJSTATE_ONLINE,则返回错误码 -ENODEV。
  3. 使用原子操作 nvme_fc_ctlr_active_on_rport 将控制器标记为活动状态,并检查返回值是否为 -ENOTUNIQ,表示控制器已经标记为活动状态。
  4. 打印有关创建关联的日志信息,包括本地端口和远程端口的 WWPN 以及 NQN。
  5. 清除控制器的 ASSOC_FAILED 标志。
  6. 创建管理队列,包括硬件队列的创建、管理队列的连接以及与 NVMe 协议相关的连接。
  7. 设置 NVME_FC_Q_LIVE 标志,表示管理队列已经建立。
  8. 检查控制器的功能。
  9. 启用控制器。
  10. 更新控制器的最大段数和最大硬件扇区数。
  11. 解锁管理队列。
  12. 初始化控制器的异步事件操作。
  13. 如果控制器的队列数大于 1,则根据 ioq_live 标志创建或重新创建 IO 队列。
  14. 获取锁,并根据控制器状态的变化更新控制器状态为 NVME_CTRL_LIVE,并启动控制器。
  15. 清零控制器的重新连接次数。
  16. 如果状态有变化,启动控制器。
  17. 如果创建关联失败,执行回滚操作,包括终止异步事件操作、发送断开关联的 LS(Link Service)消息、删除硬件队列、释放队列资源、清除 ASSOC_ACTIVE 标志以及标记控制器为非活动状态。
  18. 返回操作结果。

该函数的作用是在 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 连接中的关联。以下是该函数的主要内容:

  1. 检查 ASSOC_ACTIVE 标志,如果未设置,则直接返回,表示关联未处于活动状态。
  2. 获取控制器的锁,设置 FCCTRL_TERMIO 标志,将 iocnt 计数清零,并释放锁。
  3. 使用函数 __nvme_fc_abort_outstanding_ios 中断所有未完成的 I/O 操作。
  4. 中断异步事件操作。
  5. 等待所有被中断的 I/O 操作完成,通过等待 ioabort_wait 事件。
  6. 终止异步事件操作。
  7. 如果关联 ID 存在(非零),发送断开关联的 LS(Link Service)消息,即将关联终止。
  8. 获取控制器的锁,清零关联 ID,保存可能的接收断开请求的操作(disls),并清空接收断开请求的操作指针。
  9. 如果存在接收的断开请求操作(disls),发送断开请求的响应。
  10. 如果控制器的标签集存在,则删除 IO 队列,释放 IO 队列资源,并删除管理队列。
  11. 删除管理队列资源。
  12. 解除管理队列和 IO 队列的暂停状态,以便后续的操作可以快速失败。
  13. 标记控制器为非活动状态。
  14. 返回。没有明确的返回值,因为该函数执行的是资源释放和清理操作。

该函数用于删除 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 控制器。以下是该函数的主要内容:

  1. 从参数 nctrl 中获取 nvme_fc_ctrl 结构体的指针。
  2. 使用函数 cancel_work_synccancel_delayed_work_sync 取消正在执行的工作项(work items)和延迟工作项(delayed work items),包括 ioerr_workconnect_work
  3. 调用函数 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 控制器的重新连接或删除逻辑。以下是函数的主要逻辑:

  1. 从参数 ctrl 中获取 nvme_fc_ctrl 结构体的指针。
  2. 如果控制器的状态不是 NVME_CTRL_CONNECTING,则直接返回,不执行任何操作。
  3. 判断远程端口的状态和当前的时间,来决定是否尝试重新连接。
    • 如果远程端口的状态是 FC_OBJSTATE_ONLINE,并且连接尝试失败(status > 0 并且包含 NVME_SC_DNR 错误码),则不再尝试重新连接。
    • 如果当前时间超过了 rport->dev_loss_end,则不再尝试重新连接。
  4. 根据重新连接的判定,计算下一次重新连接的延迟时间 recon_delay
  5. 如果需要尝试重新连接,将重新连接的工作项 connect_work 推入工作队列,以在 recon_delay 秒后执行重新连接操作。
  6. 如果不需要重新连接或者最大的重新连接尝试次数已达到,则在控制台打印相应的警告信息,并调用 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 控制器的复位操作。以下是函数的主要逻辑:

  1. 从参数 work 中获取 nvme_fc_ctrl 结构体的指针。
  2. 停止 NVMe 控制器的操作,调用 nvme_stop_ctrl 函数停止控制器的运行。
  3. 调用 nvme_fc_delete_association 函数删除控制器的关联。此操作将会等待正在进行的 IO 终止。
  4. 尝试将控制器的状态切换为 NVME_CTRL_CONNECTING,如果切换失败,在控制台打印错误信息。
  5. 如果远程端口的状态是 FC_OBJSTATE_ONLINE,则尝试重新连接控制器:
    • 调用 queue_delayed_work 将重新连接的工作项 connect_work 推入工作队列立即执行,或者在需要的情况下立即执行工作项(通过 flush_delayed_work)。
  6. 如果远程端口的状态不是 FC_OBJSTATE_ONLINE,则调用 nvme_fc_reconnect_or_delete 函数,处理重新连接或删除控制器的逻辑,传递错误码 -ENOTCONN

总之,这个函数用于处理 NVMe FC 控制器的复位操作。它停止控制器的操作,删除控制器的关联,尝试重新连接控制器,或者根据情况处理重新连接或删除的逻辑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值