mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-13 14:09:38 -04:00
Merge tag 'nvme-6.16-2025-05-20' of git://git.infradead.org/nvme into for-6.16/block
Pull NVMe updates from Christoph: "nvme updates for Linux 6.16 - add per-node DMA pools and use them for PRP/SGL allocations (Caleb Sander Mateos, Keith Busch) - nvme-fcloop refcounting fixes (Daniel Wagner) - support delayed removal of the multipath node and optionally support the multipath node for private namespaces (Nilay Shroff) - support shared CQs in the PCI endpoint target code (Wilfred Mallawa) - support admin-queue only authentication (Hannes Reinecke) - use the crc32c library instead of the crypto API (Eric Biggers) - misc cleanups (Christoph Hellwig, Marcelo Moreira, Hannes Reinecke, Leon Romanovsky, Gustavo A. R. Silva)" * tag 'nvme-6.16-2025-05-20' of git://git.infradead.org/nvme: (42 commits) nvme: rename nvme_mpath_shutdown_disk to nvme_mpath_remove_disk nvme: introduce multipath_always_on module param nvme-multipath: introduce delayed removal of the multipath head node nvme-pci: derive and better document max segments limits nvme-pci: use struct_size for allocation struct nvme_dev nvme-pci: add a symolic name for the small pool size nvme-pci: use a better encoding for small prp pool allocations nvme-pci: rename the descriptor pools nvme-pci: remove struct nvme_descriptor nvme-pci: store aborted state in flags variable nvme-pci: don't try to use SGLs for metadata on the admin queue nvme-pci: make PRP list DMA pools per-NUMA-node nvme-pci: factor out a nvme_init_hctx_common() helper dmapool: add NUMA affinity support nvme-fc: do not reference lsrsp after failure nvmet-fcloop: don't wait for lport cleanup nvmet-fcloop: add missing fcloop_callback_host_done nvmet-fc: take tgtport refs for portentry nvmet-fc: free pending reqs on tgtport unregister nvmet-fcloop: drop response if targetport is gone ...
This commit is contained in:
@@ -242,7 +242,7 @@ struct nvme_dhchap_key *nvme_auth_transform_key(
|
||||
{
|
||||
const char *hmac_name;
|
||||
struct crypto_shash *key_tfm;
|
||||
struct shash_desc *shash;
|
||||
SHASH_DESC_ON_STACK(shash, key_tfm);
|
||||
struct nvme_dhchap_key *transformed_key;
|
||||
int ret, key_len;
|
||||
|
||||
@@ -267,19 +267,11 @@ struct nvme_dhchap_key *nvme_auth_transform_key(
|
||||
if (IS_ERR(key_tfm))
|
||||
return ERR_CAST(key_tfm);
|
||||
|
||||
shash = kmalloc(sizeof(struct shash_desc) +
|
||||
crypto_shash_descsize(key_tfm),
|
||||
GFP_KERNEL);
|
||||
if (!shash) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_key;
|
||||
}
|
||||
|
||||
key_len = crypto_shash_digestsize(key_tfm);
|
||||
transformed_key = nvme_auth_alloc_key(key_len, key->hash);
|
||||
if (!transformed_key) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_shash;
|
||||
goto out_free_key;
|
||||
}
|
||||
|
||||
shash->tfm = key_tfm;
|
||||
@@ -299,15 +291,12 @@ struct nvme_dhchap_key *nvme_auth_transform_key(
|
||||
if (ret < 0)
|
||||
goto out_free_transformed_key;
|
||||
|
||||
kfree(shash);
|
||||
crypto_free_shash(key_tfm);
|
||||
|
||||
return transformed_key;
|
||||
|
||||
out_free_transformed_key:
|
||||
nvme_auth_free_key(transformed_key);
|
||||
out_free_shash:
|
||||
kfree(shash);
|
||||
out_free_key:
|
||||
crypto_free_shash(key_tfm);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ struct nvme_dhchap_queue_context {
|
||||
u32 s1;
|
||||
u32 s2;
|
||||
bool bi_directional;
|
||||
bool authenticated;
|
||||
u16 transaction;
|
||||
u8 status;
|
||||
u8 dhgroup_id;
|
||||
@@ -682,6 +683,7 @@ static void nvme_auth_reset_dhchap(struct nvme_dhchap_queue_context *chap)
|
||||
static void nvme_auth_free_dhchap(struct nvme_dhchap_queue_context *chap)
|
||||
{
|
||||
nvme_auth_reset_dhchap(chap);
|
||||
chap->authenticated = false;
|
||||
if (chap->shash_tfm)
|
||||
crypto_free_shash(chap->shash_tfm);
|
||||
if (chap->dh_tfm)
|
||||
@@ -930,12 +932,14 @@ static void nvme_queue_auth_work(struct work_struct *work)
|
||||
}
|
||||
if (!ret) {
|
||||
chap->error = 0;
|
||||
chap->authenticated = true;
|
||||
if (ctrl->opts->concat &&
|
||||
(ret = nvme_auth_secure_concat(ctrl, chap))) {
|
||||
dev_warn(ctrl->device,
|
||||
"%s: qid %d failed to enable secure concatenation\n",
|
||||
__func__, chap->qid);
|
||||
chap->error = ret;
|
||||
chap->authenticated = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1023,13 +1027,16 @@ static void nvme_ctrl_auth_work(struct work_struct *work)
|
||||
return;
|
||||
|
||||
for (q = 1; q < ctrl->queue_count; q++) {
|
||||
ret = nvme_auth_negotiate(ctrl, q);
|
||||
if (ret) {
|
||||
dev_warn(ctrl->device,
|
||||
"qid %d: error %d setting up authentication\n",
|
||||
q, ret);
|
||||
break;
|
||||
}
|
||||
struct nvme_dhchap_queue_context *chap =
|
||||
&ctrl->dhchap_ctxs[q];
|
||||
/*
|
||||
* Skip re-authentication if the queue had
|
||||
* not been authenticated initially.
|
||||
*/
|
||||
if (!chap->authenticated)
|
||||
continue;
|
||||
cancel_work_sync(&chap->auth_work);
|
||||
queue_work(nvme_auth_wq, &chap->auth_work);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1037,7 +1044,13 @@ static void nvme_ctrl_auth_work(struct work_struct *work)
|
||||
* the controller terminates the connection.
|
||||
*/
|
||||
for (q = 1; q < ctrl->queue_count; q++) {
|
||||
ret = nvme_auth_wait(ctrl, q);
|
||||
struct nvme_dhchap_queue_context *chap =
|
||||
&ctrl->dhchap_ctxs[q];
|
||||
if (!chap->authenticated)
|
||||
continue;
|
||||
flush_work(&chap->auth_work);
|
||||
ret = chap->error;
|
||||
nvme_auth_reset_dhchap(chap);
|
||||
if (ret)
|
||||
dev_warn(ctrl->device,
|
||||
"qid %d: authentication failed\n", q);
|
||||
@@ -1076,6 +1089,7 @@ int nvme_auth_init_ctrl(struct nvme_ctrl *ctrl)
|
||||
chap = &ctrl->dhchap_ctxs[i];
|
||||
chap->qid = i;
|
||||
chap->ctrl = ctrl;
|
||||
chap->authenticated = false;
|
||||
INIT_WORK(&chap->auth_work, nvme_queue_auth_work);
|
||||
}
|
||||
|
||||
|
||||
@@ -668,7 +668,7 @@ static void nvme_free_ns_head(struct kref *ref)
|
||||
struct nvme_ns_head *head =
|
||||
container_of(ref, struct nvme_ns_head, ref);
|
||||
|
||||
nvme_mpath_remove_disk(head);
|
||||
nvme_mpath_put_disk(head);
|
||||
ida_free(&head->subsys->ns_ida, head->instance);
|
||||
cleanup_srcu_struct(&head->srcu);
|
||||
nvme_put_subsystem(head->subsys);
|
||||
@@ -3743,7 +3743,7 @@ static struct nvme_ns_head *nvme_find_ns_head(struct nvme_ctrl *ctrl,
|
||||
*/
|
||||
if (h->ns_id != nsid || !nvme_is_unique_nsid(ctrl, h))
|
||||
continue;
|
||||
if (!list_empty(&h->list) && nvme_tryget_ns_head(h))
|
||||
if (nvme_tryget_ns_head(h))
|
||||
return h;
|
||||
}
|
||||
|
||||
@@ -3987,7 +3987,8 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info)
|
||||
}
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
if (!info->is_shared || !head->shared) {
|
||||
if ((!info->is_shared || !head->shared) &&
|
||||
!list_empty(&head->list)) {
|
||||
dev_err(ctrl->device,
|
||||
"Duplicate unshared namespace %d\n",
|
||||
info->nsid);
|
||||
@@ -4191,7 +4192,8 @@ static void nvme_ns_remove(struct nvme_ns *ns)
|
||||
mutex_lock(&ns->ctrl->subsys->lock);
|
||||
list_del_rcu(&ns->siblings);
|
||||
if (list_empty(&ns->head->list)) {
|
||||
list_del_init(&ns->head->entry);
|
||||
if (!nvme_mpath_queue_if_no_path(ns->head))
|
||||
list_del_init(&ns->head->entry);
|
||||
last_path = true;
|
||||
}
|
||||
mutex_unlock(&ns->ctrl->subsys->lock);
|
||||
@@ -4212,7 +4214,7 @@ static void nvme_ns_remove(struct nvme_ns *ns)
|
||||
synchronize_srcu(&ns->ctrl->srcu);
|
||||
|
||||
if (last_path)
|
||||
nvme_mpath_shutdown_disk(ns->head);
|
||||
nvme_mpath_remove_disk(ns->head);
|
||||
nvme_put_ns(ns);
|
||||
}
|
||||
|
||||
|
||||
@@ -1410,9 +1410,8 @@ nvme_fc_xmt_disconnect_assoc(struct nvme_fc_ctrl *ctrl)
|
||||
}
|
||||
|
||||
static void
|
||||
nvme_fc_xmt_ls_rsp_done(struct nvmefc_ls_rsp *lsrsp)
|
||||
nvme_fc_xmt_ls_rsp_free(struct nvmefc_ls_rcv_op *lsop)
|
||||
{
|
||||
struct nvmefc_ls_rcv_op *lsop = lsrsp->nvme_fc_private;
|
||||
struct nvme_fc_rport *rport = lsop->rport;
|
||||
struct nvme_fc_lport *lport = rport->lport;
|
||||
unsigned long flags;
|
||||
@@ -1433,6 +1432,14 @@ nvme_fc_xmt_ls_rsp_done(struct nvmefc_ls_rsp *lsrsp)
|
||||
nvme_fc_rport_put(rport);
|
||||
}
|
||||
|
||||
static void
|
||||
nvme_fc_xmt_ls_rsp_done(struct nvmefc_ls_rsp *lsrsp)
|
||||
{
|
||||
struct nvmefc_ls_rcv_op *lsop = lsrsp->nvme_fc_private;
|
||||
|
||||
nvme_fc_xmt_ls_rsp_free(lsop);
|
||||
}
|
||||
|
||||
static void
|
||||
nvme_fc_xmt_ls_rsp(struct nvmefc_ls_rcv_op *lsop)
|
||||
{
|
||||
@@ -1450,7 +1457,7 @@ nvme_fc_xmt_ls_rsp(struct nvmefc_ls_rcv_op *lsop)
|
||||
dev_warn(lport->dev,
|
||||
"LLDD rejected LS RSP xmt: LS %d status %d\n",
|
||||
w0->ls_cmd, ret);
|
||||
nvme_fc_xmt_ls_rsp_done(lsop->lsrsp);
|
||||
nvme_fc_xmt_ls_rsp_free(lsop);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,61 @@
|
||||
#include "nvme.h"
|
||||
|
||||
bool multipath = true;
|
||||
module_param(multipath, bool, 0444);
|
||||
static bool multipath_always_on;
|
||||
|
||||
static int multipath_param_set(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
int ret;
|
||||
bool *arg = kp->arg;
|
||||
|
||||
ret = param_set_bool(val, kp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (multipath_always_on && !*arg) {
|
||||
pr_err("Can't disable multipath when multipath_always_on is configured.\n");
|
||||
*arg = true;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops multipath_param_ops = {
|
||||
.set = multipath_param_set,
|
||||
.get = param_get_bool,
|
||||
};
|
||||
|
||||
module_param_cb(multipath, &multipath_param_ops, &multipath, 0444);
|
||||
MODULE_PARM_DESC(multipath,
|
||||
"turn on native support for multiple controllers per subsystem");
|
||||
|
||||
static int multipath_always_on_set(const char *val,
|
||||
const struct kernel_param *kp)
|
||||
{
|
||||
int ret;
|
||||
bool *arg = kp->arg;
|
||||
|
||||
ret = param_set_bool(val, kp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (*arg)
|
||||
multipath = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops multipath_always_on_ops = {
|
||||
.set = multipath_always_on_set,
|
||||
.get = param_get_bool,
|
||||
};
|
||||
|
||||
module_param_cb(multipath_always_on, &multipath_always_on_ops,
|
||||
&multipath_always_on, 0444);
|
||||
MODULE_PARM_DESC(multipath_always_on,
|
||||
"create multipath node always except for private namespace with non-unique nsid; note that this also implicitly enables native multipath support");
|
||||
|
||||
static const char *nvme_iopolicy_names[] = {
|
||||
[NVME_IOPOLICY_NUMA] = "numa",
|
||||
[NVME_IOPOLICY_RR] = "round-robin",
|
||||
@@ -442,7 +493,17 @@ static bool nvme_available_path(struct nvme_ns_head *head)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If "head->delayed_removal_secs" is configured (i.e., non-zero), do
|
||||
* not immediately fail I/O. Instead, requeue the I/O for the configured
|
||||
* duration, anticipating that if there's a transient link failure then
|
||||
* it may recover within this time window. This parameter is exported to
|
||||
* userspace via sysfs, and its default value is zero. It is internally
|
||||
* mapped to NVME_NSHEAD_QUEUE_IF_NO_PATH. When delayed_removal_secs is
|
||||
* non-zero, this flag is set to true. When zero, the flag is cleared.
|
||||
*/
|
||||
return nvme_mpath_queue_if_no_path(head);
|
||||
}
|
||||
|
||||
static void nvme_ns_head_submit_bio(struct bio *bio)
|
||||
@@ -617,6 +678,40 @@ static void nvme_requeue_work(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
|
||||
static void nvme_remove_head(struct nvme_ns_head *head)
|
||||
{
|
||||
if (test_and_clear_bit(NVME_NSHEAD_DISK_LIVE, &head->flags)) {
|
||||
/*
|
||||
* requeue I/O after NVME_NSHEAD_DISK_LIVE has been cleared
|
||||
* to allow multipath to fail all I/O.
|
||||
*/
|
||||
kblockd_schedule_work(&head->requeue_work);
|
||||
|
||||
nvme_cdev_del(&head->cdev, &head->cdev_device);
|
||||
synchronize_srcu(&head->srcu);
|
||||
del_gendisk(head->disk);
|
||||
nvme_put_ns_head(head);
|
||||
}
|
||||
}
|
||||
|
||||
static void nvme_remove_head_work(struct work_struct *work)
|
||||
{
|
||||
struct nvme_ns_head *head = container_of(to_delayed_work(work),
|
||||
struct nvme_ns_head, remove_work);
|
||||
bool remove = false;
|
||||
|
||||
mutex_lock(&head->subsys->lock);
|
||||
if (list_empty(&head->list)) {
|
||||
list_del_init(&head->entry);
|
||||
remove = true;
|
||||
}
|
||||
mutex_unlock(&head->subsys->lock);
|
||||
if (remove)
|
||||
nvme_remove_head(head);
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
|
||||
{
|
||||
struct queue_limits lim;
|
||||
@@ -626,14 +721,25 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
|
||||
spin_lock_init(&head->requeue_lock);
|
||||
INIT_WORK(&head->requeue_work, nvme_requeue_work);
|
||||
INIT_WORK(&head->partition_scan_work, nvme_partition_scan_work);
|
||||
INIT_DELAYED_WORK(&head->remove_work, nvme_remove_head_work);
|
||||
head->delayed_removal_secs = 0;
|
||||
|
||||
/*
|
||||
* Add a multipath node if the subsystems supports multiple controllers.
|
||||
* We also do this for private namespaces as the namespace sharing flag
|
||||
* could change after a rescan.
|
||||
* If "multipath_always_on" is enabled, a multipath node is added
|
||||
* regardless of whether the disk is single/multi ported, and whether
|
||||
* the namespace is shared or private. If "multipath_always_on" is not
|
||||
* enabled, a multipath node is added only if the subsystem supports
|
||||
* multiple controllers and the "multipath" option is configured. In
|
||||
* either case, for private namespaces, we ensure that the NSID is
|
||||
* unique.
|
||||
*/
|
||||
if (!(ctrl->subsys->cmic & NVME_CTRL_CMIC_MULTI_CTRL) ||
|
||||
!nvme_is_unique_nsid(ctrl, head) || !multipath)
|
||||
if (!multipath_always_on) {
|
||||
if (!(ctrl->subsys->cmic & NVME_CTRL_CMIC_MULTI_CTRL) ||
|
||||
!multipath)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!nvme_is_unique_nsid(ctrl, head))
|
||||
return 0;
|
||||
|
||||
blk_set_stacking_limits(&lim);
|
||||
@@ -659,6 +765,7 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
|
||||
set_bit(GD_SUPPRESS_PART_SCAN, &head->disk->state);
|
||||
sprintf(head->disk->disk_name, "nvme%dn%d",
|
||||
ctrl->subsys->instance, head->instance);
|
||||
nvme_tryget_ns_head(head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1015,6 +1122,49 @@ static ssize_t numa_nodes_show(struct device *dev, struct device_attribute *attr
|
||||
}
|
||||
DEVICE_ATTR_RO(numa_nodes);
|
||||
|
||||
static ssize_t delayed_removal_secs_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct gendisk *disk = dev_to_disk(dev);
|
||||
struct nvme_ns_head *head = disk->private_data;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&head->subsys->lock);
|
||||
ret = sysfs_emit(buf, "%u\n", head->delayed_removal_secs);
|
||||
mutex_unlock(&head->subsys->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t delayed_removal_secs_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct gendisk *disk = dev_to_disk(dev);
|
||||
struct nvme_ns_head *head = disk->private_data;
|
||||
unsigned int sec;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 0, &sec);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&head->subsys->lock);
|
||||
head->delayed_removal_secs = sec;
|
||||
if (sec)
|
||||
set_bit(NVME_NSHEAD_QUEUE_IF_NO_PATH, &head->flags);
|
||||
else
|
||||
clear_bit(NVME_NSHEAD_QUEUE_IF_NO_PATH, &head->flags);
|
||||
mutex_unlock(&head->subsys->lock);
|
||||
/*
|
||||
* Ensure that update to NVME_NSHEAD_QUEUE_IF_NO_PATH is seen
|
||||
* by its reader.
|
||||
*/
|
||||
synchronize_srcu(&head->srcu);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
DEVICE_ATTR_RW(delayed_removal_secs);
|
||||
|
||||
static int nvme_lookup_ana_group_desc(struct nvme_ctrl *ctrl,
|
||||
struct nvme_ana_group_desc *desc, void *data)
|
||||
{
|
||||
@@ -1136,23 +1286,43 @@ void nvme_mpath_add_disk(struct nvme_ns *ns, __le32 anagrpid)
|
||||
#endif
|
||||
}
|
||||
|
||||
void nvme_mpath_shutdown_disk(struct nvme_ns_head *head)
|
||||
void nvme_mpath_remove_disk(struct nvme_ns_head *head)
|
||||
{
|
||||
if (!head->disk)
|
||||
return;
|
||||
if (test_and_clear_bit(NVME_NSHEAD_DISK_LIVE, &head->flags)) {
|
||||
nvme_cdev_del(&head->cdev, &head->cdev_device);
|
||||
bool remove = false;
|
||||
|
||||
mutex_lock(&head->subsys->lock);
|
||||
/*
|
||||
* We are called when all paths have been removed, and at that point
|
||||
* head->list is expected to be empty. However, nvme_remove_ns() and
|
||||
* nvme_init_ns_head() can run concurrently and so if head->delayed_
|
||||
* removal_secs is configured, it is possible that by the time we reach
|
||||
* this point, head->list may no longer be empty. Therefore, we recheck
|
||||
* head->list here. If it is no longer empty then we skip enqueuing the
|
||||
* delayed head removal work.
|
||||
*/
|
||||
if (!list_empty(&head->list))
|
||||
goto out;
|
||||
|
||||
if (head->delayed_removal_secs) {
|
||||
/*
|
||||
* requeue I/O after NVME_NSHEAD_DISK_LIVE has been cleared
|
||||
* to allow multipath to fail all I/O.
|
||||
* Ensure that no one could remove this module while the head
|
||||
* remove work is pending.
|
||||
*/
|
||||
synchronize_srcu(&head->srcu);
|
||||
kblockd_schedule_work(&head->requeue_work);
|
||||
del_gendisk(head->disk);
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
goto out;
|
||||
queue_delayed_work(nvme_wq, &head->remove_work,
|
||||
head->delayed_removal_secs * HZ);
|
||||
} else {
|
||||
list_del_init(&head->entry);
|
||||
remove = true;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&head->subsys->lock);
|
||||
if (remove)
|
||||
nvme_remove_head(head);
|
||||
}
|
||||
|
||||
void nvme_mpath_remove_disk(struct nvme_ns_head *head)
|
||||
void nvme_mpath_put_disk(struct nvme_ns_head *head)
|
||||
{
|
||||
if (!head->disk)
|
||||
return;
|
||||
|
||||
@@ -506,7 +506,10 @@ struct nvme_ns_head {
|
||||
struct work_struct partition_scan_work;
|
||||
struct mutex lock;
|
||||
unsigned long flags;
|
||||
#define NVME_NSHEAD_DISK_LIVE 0
|
||||
struct delayed_work remove_work;
|
||||
unsigned int delayed_removal_secs;
|
||||
#define NVME_NSHEAD_DISK_LIVE 0
|
||||
#define NVME_NSHEAD_QUEUE_IF_NO_PATH 1
|
||||
struct nvme_ns __rcu *current_path[];
|
||||
#endif
|
||||
};
|
||||
@@ -963,7 +966,7 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl,struct nvme_ns_head *head);
|
||||
void nvme_mpath_add_sysfs_link(struct nvme_ns_head *ns);
|
||||
void nvme_mpath_remove_sysfs_link(struct nvme_ns *ns);
|
||||
void nvme_mpath_add_disk(struct nvme_ns *ns, __le32 anagrpid);
|
||||
void nvme_mpath_remove_disk(struct nvme_ns_head *head);
|
||||
void nvme_mpath_put_disk(struct nvme_ns_head *head);
|
||||
int nvme_mpath_init_identify(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id);
|
||||
void nvme_mpath_init_ctrl(struct nvme_ctrl *ctrl);
|
||||
void nvme_mpath_update(struct nvme_ctrl *ctrl);
|
||||
@@ -972,7 +975,7 @@ void nvme_mpath_stop(struct nvme_ctrl *ctrl);
|
||||
bool nvme_mpath_clear_current_path(struct nvme_ns *ns);
|
||||
void nvme_mpath_revalidate_paths(struct nvme_ns *ns);
|
||||
void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl);
|
||||
void nvme_mpath_shutdown_disk(struct nvme_ns_head *head);
|
||||
void nvme_mpath_remove_disk(struct nvme_ns_head *head);
|
||||
void nvme_mpath_start_request(struct request *rq);
|
||||
void nvme_mpath_end_request(struct request *rq);
|
||||
|
||||
@@ -989,12 +992,19 @@ extern struct device_attribute dev_attr_ana_grpid;
|
||||
extern struct device_attribute dev_attr_ana_state;
|
||||
extern struct device_attribute dev_attr_queue_depth;
|
||||
extern struct device_attribute dev_attr_numa_nodes;
|
||||
extern struct device_attribute dev_attr_delayed_removal_secs;
|
||||
extern struct device_attribute subsys_attr_iopolicy;
|
||||
|
||||
static inline bool nvme_disk_is_ns_head(struct gendisk *disk)
|
||||
{
|
||||
return disk->fops == &nvme_ns_head_ops;
|
||||
}
|
||||
static inline bool nvme_mpath_queue_if_no_path(struct nvme_ns_head *head)
|
||||
{
|
||||
if (test_bit(NVME_NSHEAD_QUEUE_IF_NO_PATH, &head->flags))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
#define multipath false
|
||||
static inline bool nvme_ctrl_use_ana(struct nvme_ctrl *ctrl)
|
||||
@@ -1015,7 +1025,7 @@ static inline int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl,
|
||||
static inline void nvme_mpath_add_disk(struct nvme_ns *ns, __le32 anagrpid)
|
||||
{
|
||||
}
|
||||
static inline void nvme_mpath_remove_disk(struct nvme_ns_head *head)
|
||||
static inline void nvme_mpath_put_disk(struct nvme_ns_head *head)
|
||||
{
|
||||
}
|
||||
static inline void nvme_mpath_add_sysfs_link(struct nvme_ns *ns)
|
||||
@@ -1034,7 +1044,7 @@ static inline void nvme_mpath_revalidate_paths(struct nvme_ns *ns)
|
||||
static inline void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl)
|
||||
{
|
||||
}
|
||||
static inline void nvme_mpath_shutdown_disk(struct nvme_ns_head *head)
|
||||
static inline void nvme_mpath_remove_disk(struct nvme_ns_head *head)
|
||||
{
|
||||
}
|
||||
static inline void nvme_trace_bio_complete(struct request *req)
|
||||
@@ -1082,6 +1092,10 @@ static inline bool nvme_disk_is_ns_head(struct gendisk *disk)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline bool nvme_mpath_queue_if_no_path(struct nvme_ns_head *head)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_NVME_MULTIPATH */
|
||||
|
||||
int nvme_ns_get_unique_id(struct nvme_ns *ns, u8 id[16],
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/nodemask.h>
|
||||
#include <linux/once.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/suspend.h>
|
||||
@@ -34,16 +35,31 @@
|
||||
#define SQ_SIZE(q) ((q)->q_depth << (q)->sqes)
|
||||
#define CQ_SIZE(q) ((q)->q_depth * sizeof(struct nvme_completion))
|
||||
|
||||
#define SGES_PER_PAGE (NVME_CTRL_PAGE_SIZE / sizeof(struct nvme_sgl_desc))
|
||||
/* Optimisation for I/Os between 4k and 128k */
|
||||
#define NVME_SMALL_POOL_SIZE 256
|
||||
|
||||
/*
|
||||
* These can be higher, but we need to ensure that any command doesn't
|
||||
* require an sg allocation that needs more than a page of data.
|
||||
*/
|
||||
#define NVME_MAX_KB_SZ 8192
|
||||
#define NVME_MAX_SEGS 128
|
||||
#define NVME_MAX_META_SEGS 15
|
||||
#define NVME_MAX_NR_ALLOCATIONS 5
|
||||
#define NVME_MAX_NR_DESCRIPTORS 5
|
||||
|
||||
/*
|
||||
* For data SGLs we support a single descriptors worth of SGL entries, but for
|
||||
* now we also limit it to avoid an allocation larger than PAGE_SIZE for the
|
||||
* scatterlist.
|
||||
*/
|
||||
#define NVME_MAX_SEGS \
|
||||
min(NVME_CTRL_PAGE_SIZE / sizeof(struct nvme_sgl_desc), \
|
||||
(PAGE_SIZE / sizeof(struct scatterlist)))
|
||||
|
||||
/*
|
||||
* For metadata SGLs, only the small descriptor is supported, and the first
|
||||
* entry is the segment descriptor, which for the data pointer sits in the SQE.
|
||||
*/
|
||||
#define NVME_MAX_META_SEGS \
|
||||
((NVME_SMALL_POOL_SIZE / sizeof(struct nvme_sgl_desc)) - 1)
|
||||
|
||||
static int use_threaded_interrupts;
|
||||
module_param(use_threaded_interrupts, int, 0444);
|
||||
@@ -112,6 +128,11 @@ static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown);
|
||||
static void nvme_delete_io_queues(struct nvme_dev *dev);
|
||||
static void nvme_update_attrs(struct nvme_dev *dev);
|
||||
|
||||
struct nvme_descriptor_pools {
|
||||
struct dma_pool *large;
|
||||
struct dma_pool *small;
|
||||
};
|
||||
|
||||
/*
|
||||
* Represents an NVM Express device. Each nvme_dev is a PCI function.
|
||||
*/
|
||||
@@ -121,8 +142,6 @@ struct nvme_dev {
|
||||
struct blk_mq_tag_set admin_tagset;
|
||||
u32 __iomem *dbs;
|
||||
struct device *dev;
|
||||
struct dma_pool *prp_page_pool;
|
||||
struct dma_pool *prp_small_pool;
|
||||
unsigned online_queues;
|
||||
unsigned max_qid;
|
||||
unsigned io_queues[HCTX_MAX_TYPES];
|
||||
@@ -162,6 +181,7 @@ struct nvme_dev {
|
||||
unsigned int nr_allocated_queues;
|
||||
unsigned int nr_write_queues;
|
||||
unsigned int nr_poll_queues;
|
||||
struct nvme_descriptor_pools descriptor_pools[];
|
||||
};
|
||||
|
||||
static int io_queue_depth_set(const char *val, const struct kernel_param *kp)
|
||||
@@ -191,6 +211,7 @@ static inline struct nvme_dev *to_nvme_dev(struct nvme_ctrl *ctrl)
|
||||
*/
|
||||
struct nvme_queue {
|
||||
struct nvme_dev *dev;
|
||||
struct nvme_descriptor_pools descriptor_pools;
|
||||
spinlock_t sq_lock;
|
||||
void *sq_cmds;
|
||||
/* only used for poll queues: */
|
||||
@@ -219,30 +240,30 @@ struct nvme_queue {
|
||||
struct completion delete_done;
|
||||
};
|
||||
|
||||
union nvme_descriptor {
|
||||
struct nvme_sgl_desc *sg_list;
|
||||
__le64 *prp_list;
|
||||
/* bits for iod->flags */
|
||||
enum nvme_iod_flags {
|
||||
/* this command has been aborted by the timeout handler */
|
||||
IOD_ABORTED = 1U << 0,
|
||||
|
||||
/* uses the small descriptor pool */
|
||||
IOD_SMALL_DESCRIPTOR = 1U << 1,
|
||||
};
|
||||
|
||||
/*
|
||||
* The nvme_iod describes the data in an I/O.
|
||||
*
|
||||
* The sg pointer contains the list of PRP/SGL chunk allocations in addition
|
||||
* to the actual struct scatterlist.
|
||||
*/
|
||||
struct nvme_iod {
|
||||
struct nvme_request req;
|
||||
struct nvme_command cmd;
|
||||
bool aborted;
|
||||
s8 nr_allocations; /* PRP list pool allocations. 0 means small
|
||||
pool in use */
|
||||
u8 flags;
|
||||
u8 nr_descriptors;
|
||||
unsigned int dma_len; /* length of single DMA segment mapping */
|
||||
dma_addr_t first_dma;
|
||||
dma_addr_t meta_dma;
|
||||
struct sg_table sgt;
|
||||
struct sg_table meta_sgt;
|
||||
union nvme_descriptor meta_list;
|
||||
union nvme_descriptor list[NVME_MAX_NR_ALLOCATIONS];
|
||||
struct nvme_sgl_desc *meta_descriptor;
|
||||
void *descriptors[NVME_MAX_NR_DESCRIPTORS];
|
||||
};
|
||||
|
||||
static inline unsigned int nvme_dbbuf_size(struct nvme_dev *dev)
|
||||
@@ -397,28 +418,76 @@ static int nvme_pci_npages_prp(void)
|
||||
return DIV_ROUND_UP(8 * nprps, NVME_CTRL_PAGE_SIZE - 8);
|
||||
}
|
||||
|
||||
static int nvme_admin_init_hctx(struct blk_mq_hw_ctx *hctx, void *data,
|
||||
unsigned int hctx_idx)
|
||||
static struct nvme_descriptor_pools *
|
||||
nvme_setup_descriptor_pools(struct nvme_dev *dev, unsigned numa_node)
|
||||
{
|
||||
struct nvme_descriptor_pools *pools = &dev->descriptor_pools[numa_node];
|
||||
size_t small_align = NVME_SMALL_POOL_SIZE;
|
||||
|
||||
if (pools->small)
|
||||
return pools; /* already initialized */
|
||||
|
||||
pools->large = dma_pool_create_node("nvme descriptor page", dev->dev,
|
||||
NVME_CTRL_PAGE_SIZE, NVME_CTRL_PAGE_SIZE, 0, numa_node);
|
||||
if (!pools->large)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (dev->ctrl.quirks & NVME_QUIRK_DMAPOOL_ALIGN_512)
|
||||
small_align = 512;
|
||||
|
||||
pools->small = dma_pool_create_node("nvme descriptor small", dev->dev,
|
||||
NVME_SMALL_POOL_SIZE, small_align, 0, numa_node);
|
||||
if (!pools->small) {
|
||||
dma_pool_destroy(pools->large);
|
||||
pools->large = NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
return pools;
|
||||
}
|
||||
|
||||
static void nvme_release_descriptor_pools(struct nvme_dev *dev)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < nr_node_ids; i++) {
|
||||
struct nvme_descriptor_pools *pools = &dev->descriptor_pools[i];
|
||||
|
||||
dma_pool_destroy(pools->large);
|
||||
dma_pool_destroy(pools->small);
|
||||
}
|
||||
}
|
||||
|
||||
static int nvme_init_hctx_common(struct blk_mq_hw_ctx *hctx, void *data,
|
||||
unsigned qid)
|
||||
{
|
||||
struct nvme_dev *dev = to_nvme_dev(data);
|
||||
struct nvme_queue *nvmeq = &dev->queues[0];
|
||||
struct nvme_queue *nvmeq = &dev->queues[qid];
|
||||
struct nvme_descriptor_pools *pools;
|
||||
struct blk_mq_tags *tags;
|
||||
|
||||
WARN_ON(hctx_idx != 0);
|
||||
WARN_ON(dev->admin_tagset.tags[0] != hctx->tags);
|
||||
tags = qid ? dev->tagset.tags[qid - 1] : dev->admin_tagset.tags[0];
|
||||
WARN_ON(tags != hctx->tags);
|
||||
pools = nvme_setup_descriptor_pools(dev, hctx->numa_node);
|
||||
if (IS_ERR(pools))
|
||||
return PTR_ERR(pools);
|
||||
|
||||
nvmeq->descriptor_pools = *pools;
|
||||
hctx->driver_data = nvmeq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvme_init_hctx(struct blk_mq_hw_ctx *hctx, void *data,
|
||||
unsigned int hctx_idx)
|
||||
static int nvme_admin_init_hctx(struct blk_mq_hw_ctx *hctx, void *data,
|
||||
unsigned int hctx_idx)
|
||||
{
|
||||
struct nvme_dev *dev = to_nvme_dev(data);
|
||||
struct nvme_queue *nvmeq = &dev->queues[hctx_idx + 1];
|
||||
WARN_ON(hctx_idx != 0);
|
||||
return nvme_init_hctx_common(hctx, data, 0);
|
||||
}
|
||||
|
||||
WARN_ON(dev->tagset.tags[hctx_idx] != hctx->tags);
|
||||
hctx->driver_data = nvmeq;
|
||||
return 0;
|
||||
static int nvme_init_hctx(struct blk_mq_hw_ctx *hctx, void *data,
|
||||
unsigned int hctx_idx)
|
||||
{
|
||||
return nvme_init_hctx_common(hctx, data, hctx_idx + 1);
|
||||
}
|
||||
|
||||
static int nvme_pci_init_request(struct blk_mq_tag_set *set,
|
||||
@@ -537,23 +606,39 @@ static inline bool nvme_pci_use_sgls(struct nvme_dev *dev, struct request *req,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void nvme_free_prps(struct nvme_dev *dev, struct request *req)
|
||||
static inline struct dma_pool *nvme_dma_pool(struct nvme_queue *nvmeq,
|
||||
struct nvme_iod *iod)
|
||||
{
|
||||
if (iod->flags & IOD_SMALL_DESCRIPTOR)
|
||||
return nvmeq->descriptor_pools.small;
|
||||
return nvmeq->descriptor_pools.large;
|
||||
}
|
||||
|
||||
static void nvme_free_descriptors(struct nvme_queue *nvmeq, struct request *req)
|
||||
{
|
||||
const int last_prp = NVME_CTRL_PAGE_SIZE / sizeof(__le64) - 1;
|
||||
struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
|
||||
dma_addr_t dma_addr = iod->first_dma;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < iod->nr_allocations; i++) {
|
||||
__le64 *prp_list = iod->list[i].prp_list;
|
||||
if (iod->nr_descriptors == 1) {
|
||||
dma_pool_free(nvme_dma_pool(nvmeq, iod), iod->descriptors[0],
|
||||
dma_addr);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < iod->nr_descriptors; i++) {
|
||||
__le64 *prp_list = iod->descriptors[i];
|
||||
dma_addr_t next_dma_addr = le64_to_cpu(prp_list[last_prp]);
|
||||
|
||||
dma_pool_free(dev->prp_page_pool, prp_list, dma_addr);
|
||||
dma_pool_free(nvmeq->descriptor_pools.large, prp_list,
|
||||
dma_addr);
|
||||
dma_addr = next_dma_addr;
|
||||
}
|
||||
}
|
||||
|
||||
static void nvme_unmap_data(struct nvme_dev *dev, struct request *req)
|
||||
static void nvme_unmap_data(struct nvme_dev *dev, struct nvme_queue *nvmeq,
|
||||
struct request *req)
|
||||
{
|
||||
struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
|
||||
|
||||
@@ -566,15 +651,7 @@ static void nvme_unmap_data(struct nvme_dev *dev, struct request *req)
|
||||
WARN_ON_ONCE(!iod->sgt.nents);
|
||||
|
||||
dma_unmap_sgtable(dev->dev, &iod->sgt, rq_dma_dir(req), 0);
|
||||
|
||||
if (iod->nr_allocations == 0)
|
||||
dma_pool_free(dev->prp_small_pool, iod->list[0].sg_list,
|
||||
iod->first_dma);
|
||||
else if (iod->nr_allocations == 1)
|
||||
dma_pool_free(dev->prp_page_pool, iod->list[0].sg_list,
|
||||
iod->first_dma);
|
||||
else
|
||||
nvme_free_prps(dev, req);
|
||||
nvme_free_descriptors(nvmeq, req);
|
||||
mempool_free(iod->sgt.sgl, dev->iod_mempool);
|
||||
}
|
||||
|
||||
@@ -592,11 +669,10 @@ static void nvme_print_sgl(struct scatterlist *sgl, int nents)
|
||||
}
|
||||
}
|
||||
|
||||
static blk_status_t nvme_pci_setup_prps(struct nvme_dev *dev,
|
||||
static blk_status_t nvme_pci_setup_prps(struct nvme_queue *nvmeq,
|
||||
struct request *req, struct nvme_rw_command *cmnd)
|
||||
{
|
||||
struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
|
||||
struct dma_pool *pool;
|
||||
int length = blk_rq_payload_bytes(req);
|
||||
struct scatterlist *sg = iod->sgt.sgl;
|
||||
int dma_len = sg_dma_len(sg);
|
||||
@@ -604,7 +680,7 @@ static blk_status_t nvme_pci_setup_prps(struct nvme_dev *dev,
|
||||
int offset = dma_addr & (NVME_CTRL_PAGE_SIZE - 1);
|
||||
__le64 *prp_list;
|
||||
dma_addr_t prp_dma;
|
||||
int nprps, i;
|
||||
int i;
|
||||
|
||||
length -= (NVME_CTRL_PAGE_SIZE - offset);
|
||||
if (length <= 0) {
|
||||
@@ -626,30 +702,26 @@ static blk_status_t nvme_pci_setup_prps(struct nvme_dev *dev,
|
||||
goto done;
|
||||
}
|
||||
|
||||
nprps = DIV_ROUND_UP(length, NVME_CTRL_PAGE_SIZE);
|
||||
if (nprps <= (256 / 8)) {
|
||||
pool = dev->prp_small_pool;
|
||||
iod->nr_allocations = 0;
|
||||
} else {
|
||||
pool = dev->prp_page_pool;
|
||||
iod->nr_allocations = 1;
|
||||
}
|
||||
if (DIV_ROUND_UP(length, NVME_CTRL_PAGE_SIZE) <=
|
||||
NVME_SMALL_POOL_SIZE / sizeof(__le64))
|
||||
iod->flags |= IOD_SMALL_DESCRIPTOR;
|
||||
|
||||
prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma);
|
||||
if (!prp_list) {
|
||||
iod->nr_allocations = -1;
|
||||
prp_list = dma_pool_alloc(nvme_dma_pool(nvmeq, iod), GFP_ATOMIC,
|
||||
&prp_dma);
|
||||
if (!prp_list)
|
||||
return BLK_STS_RESOURCE;
|
||||
}
|
||||
iod->list[0].prp_list = prp_list;
|
||||
iod->descriptors[iod->nr_descriptors++] = prp_list;
|
||||
iod->first_dma = prp_dma;
|
||||
i = 0;
|
||||
for (;;) {
|
||||
if (i == NVME_CTRL_PAGE_SIZE >> 3) {
|
||||
__le64 *old_prp_list = prp_list;
|
||||
prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma);
|
||||
|
||||
prp_list = dma_pool_alloc(nvmeq->descriptor_pools.large,
|
||||
GFP_ATOMIC, &prp_dma);
|
||||
if (!prp_list)
|
||||
goto free_prps;
|
||||
iod->list[iod->nr_allocations++].prp_list = prp_list;
|
||||
iod->descriptors[iod->nr_descriptors++] = prp_list;
|
||||
prp_list[0] = old_prp_list[i - 1];
|
||||
old_prp_list[i - 1] = cpu_to_le64(prp_dma);
|
||||
i = 1;
|
||||
@@ -673,7 +745,7 @@ static blk_status_t nvme_pci_setup_prps(struct nvme_dev *dev,
|
||||
cmnd->dptr.prp2 = cpu_to_le64(iod->first_dma);
|
||||
return BLK_STS_OK;
|
||||
free_prps:
|
||||
nvme_free_prps(dev, req);
|
||||
nvme_free_descriptors(nvmeq, req);
|
||||
return BLK_STS_RESOURCE;
|
||||
bad_sgl:
|
||||
WARN(DO_ONCE(nvme_print_sgl, iod->sgt.sgl, iod->sgt.nents),
|
||||
@@ -698,11 +770,10 @@ static void nvme_pci_sgl_set_seg(struct nvme_sgl_desc *sge,
|
||||
sge->type = NVME_SGL_FMT_LAST_SEG_DESC << 4;
|
||||
}
|
||||
|
||||
static blk_status_t nvme_pci_setup_sgls(struct nvme_dev *dev,
|
||||
static blk_status_t nvme_pci_setup_sgls(struct nvme_queue *nvmeq,
|
||||
struct request *req, struct nvme_rw_command *cmd)
|
||||
{
|
||||
struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
|
||||
struct dma_pool *pool;
|
||||
struct nvme_sgl_desc *sg_list;
|
||||
struct scatterlist *sg = iod->sgt.sgl;
|
||||
unsigned int entries = iod->sgt.nents;
|
||||
@@ -717,21 +788,14 @@ static blk_status_t nvme_pci_setup_sgls(struct nvme_dev *dev,
|
||||
return BLK_STS_OK;
|
||||
}
|
||||
|
||||
if (entries <= (256 / sizeof(struct nvme_sgl_desc))) {
|
||||
pool = dev->prp_small_pool;
|
||||
iod->nr_allocations = 0;
|
||||
} else {
|
||||
pool = dev->prp_page_pool;
|
||||
iod->nr_allocations = 1;
|
||||
}
|
||||
if (entries <= NVME_SMALL_POOL_SIZE / sizeof(*sg_list))
|
||||
iod->flags |= IOD_SMALL_DESCRIPTOR;
|
||||
|
||||
sg_list = dma_pool_alloc(pool, GFP_ATOMIC, &sgl_dma);
|
||||
if (!sg_list) {
|
||||
iod->nr_allocations = -1;
|
||||
sg_list = dma_pool_alloc(nvme_dma_pool(nvmeq, iod), GFP_ATOMIC,
|
||||
&sgl_dma);
|
||||
if (!sg_list)
|
||||
return BLK_STS_RESOURCE;
|
||||
}
|
||||
|
||||
iod->list[0].sg_list = sg_list;
|
||||
iod->descriptors[iod->nr_descriptors++] = sg_list;
|
||||
iod->first_dma = sgl_dma;
|
||||
|
||||
nvme_pci_sgl_set_seg(&cmd->dptr.sgl, sgl_dma, entries);
|
||||
@@ -785,12 +849,12 @@ static blk_status_t nvme_setup_sgl_simple(struct nvme_dev *dev,
|
||||
static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req,
|
||||
struct nvme_command *cmnd)
|
||||
{
|
||||
struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
|
||||
struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
|
||||
blk_status_t ret = BLK_STS_RESOURCE;
|
||||
int rc;
|
||||
|
||||
if (blk_rq_nr_phys_segments(req) == 1) {
|
||||
struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
|
||||
struct bio_vec bv = req_bvec(req);
|
||||
|
||||
if (!is_pci_p2pdma_page(bv.bv_page)) {
|
||||
@@ -825,9 +889,9 @@ static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req,
|
||||
}
|
||||
|
||||
if (nvme_pci_use_sgls(dev, req, iod->sgt.nents))
|
||||
ret = nvme_pci_setup_sgls(dev, req, &cmnd->rw);
|
||||
ret = nvme_pci_setup_sgls(nvmeq, req, &cmnd->rw);
|
||||
else
|
||||
ret = nvme_pci_setup_prps(dev, req, &cmnd->rw);
|
||||
ret = nvme_pci_setup_prps(nvmeq, req, &cmnd->rw);
|
||||
if (ret != BLK_STS_OK)
|
||||
goto out_unmap_sg;
|
||||
return BLK_STS_OK;
|
||||
@@ -842,6 +906,7 @@ static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req,
|
||||
static blk_status_t nvme_pci_setup_meta_sgls(struct nvme_dev *dev,
|
||||
struct request *req)
|
||||
{
|
||||
struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
|
||||
struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
|
||||
struct nvme_rw_command *cmnd = &iod->cmd.rw;
|
||||
struct nvme_sgl_desc *sg_list;
|
||||
@@ -865,12 +930,13 @@ static blk_status_t nvme_pci_setup_meta_sgls(struct nvme_dev *dev,
|
||||
if (rc)
|
||||
goto out_free_sg;
|
||||
|
||||
sg_list = dma_pool_alloc(dev->prp_small_pool, GFP_ATOMIC, &sgl_dma);
|
||||
sg_list = dma_pool_alloc(nvmeq->descriptor_pools.small, GFP_ATOMIC,
|
||||
&sgl_dma);
|
||||
if (!sg_list)
|
||||
goto out_unmap_sg;
|
||||
|
||||
entries = iod->meta_sgt.nents;
|
||||
iod->meta_list.sg_list = sg_list;
|
||||
iod->meta_descriptor = sg_list;
|
||||
iod->meta_dma = sgl_dma;
|
||||
|
||||
cmnd->flags = NVME_CMD_SGL_METASEG;
|
||||
@@ -912,7 +978,10 @@ static blk_status_t nvme_pci_setup_meta_mptr(struct nvme_dev *dev,
|
||||
|
||||
static blk_status_t nvme_map_metadata(struct nvme_dev *dev, struct request *req)
|
||||
{
|
||||
if (nvme_pci_metadata_use_sgls(dev, req))
|
||||
struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
|
||||
|
||||
if ((iod->cmd.common.flags & NVME_CMD_SGL_METABUF) &&
|
||||
nvme_pci_metadata_use_sgls(dev, req))
|
||||
return nvme_pci_setup_meta_sgls(dev, req);
|
||||
return nvme_pci_setup_meta_mptr(dev, req);
|
||||
}
|
||||
@@ -922,8 +991,8 @@ static blk_status_t nvme_prep_rq(struct nvme_dev *dev, struct request *req)
|
||||
struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
|
||||
blk_status_t ret;
|
||||
|
||||
iod->aborted = false;
|
||||
iod->nr_allocations = -1;
|
||||
iod->flags = 0;
|
||||
iod->nr_descriptors = 0;
|
||||
iod->sgt.nents = 0;
|
||||
iod->meta_sgt.nents = 0;
|
||||
|
||||
@@ -947,7 +1016,7 @@ static blk_status_t nvme_prep_rq(struct nvme_dev *dev, struct request *req)
|
||||
return BLK_STS_OK;
|
||||
out_unmap_data:
|
||||
if (blk_rq_nr_phys_segments(req))
|
||||
nvme_unmap_data(dev, req);
|
||||
nvme_unmap_data(dev, req->mq_hctx->driver_data, req);
|
||||
out_free_cmd:
|
||||
nvme_cleanup_cmd(req);
|
||||
return ret;
|
||||
@@ -1037,6 +1106,7 @@ static void nvme_queue_rqs(struct rq_list *rqlist)
|
||||
}
|
||||
|
||||
static __always_inline void nvme_unmap_metadata(struct nvme_dev *dev,
|
||||
struct nvme_queue *nvmeq,
|
||||
struct request *req)
|
||||
{
|
||||
struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
|
||||
@@ -1048,8 +1118,8 @@ static __always_inline void nvme_unmap_metadata(struct nvme_dev *dev,
|
||||
return;
|
||||
}
|
||||
|
||||
dma_pool_free(dev->prp_small_pool, iod->meta_list.sg_list,
|
||||
iod->meta_dma);
|
||||
dma_pool_free(nvmeq->descriptor_pools.small, iod->meta_descriptor,
|
||||
iod->meta_dma);
|
||||
dma_unmap_sgtable(dev->dev, &iod->meta_sgt, rq_dma_dir(req), 0);
|
||||
mempool_free(iod->meta_sgt.sgl, dev->iod_meta_mempool);
|
||||
}
|
||||
@@ -1060,10 +1130,10 @@ static __always_inline void nvme_pci_unmap_rq(struct request *req)
|
||||
struct nvme_dev *dev = nvmeq->dev;
|
||||
|
||||
if (blk_integrity_rq(req))
|
||||
nvme_unmap_metadata(dev, req);
|
||||
nvme_unmap_metadata(dev, nvmeq, req);
|
||||
|
||||
if (blk_rq_nr_phys_segments(req))
|
||||
nvme_unmap_data(dev, req);
|
||||
nvme_unmap_data(dev, nvmeq, req);
|
||||
}
|
||||
|
||||
static void nvme_pci_complete_rq(struct request *req)
|
||||
@@ -1488,7 +1558,7 @@ static enum blk_eh_timer_return nvme_timeout(struct request *req)
|
||||
* returned to the driver, or if this is the admin queue.
|
||||
*/
|
||||
opcode = nvme_req(req)->cmd->common.opcode;
|
||||
if (!nvmeq->qid || iod->aborted) {
|
||||
if (!nvmeq->qid || (iod->flags & IOD_ABORTED)) {
|
||||
dev_warn(dev->ctrl.device,
|
||||
"I/O tag %d (%04x) opcode %#x (%s) QID %d timeout, reset controller\n",
|
||||
req->tag, nvme_cid(req), opcode,
|
||||
@@ -1501,7 +1571,7 @@ static enum blk_eh_timer_return nvme_timeout(struct request *req)
|
||||
atomic_inc(&dev->ctrl.abort_limit);
|
||||
return BLK_EH_RESET_TIMER;
|
||||
}
|
||||
iod->aborted = true;
|
||||
iod->flags |= IOD_ABORTED;
|
||||
|
||||
cmd.abort.opcode = nvme_admin_abort_cmd;
|
||||
cmd.abort.cid = nvme_cid(req);
|
||||
@@ -2840,35 +2910,6 @@ static int nvme_disable_prepare_reset(struct nvme_dev *dev, bool shutdown)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvme_setup_prp_pools(struct nvme_dev *dev)
|
||||
{
|
||||
size_t small_align = 256;
|
||||
|
||||
dev->prp_page_pool = dma_pool_create("prp list page", dev->dev,
|
||||
NVME_CTRL_PAGE_SIZE,
|
||||
NVME_CTRL_PAGE_SIZE, 0);
|
||||
if (!dev->prp_page_pool)
|
||||
return -ENOMEM;
|
||||
|
||||
if (dev->ctrl.quirks & NVME_QUIRK_DMAPOOL_ALIGN_512)
|
||||
small_align = 512;
|
||||
|
||||
/* Optimisation for I/Os between 4k and 128k */
|
||||
dev->prp_small_pool = dma_pool_create("prp list 256", dev->dev,
|
||||
256, small_align, 0);
|
||||
if (!dev->prp_small_pool) {
|
||||
dma_pool_destroy(dev->prp_page_pool);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nvme_release_prp_pools(struct nvme_dev *dev)
|
||||
{
|
||||
dma_pool_destroy(dev->prp_page_pool);
|
||||
dma_pool_destroy(dev->prp_small_pool);
|
||||
}
|
||||
|
||||
static int nvme_pci_alloc_iod_mempool(struct nvme_dev *dev)
|
||||
{
|
||||
size_t meta_size = sizeof(struct scatterlist) * (NVME_MAX_META_SEGS + 1);
|
||||
@@ -3183,7 +3224,8 @@ static struct nvme_dev *nvme_pci_alloc_dev(struct pci_dev *pdev,
|
||||
struct nvme_dev *dev;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
dev = kzalloc_node(sizeof(*dev), GFP_KERNEL, node);
|
||||
dev = kzalloc_node(struct_size(dev, descriptor_pools, nr_node_ids),
|
||||
GFP_KERNEL, node);
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
INIT_WORK(&dev->ctrl.reset_work, nvme_reset_work);
|
||||
@@ -3258,13 +3300,9 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
if (result)
|
||||
goto out_uninit_ctrl;
|
||||
|
||||
result = nvme_setup_prp_pools(dev);
|
||||
if (result)
|
||||
goto out_dev_unmap;
|
||||
|
||||
result = nvme_pci_alloc_iod_mempool(dev);
|
||||
if (result)
|
||||
goto out_release_prp_pools;
|
||||
goto out_dev_unmap;
|
||||
|
||||
dev_info(dev->ctrl.device, "pci function %s\n", dev_name(&pdev->dev));
|
||||
|
||||
@@ -3340,8 +3378,6 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
out_release_iod_mempool:
|
||||
mempool_destroy(dev->iod_mempool);
|
||||
mempool_destroy(dev->iod_meta_mempool);
|
||||
out_release_prp_pools:
|
||||
nvme_release_prp_pools(dev);
|
||||
out_dev_unmap:
|
||||
nvme_dev_unmap(dev);
|
||||
out_uninit_ctrl:
|
||||
@@ -3406,7 +3442,7 @@ static void nvme_remove(struct pci_dev *pdev)
|
||||
nvme_free_queues(dev, 0);
|
||||
mempool_destroy(dev->iod_mempool);
|
||||
mempool_destroy(dev->iod_meta_mempool);
|
||||
nvme_release_prp_pools(dev);
|
||||
nvme_release_descriptor_pools(dev);
|
||||
nvme_dev_unmap(dev);
|
||||
nvme_uninit_ctrl(&dev->ctrl);
|
||||
}
|
||||
@@ -3805,9 +3841,7 @@ static int __init nvme_init(void)
|
||||
BUILD_BUG_ON(sizeof(struct nvme_create_sq) != 64);
|
||||
BUILD_BUG_ON(sizeof(struct nvme_delete_queue) != 64);
|
||||
BUILD_BUG_ON(IRQ_AFFINITY_MAX_SETS < 2);
|
||||
BUILD_BUG_ON(NVME_MAX_SEGS > SGES_PER_PAGE);
|
||||
BUILD_BUG_ON(sizeof(struct scatterlist) * NVME_MAX_SEGS > PAGE_SIZE);
|
||||
BUILD_BUG_ON(nvme_pci_npages_prp() > NVME_MAX_NR_ALLOCATIONS);
|
||||
BUILD_BUG_ON(nvme_pci_npages_prp() > NVME_MAX_NR_DESCRIPTORS);
|
||||
|
||||
return pci_register_driver(&nvme_driver);
|
||||
}
|
||||
|
||||
@@ -260,6 +260,7 @@ static struct attribute *nvme_ns_attrs[] = {
|
||||
&dev_attr_ana_state.attr,
|
||||
&dev_attr_queue_depth.attr,
|
||||
&dev_attr_numa_nodes.attr,
|
||||
&dev_attr_delayed_removal_secs.attr,
|
||||
#endif
|
||||
&dev_attr_io_passthru_err_log_enabled.attr,
|
||||
NULL,
|
||||
@@ -296,6 +297,12 @@ static umode_t nvme_ns_attrs_are_visible(struct kobject *kobj,
|
||||
if (nvme_disk_is_ns_head(dev_to_disk(dev)))
|
||||
return 0;
|
||||
}
|
||||
if (a == &dev_attr_delayed_removal_secs.attr) {
|
||||
struct gendisk *disk = dev_to_disk(dev);
|
||||
|
||||
if (!nvme_disk_is_ns_head(disk))
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return a->mode;
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ static inline bool nvme_tcp_queue_more(struct nvme_tcp_queue *queue)
|
||||
}
|
||||
|
||||
static inline void nvme_tcp_queue_request(struct nvme_tcp_request *req,
|
||||
bool sync, bool last)
|
||||
bool last)
|
||||
{
|
||||
struct nvme_tcp_queue *queue = req->queue;
|
||||
bool empty;
|
||||
@@ -417,7 +417,7 @@ static inline void nvme_tcp_queue_request(struct nvme_tcp_request *req,
|
||||
* are on the same cpu, so we don't introduce contention.
|
||||
*/
|
||||
if (queue->io_cpu == raw_smp_processor_id() &&
|
||||
sync && empty && mutex_trylock(&queue->send_mutex)) {
|
||||
empty && mutex_trylock(&queue->send_mutex)) {
|
||||
nvme_tcp_send_all(queue);
|
||||
mutex_unlock(&queue->send_mutex);
|
||||
}
|
||||
@@ -770,7 +770,9 @@ static int nvme_tcp_handle_r2t(struct nvme_tcp_queue *queue,
|
||||
req->ttag = pdu->ttag;
|
||||
|
||||
nvme_tcp_setup_h2c_data_pdu(req);
|
||||
nvme_tcp_queue_request(req, false, true);
|
||||
|
||||
llist_add(&req->lentry, &queue->req_list);
|
||||
queue_work_on(queue->io_cpu, nvme_tcp_wq, &queue->io_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2385,7 +2387,7 @@ static int nvme_tcp_setup_ctrl(struct nvme_ctrl *ctrl, bool new)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ctrl->opts && ctrl->opts->concat && !ctrl->tls_pskid) {
|
||||
if (ctrl->opts->concat && !ctrl->tls_pskid) {
|
||||
/* See comments for nvme_tcp_key_revoke_needed() */
|
||||
dev_dbg(ctrl->device, "restart admin queue for secure concatenation\n");
|
||||
nvme_stop_keep_alive(ctrl);
|
||||
@@ -2637,7 +2639,7 @@ static void nvme_tcp_submit_async_event(struct nvme_ctrl *arg)
|
||||
ctrl->async_req.curr_bio = NULL;
|
||||
ctrl->async_req.data_len = 0;
|
||||
|
||||
nvme_tcp_queue_request(&ctrl->async_req, true, true);
|
||||
nvme_tcp_queue_request(&ctrl->async_req, true);
|
||||
}
|
||||
|
||||
static void nvme_tcp_complete_timed_out(struct request *rq)
|
||||
@@ -2789,7 +2791,7 @@ static blk_status_t nvme_tcp_queue_rq(struct blk_mq_hw_ctx *hctx,
|
||||
|
||||
nvme_start_request(rq);
|
||||
|
||||
nvme_tcp_queue_request(req, true, bd->last);
|
||||
nvme_tcp_queue_request(req, bd->last);
|
||||
|
||||
return BLK_STS_OK;
|
||||
}
|
||||
|
||||
@@ -63,14 +63,9 @@ static void nvmet_execute_create_sq(struct nvmet_req *req)
|
||||
if (status != NVME_SC_SUCCESS)
|
||||
goto complete;
|
||||
|
||||
/*
|
||||
* Note: The NVMe specification allows multiple SQs to use the same CQ.
|
||||
* However, the target code does not really support that. So for now,
|
||||
* prevent this and fail the command if sqid and cqid are different.
|
||||
*/
|
||||
if (!cqid || cqid != sqid) {
|
||||
pr_err("SQ %u: Unsupported CQID %u\n", sqid, cqid);
|
||||
status = NVME_SC_CQ_INVALID | NVME_STATUS_DNR;
|
||||
status = nvmet_check_io_cqid(ctrl, cqid, false);
|
||||
if (status != NVME_SC_SUCCESS) {
|
||||
pr_err("SQ %u: Invalid CQID %u\n", sqid, cqid);
|
||||
goto complete;
|
||||
}
|
||||
|
||||
@@ -79,7 +74,7 @@ static void nvmet_execute_create_sq(struct nvmet_req *req)
|
||||
goto complete;
|
||||
}
|
||||
|
||||
status = ctrl->ops->create_sq(ctrl, sqid, sq_flags, qsize, prp1);
|
||||
status = ctrl->ops->create_sq(ctrl, sqid, cqid, sq_flags, qsize, prp1);
|
||||
|
||||
complete:
|
||||
nvmet_req_complete(req, status);
|
||||
@@ -96,15 +91,16 @@ static void nvmet_execute_delete_cq(struct nvmet_req *req)
|
||||
goto complete;
|
||||
}
|
||||
|
||||
if (!cqid) {
|
||||
status = nvmet_check_io_cqid(ctrl, cqid, false);
|
||||
if (status != NVME_SC_SUCCESS)
|
||||
goto complete;
|
||||
|
||||
if (!ctrl->cqs[cqid] || nvmet_cq_in_use(ctrl->cqs[cqid])) {
|
||||
/* Some SQs are still using this CQ */
|
||||
status = NVME_SC_QID_INVALID | NVME_STATUS_DNR;
|
||||
goto complete;
|
||||
}
|
||||
|
||||
status = nvmet_check_cqid(ctrl, cqid);
|
||||
if (status != NVME_SC_SUCCESS)
|
||||
goto complete;
|
||||
|
||||
status = ctrl->ops->delete_cq(ctrl, cqid);
|
||||
|
||||
complete:
|
||||
@@ -127,12 +123,7 @@ static void nvmet_execute_create_cq(struct nvmet_req *req)
|
||||
goto complete;
|
||||
}
|
||||
|
||||
if (!cqid) {
|
||||
status = NVME_SC_QID_INVALID | NVME_STATUS_DNR;
|
||||
goto complete;
|
||||
}
|
||||
|
||||
status = nvmet_check_cqid(ctrl, cqid);
|
||||
status = nvmet_check_io_cqid(ctrl, cqid, true);
|
||||
if (status != NVME_SC_SUCCESS)
|
||||
goto complete;
|
||||
|
||||
|
||||
@@ -280,9 +280,12 @@ void nvmet_destroy_auth(struct nvmet_ctrl *ctrl)
|
||||
|
||||
bool nvmet_check_auth_status(struct nvmet_req *req)
|
||||
{
|
||||
if (req->sq->ctrl->host_key &&
|
||||
!req->sq->authenticated)
|
||||
return false;
|
||||
if (req->sq->ctrl->host_key) {
|
||||
if (req->sq->qid > 0)
|
||||
return true;
|
||||
if (!req->sq->authenticated)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -290,7 +293,7 @@ int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
|
||||
unsigned int shash_len)
|
||||
{
|
||||
struct crypto_shash *shash_tfm;
|
||||
struct shash_desc *shash;
|
||||
SHASH_DESC_ON_STACK(shash, shash_tfm);
|
||||
struct nvmet_ctrl *ctrl = req->sq->ctrl;
|
||||
const char *hash_name;
|
||||
u8 *challenge = req->sq->dhchap_c1;
|
||||
@@ -342,19 +345,13 @@ int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
|
||||
req->sq->dhchap_c1,
|
||||
challenge, shash_len);
|
||||
if (ret)
|
||||
goto out_free_challenge;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_debug("ctrl %d qid %d host response seq %u transaction %d\n",
|
||||
ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1,
|
||||
req->sq->dhchap_tid);
|
||||
|
||||
shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm),
|
||||
GFP_KERNEL);
|
||||
if (!shash) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_challenge;
|
||||
}
|
||||
shash->tfm = shash_tfm;
|
||||
ret = crypto_shash_init(shash);
|
||||
if (ret)
|
||||
@@ -389,8 +386,6 @@ int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
|
||||
goto out;
|
||||
ret = crypto_shash_final(shash, response);
|
||||
out:
|
||||
kfree(shash);
|
||||
out_free_challenge:
|
||||
if (challenge != req->sq->dhchap_c1)
|
||||
kfree(challenge);
|
||||
out_free_response:
|
||||
|
||||
@@ -813,11 +813,43 @@ void nvmet_req_complete(struct nvmet_req *req, u16 status)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nvmet_req_complete);
|
||||
|
||||
void nvmet_cq_init(struct nvmet_cq *cq)
|
||||
{
|
||||
refcount_set(&cq->ref, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nvmet_cq_init);
|
||||
|
||||
bool nvmet_cq_get(struct nvmet_cq *cq)
|
||||
{
|
||||
return refcount_inc_not_zero(&cq->ref);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nvmet_cq_get);
|
||||
|
||||
void nvmet_cq_put(struct nvmet_cq *cq)
|
||||
{
|
||||
if (refcount_dec_and_test(&cq->ref))
|
||||
nvmet_cq_destroy(cq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nvmet_cq_put);
|
||||
|
||||
void nvmet_cq_setup(struct nvmet_ctrl *ctrl, struct nvmet_cq *cq,
|
||||
u16 qid, u16 size)
|
||||
{
|
||||
cq->qid = qid;
|
||||
cq->size = size;
|
||||
|
||||
ctrl->cqs[qid] = cq;
|
||||
}
|
||||
|
||||
void nvmet_cq_destroy(struct nvmet_cq *cq)
|
||||
{
|
||||
struct nvmet_ctrl *ctrl = cq->ctrl;
|
||||
|
||||
if (ctrl) {
|
||||
ctrl->cqs[cq->qid] = NULL;
|
||||
nvmet_ctrl_put(cq->ctrl);
|
||||
cq->ctrl = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void nvmet_sq_setup(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq,
|
||||
@@ -837,37 +869,47 @@ static void nvmet_confirm_sq(struct percpu_ref *ref)
|
||||
complete(&sq->confirm_done);
|
||||
}
|
||||
|
||||
u16 nvmet_check_cqid(struct nvmet_ctrl *ctrl, u16 cqid)
|
||||
u16 nvmet_check_cqid(struct nvmet_ctrl *ctrl, u16 cqid, bool create)
|
||||
{
|
||||
if (!ctrl->sqs)
|
||||
if (!ctrl->cqs)
|
||||
return NVME_SC_INTERNAL | NVME_STATUS_DNR;
|
||||
|
||||
if (cqid > ctrl->subsys->max_qid)
|
||||
return NVME_SC_QID_INVALID | NVME_STATUS_DNR;
|
||||
|
||||
/*
|
||||
* Note: For PCI controllers, the NVMe specifications allows multiple
|
||||
* SQs to share a single CQ. However, we do not support this yet, so
|
||||
* check that there is no SQ defined for a CQ. If one exist, then the
|
||||
* CQ ID is invalid for creation as well as when the CQ is being
|
||||
* deleted (as that would mean that the SQ was not deleted before the
|
||||
* CQ).
|
||||
*/
|
||||
if (ctrl->sqs[cqid])
|
||||
if ((create && ctrl->cqs[cqid]) || (!create && !ctrl->cqs[cqid]))
|
||||
return NVME_SC_QID_INVALID | NVME_STATUS_DNR;
|
||||
|
||||
return NVME_SC_SUCCESS;
|
||||
}
|
||||
|
||||
u16 nvmet_check_io_cqid(struct nvmet_ctrl *ctrl, u16 cqid, bool create)
|
||||
{
|
||||
if (!cqid)
|
||||
return NVME_SC_QID_INVALID | NVME_STATUS_DNR;
|
||||
return nvmet_check_cqid(ctrl, cqid, create);
|
||||
}
|
||||
|
||||
bool nvmet_cq_in_use(struct nvmet_cq *cq)
|
||||
{
|
||||
return refcount_read(&cq->ref) > 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nvmet_cq_in_use);
|
||||
|
||||
u16 nvmet_cq_create(struct nvmet_ctrl *ctrl, struct nvmet_cq *cq,
|
||||
u16 qid, u16 size)
|
||||
{
|
||||
u16 status;
|
||||
|
||||
status = nvmet_check_cqid(ctrl, qid);
|
||||
status = nvmet_check_cqid(ctrl, qid, true);
|
||||
if (status != NVME_SC_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (!kref_get_unless_zero(&ctrl->ref))
|
||||
return NVME_SC_INTERNAL | NVME_STATUS_DNR;
|
||||
cq->ctrl = ctrl;
|
||||
|
||||
nvmet_cq_init(cq);
|
||||
nvmet_cq_setup(ctrl, cq, qid, size);
|
||||
|
||||
return NVME_SC_SUCCESS;
|
||||
@@ -891,7 +933,7 @@ u16 nvmet_check_sqid(struct nvmet_ctrl *ctrl, u16 sqid,
|
||||
}
|
||||
|
||||
u16 nvmet_sq_create(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq,
|
||||
u16 sqid, u16 size)
|
||||
struct nvmet_cq *cq, u16 sqid, u16 size)
|
||||
{
|
||||
u16 status;
|
||||
int ret;
|
||||
@@ -903,7 +945,7 @@ u16 nvmet_sq_create(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq,
|
||||
if (status != NVME_SC_SUCCESS)
|
||||
return status;
|
||||
|
||||
ret = nvmet_sq_init(sq);
|
||||
ret = nvmet_sq_init(sq, cq);
|
||||
if (ret) {
|
||||
status = NVME_SC_INTERNAL | NVME_STATUS_DNR;
|
||||
goto ctrl_put;
|
||||
@@ -935,6 +977,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)
|
||||
wait_for_completion(&sq->free_done);
|
||||
percpu_ref_exit(&sq->ref);
|
||||
nvmet_auth_sq_free(sq);
|
||||
nvmet_cq_put(sq->cq);
|
||||
|
||||
/*
|
||||
* we must reference the ctrl again after waiting for inflight IO
|
||||
@@ -967,18 +1010,23 @@ static void nvmet_sq_free(struct percpu_ref *ref)
|
||||
complete(&sq->free_done);
|
||||
}
|
||||
|
||||
int nvmet_sq_init(struct nvmet_sq *sq)
|
||||
int nvmet_sq_init(struct nvmet_sq *sq, struct nvmet_cq *cq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!nvmet_cq_get(cq))
|
||||
return -EINVAL;
|
||||
|
||||
ret = percpu_ref_init(&sq->ref, nvmet_sq_free, 0, GFP_KERNEL);
|
||||
if (ret) {
|
||||
pr_err("percpu_ref init failed!\n");
|
||||
nvmet_cq_put(cq);
|
||||
return ret;
|
||||
}
|
||||
init_completion(&sq->free_done);
|
||||
init_completion(&sq->confirm_done);
|
||||
nvmet_auth_sq_init(sq);
|
||||
sq->cq = cq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1108,13 +1156,13 @@ static u16 nvmet_parse_io_cmd(struct nvmet_req *req)
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq,
|
||||
struct nvmet_sq *sq, const struct nvmet_fabrics_ops *ops)
|
||||
bool nvmet_req_init(struct nvmet_req *req, struct nvmet_sq *sq,
|
||||
const struct nvmet_fabrics_ops *ops)
|
||||
{
|
||||
u8 flags = req->cmd->common.flags;
|
||||
u16 status;
|
||||
|
||||
req->cq = cq;
|
||||
req->cq = sq->cq;
|
||||
req->sq = sq;
|
||||
req->ops = ops;
|
||||
req->sg = NULL;
|
||||
@@ -1612,12 +1660,17 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
|
||||
if (!ctrl->sqs)
|
||||
goto out_free_changed_ns_list;
|
||||
|
||||
ctrl->cqs = kcalloc(subsys->max_qid + 1, sizeof(struct nvmet_cq *),
|
||||
GFP_KERNEL);
|
||||
if (!ctrl->cqs)
|
||||
goto out_free_sqs;
|
||||
|
||||
ret = ida_alloc_range(&cntlid_ida,
|
||||
subsys->cntlid_min, subsys->cntlid_max,
|
||||
GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
args->status = NVME_SC_CONNECT_CTRL_BUSY | NVME_STATUS_DNR;
|
||||
goto out_free_sqs;
|
||||
goto out_free_cqs;
|
||||
}
|
||||
ctrl->cntlid = ret;
|
||||
|
||||
@@ -1676,6 +1729,8 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
|
||||
mutex_unlock(&subsys->lock);
|
||||
nvmet_stop_keep_alive_timer(ctrl);
|
||||
ida_free(&cntlid_ida, ctrl->cntlid);
|
||||
out_free_cqs:
|
||||
kfree(ctrl->cqs);
|
||||
out_free_sqs:
|
||||
kfree(ctrl->sqs);
|
||||
out_free_changed_ns_list:
|
||||
@@ -1712,6 +1767,7 @@ static void nvmet_ctrl_free(struct kref *ref)
|
||||
|
||||
nvmet_async_events_free(ctrl);
|
||||
kfree(ctrl->sqs);
|
||||
kfree(ctrl->cqs);
|
||||
kfree(ctrl->changed_ns_list);
|
||||
kfree(ctrl);
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ static void nvmet_format_discovery_entry(struct nvmf_disc_rsp_page_hdr *hdr,
|
||||
memcpy(e->trsvcid, port->disc_addr.trsvcid, NVMF_TRSVCID_SIZE);
|
||||
memcpy(e->traddr, traddr, NVMF_TRADDR_SIZE);
|
||||
memcpy(e->tsas.common, port->disc_addr.tsas.common, NVMF_TSAS_SIZE);
|
||||
strncpy(e->subnqn, subsys_nqn, NVMF_NQN_SIZE);
|
||||
strscpy(e->subnqn, subsys_nqn, NVMF_NQN_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -208,6 +208,14 @@ static u16 nvmet_install_queue(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
|
||||
return NVME_SC_CONNECT_CTRL_BUSY | NVME_STATUS_DNR;
|
||||
}
|
||||
|
||||
kref_get(&ctrl->ref);
|
||||
old = cmpxchg(&req->cq->ctrl, NULL, ctrl);
|
||||
if (old) {
|
||||
pr_warn("queue already connected!\n");
|
||||
req->error_loc = offsetof(struct nvmf_connect_command, opcode);
|
||||
return NVME_SC_CONNECT_CTRL_BUSY | NVME_STATUS_DNR;
|
||||
}
|
||||
|
||||
/* note: convert queue size from 0's-based value to 1's-based value */
|
||||
nvmet_cq_setup(ctrl, req->cq, qid, sqsize + 1);
|
||||
nvmet_sq_setup(ctrl, req->sq, qid, sqsize + 1);
|
||||
@@ -239,8 +247,8 @@ static u32 nvmet_connect_result(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq)
|
||||
bool needs_auth = nvmet_has_auth(ctrl, sq);
|
||||
key_serial_t keyid = nvmet_queue_tls_keyid(sq);
|
||||
|
||||
/* Do not authenticate I/O queues for secure concatenation */
|
||||
if (ctrl->concat && sq->qid)
|
||||
/* Do not authenticate I/O queues */
|
||||
if (sq->qid)
|
||||
needs_auth = false;
|
||||
|
||||
if (keyid)
|
||||
|
||||
@@ -816,7 +816,8 @@ nvmet_fc_alloc_target_queue(struct nvmet_fc_tgt_assoc *assoc,
|
||||
|
||||
nvmet_fc_prep_fcp_iodlist(assoc->tgtport, queue);
|
||||
|
||||
ret = nvmet_sq_init(&queue->nvme_sq);
|
||||
nvmet_cq_init(&queue->nvme_cq);
|
||||
ret = nvmet_sq_init(&queue->nvme_sq, &queue->nvme_cq);
|
||||
if (ret)
|
||||
goto out_fail_iodlist;
|
||||
|
||||
@@ -826,6 +827,7 @@ nvmet_fc_alloc_target_queue(struct nvmet_fc_tgt_assoc *assoc,
|
||||
return queue;
|
||||
|
||||
out_fail_iodlist:
|
||||
nvmet_cq_put(&queue->nvme_cq);
|
||||
nvmet_fc_destroy_fcp_iodlist(assoc->tgtport, queue);
|
||||
destroy_workqueue(queue->work_q);
|
||||
out_free_queue:
|
||||
@@ -934,6 +936,7 @@ nvmet_fc_delete_target_queue(struct nvmet_fc_tgt_queue *queue)
|
||||
flush_workqueue(queue->work_q);
|
||||
|
||||
nvmet_sq_destroy(&queue->nvme_sq);
|
||||
nvmet_cq_put(&queue->nvme_cq);
|
||||
|
||||
nvmet_fc_tgt_q_put(queue);
|
||||
}
|
||||
@@ -1254,6 +1257,7 @@ nvmet_fc_portentry_bind(struct nvmet_fc_tgtport *tgtport,
|
||||
{
|
||||
lockdep_assert_held(&nvmet_fc_tgtlock);
|
||||
|
||||
nvmet_fc_tgtport_get(tgtport);
|
||||
pe->tgtport = tgtport;
|
||||
tgtport->pe = pe;
|
||||
|
||||
@@ -1273,8 +1277,10 @@ nvmet_fc_portentry_unbind(struct nvmet_fc_port_entry *pe)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&nvmet_fc_tgtlock, flags);
|
||||
if (pe->tgtport)
|
||||
if (pe->tgtport) {
|
||||
nvmet_fc_tgtport_put(pe->tgtport);
|
||||
pe->tgtport->pe = NULL;
|
||||
}
|
||||
list_del(&pe->pe_list);
|
||||
spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags);
|
||||
}
|
||||
@@ -1292,8 +1298,10 @@ nvmet_fc_portentry_unbind_tgt(struct nvmet_fc_tgtport *tgtport)
|
||||
|
||||
spin_lock_irqsave(&nvmet_fc_tgtlock, flags);
|
||||
pe = tgtport->pe;
|
||||
if (pe)
|
||||
if (pe) {
|
||||
nvmet_fc_tgtport_put(pe->tgtport);
|
||||
pe->tgtport = NULL;
|
||||
}
|
||||
tgtport->pe = NULL;
|
||||
spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags);
|
||||
}
|
||||
@@ -1316,6 +1324,9 @@ nvmet_fc_portentry_rebind_tgt(struct nvmet_fc_tgtport *tgtport)
|
||||
list_for_each_entry(pe, &nvmet_fc_portentry_list, pe_list) {
|
||||
if (tgtport->fc_target_port.node_name == pe->node_name &&
|
||||
tgtport->fc_target_port.port_name == pe->port_name) {
|
||||
if (!nvmet_fc_tgtport_get(tgtport))
|
||||
continue;
|
||||
|
||||
WARN_ON(pe->tgtport);
|
||||
tgtport->pe = pe;
|
||||
pe->tgtport = tgtport;
|
||||
@@ -1580,6 +1591,39 @@ nvmet_fc_delete_ctrl(struct nvmet_ctrl *ctrl)
|
||||
spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
nvmet_fc_free_pending_reqs(struct nvmet_fc_tgtport *tgtport)
|
||||
{
|
||||
struct nvmet_fc_ls_req_op *lsop;
|
||||
struct nvmefc_ls_req *lsreq;
|
||||
struct nvmet_fc_ls_iod *iod;
|
||||
int i;
|
||||
|
||||
iod = tgtport->iod;
|
||||
for (i = 0; i < NVMET_LS_CTX_COUNT; iod++, i++)
|
||||
cancel_work(&iod->work);
|
||||
|
||||
/*
|
||||
* After this point the connection is lost and thus any pending
|
||||
* request can't be processed by the normal completion path. This
|
||||
* is likely a request from nvmet_fc_send_ls_req_async.
|
||||
*/
|
||||
while ((lsop = list_first_entry_or_null(&tgtport->ls_req_list,
|
||||
struct nvmet_fc_ls_req_op, lsreq_list))) {
|
||||
list_del(&lsop->lsreq_list);
|
||||
|
||||
if (!lsop->req_queued)
|
||||
continue;
|
||||
|
||||
lsreq = &lsop->ls_req;
|
||||
fc_dma_unmap_single(tgtport->dev, lsreq->rqstdma,
|
||||
(lsreq->rqstlen + lsreq->rsplen),
|
||||
DMA_BIDIRECTIONAL);
|
||||
nvmet_fc_tgtport_put(tgtport);
|
||||
kfree(lsop);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* nvmet_fc_unregister_targetport - transport entry point called by an
|
||||
* LLDD to deregister/remove a previously
|
||||
@@ -1608,13 +1652,7 @@ nvmet_fc_unregister_targetport(struct nvmet_fc_target_port *target_port)
|
||||
|
||||
flush_workqueue(nvmet_wq);
|
||||
|
||||
/*
|
||||
* should terminate LS's as well. However, LS's will be generated
|
||||
* at the tail end of association termination, so they likely don't
|
||||
* exist yet. And even if they did, it's worthwhile to just let
|
||||
* them finish and targetport ref counting will clean things up.
|
||||
*/
|
||||
|
||||
nvmet_fc_free_pending_reqs(tgtport);
|
||||
nvmet_fc_tgtport_put(tgtport);
|
||||
|
||||
return 0;
|
||||
@@ -2531,10 +2569,8 @@ nvmet_fc_handle_fcp_rqst(struct nvmet_fc_tgtport *tgtport,
|
||||
fod->data_sg = NULL;
|
||||
fod->data_sg_cnt = 0;
|
||||
|
||||
ret = nvmet_req_init(&fod->req,
|
||||
&fod->queue->nvme_cq,
|
||||
&fod->queue->nvme_sq,
|
||||
&nvmet_fc_tgt_fcp_ops);
|
||||
ret = nvmet_req_init(&fod->req, &fod->queue->nvme_sq,
|
||||
&nvmet_fc_tgt_fcp_ops);
|
||||
if (!ret) {
|
||||
/* bad SQE content or invalid ctrl state */
|
||||
/* nvmet layer has already called op done to send rsp. */
|
||||
@@ -2860,12 +2896,17 @@ nvmet_fc_add_port(struct nvmet_port *port)
|
||||
list_for_each_entry(tgtport, &nvmet_fc_target_list, tgt_list) {
|
||||
if ((tgtport->fc_target_port.node_name == traddr.nn) &&
|
||||
(tgtport->fc_target_port.port_name == traddr.pn)) {
|
||||
if (!nvmet_fc_tgtport_get(tgtport))
|
||||
continue;
|
||||
|
||||
/* a FC port can only be 1 nvmet port id */
|
||||
if (!tgtport->pe) {
|
||||
nvmet_fc_portentry_bind(tgtport, pe, port);
|
||||
ret = 0;
|
||||
} else
|
||||
ret = -EALREADY;
|
||||
|
||||
nvmet_fc_tgtport_put(tgtport);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2881,11 +2922,21 @@ static void
|
||||
nvmet_fc_remove_port(struct nvmet_port *port)
|
||||
{
|
||||
struct nvmet_fc_port_entry *pe = port->priv;
|
||||
struct nvmet_fc_tgtport *tgtport = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&nvmet_fc_tgtlock, flags);
|
||||
if (pe->tgtport && nvmet_fc_tgtport_get(pe->tgtport))
|
||||
tgtport = pe->tgtport;
|
||||
spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags);
|
||||
|
||||
nvmet_fc_portentry_unbind(pe);
|
||||
|
||||
/* terminate any outstanding associations */
|
||||
__nvmet_fc_free_assocs(pe->tgtport);
|
||||
if (tgtport) {
|
||||
/* terminate any outstanding associations */
|
||||
__nvmet_fc_free_assocs(tgtport);
|
||||
nvmet_fc_tgtport_put(tgtport);
|
||||
}
|
||||
|
||||
kfree(pe);
|
||||
}
|
||||
@@ -2894,10 +2945,21 @@ static void
|
||||
nvmet_fc_discovery_chg(struct nvmet_port *port)
|
||||
{
|
||||
struct nvmet_fc_port_entry *pe = port->priv;
|
||||
struct nvmet_fc_tgtport *tgtport = pe->tgtport;
|
||||
struct nvmet_fc_tgtport *tgtport = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&nvmet_fc_tgtlock, flags);
|
||||
if (pe->tgtport && nvmet_fc_tgtport_get(pe->tgtport))
|
||||
tgtport = pe->tgtport;
|
||||
spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags);
|
||||
|
||||
if (!tgtport)
|
||||
return;
|
||||
|
||||
if (tgtport && tgtport->ops->discovery_event)
|
||||
tgtport->ops->discovery_event(&tgtport->fc_target_port);
|
||||
|
||||
nvmet_fc_tgtport_put(tgtport);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
|
||||
@@ -207,7 +207,6 @@ static LIST_HEAD(fcloop_nports);
|
||||
struct fcloop_lport {
|
||||
struct nvme_fc_local_port *localport;
|
||||
struct list_head lport_list;
|
||||
struct completion unreg_done;
|
||||
refcount_t ref;
|
||||
};
|
||||
|
||||
@@ -215,6 +214,9 @@ struct fcloop_lport_priv {
|
||||
struct fcloop_lport *lport;
|
||||
};
|
||||
|
||||
/* The port is already being removed, avoid double free */
|
||||
#define PORT_DELETED 0
|
||||
|
||||
struct fcloop_rport {
|
||||
struct nvme_fc_remote_port *remoteport;
|
||||
struct nvmet_fc_target_port *targetport;
|
||||
@@ -223,6 +225,7 @@ struct fcloop_rport {
|
||||
spinlock_t lock;
|
||||
struct list_head ls_list;
|
||||
struct work_struct ls_work;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
struct fcloop_tport {
|
||||
@@ -233,6 +236,7 @@ struct fcloop_tport {
|
||||
spinlock_t lock;
|
||||
struct list_head ls_list;
|
||||
struct work_struct ls_work;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
struct fcloop_nport {
|
||||
@@ -288,6 +292,9 @@ struct fcloop_ini_fcpreq {
|
||||
spinlock_t inilock;
|
||||
};
|
||||
|
||||
/* SLAB cache for fcloop_lsreq structures */
|
||||
static struct kmem_cache *lsreq_cache;
|
||||
|
||||
static inline struct fcloop_lsreq *
|
||||
ls_rsp_to_lsreq(struct nvmefc_ls_rsp *lsrsp)
|
||||
{
|
||||
@@ -338,6 +345,7 @@ fcloop_rport_lsrqst_work(struct work_struct *work)
|
||||
* callee may free memory containing tls_req.
|
||||
* do not reference lsreq after this.
|
||||
*/
|
||||
kmem_cache_free(lsreq_cache, tls_req);
|
||||
|
||||
spin_lock(&rport->lock);
|
||||
}
|
||||
@@ -349,10 +357,13 @@ fcloop_h2t_ls_req(struct nvme_fc_local_port *localport,
|
||||
struct nvme_fc_remote_port *remoteport,
|
||||
struct nvmefc_ls_req *lsreq)
|
||||
{
|
||||
struct fcloop_lsreq *tls_req = lsreq->private;
|
||||
struct fcloop_rport *rport = remoteport->private;
|
||||
struct fcloop_lsreq *tls_req;
|
||||
int ret = 0;
|
||||
|
||||
tls_req = kmem_cache_alloc(lsreq_cache, GFP_KERNEL);
|
||||
if (!tls_req)
|
||||
return -ENOMEM;
|
||||
tls_req->lsreq = lsreq;
|
||||
INIT_LIST_HEAD(&tls_req->ls_list);
|
||||
|
||||
@@ -389,14 +400,17 @@ fcloop_h2t_xmt_ls_rsp(struct nvmet_fc_target_port *targetport,
|
||||
|
||||
lsrsp->done(lsrsp);
|
||||
|
||||
if (remoteport) {
|
||||
rport = remoteport->private;
|
||||
spin_lock(&rport->lock);
|
||||
list_add_tail(&tls_req->ls_list, &rport->ls_list);
|
||||
spin_unlock(&rport->lock);
|
||||
queue_work(nvmet_wq, &rport->ls_work);
|
||||
if (!remoteport) {
|
||||
kmem_cache_free(lsreq_cache, tls_req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rport = remoteport->private;
|
||||
spin_lock(&rport->lock);
|
||||
list_add_tail(&tls_req->ls_list, &rport->ls_list);
|
||||
spin_unlock(&rport->lock);
|
||||
queue_work(nvmet_wq, &rport->ls_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -422,6 +436,7 @@ fcloop_tport_lsrqst_work(struct work_struct *work)
|
||||
* callee may free memory containing tls_req.
|
||||
* do not reference lsreq after this.
|
||||
*/
|
||||
kmem_cache_free(lsreq_cache, tls_req);
|
||||
|
||||
spin_lock(&tport->lock);
|
||||
}
|
||||
@@ -432,8 +447,8 @@ static int
|
||||
fcloop_t2h_ls_req(struct nvmet_fc_target_port *targetport, void *hosthandle,
|
||||
struct nvmefc_ls_req *lsreq)
|
||||
{
|
||||
struct fcloop_lsreq *tls_req = lsreq->private;
|
||||
struct fcloop_tport *tport = targetport->private;
|
||||
struct fcloop_lsreq *tls_req;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
@@ -441,6 +456,10 @@ fcloop_t2h_ls_req(struct nvmet_fc_target_port *targetport, void *hosthandle,
|
||||
* hosthandle ignored as fcloop currently is
|
||||
* 1:1 tgtport vs remoteport
|
||||
*/
|
||||
|
||||
tls_req = kmem_cache_alloc(lsreq_cache, GFP_KERNEL);
|
||||
if (!tls_req)
|
||||
return -ENOMEM;
|
||||
tls_req->lsreq = lsreq;
|
||||
INIT_LIST_HEAD(&tls_req->ls_list);
|
||||
|
||||
@@ -457,6 +476,9 @@ fcloop_t2h_ls_req(struct nvmet_fc_target_port *targetport, void *hosthandle,
|
||||
ret = nvme_fc_rcv_ls_req(tport->remoteport, &tls_req->ls_rsp,
|
||||
lsreq->rqstaddr, lsreq->rqstlen);
|
||||
|
||||
if (ret)
|
||||
kmem_cache_free(lsreq_cache, tls_req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -471,18 +493,30 @@ fcloop_t2h_xmt_ls_rsp(struct nvme_fc_local_port *localport,
|
||||
struct nvmet_fc_target_port *targetport = rport->targetport;
|
||||
struct fcloop_tport *tport;
|
||||
|
||||
if (!targetport) {
|
||||
/*
|
||||
* The target port is gone. The target doesn't expect any
|
||||
* response anymore and the ->done call is not valid
|
||||
* because the resources have been freed by
|
||||
* nvmet_fc_free_pending_reqs.
|
||||
*
|
||||
* We end up here from delete association exchange:
|
||||
* nvmet_fc_xmt_disconnect_assoc sends an async request.
|
||||
*/
|
||||
kmem_cache_free(lsreq_cache, tls_req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(lsreq->rspaddr, lsrsp->rspbuf,
|
||||
((lsreq->rsplen < lsrsp->rsplen) ?
|
||||
lsreq->rsplen : lsrsp->rsplen));
|
||||
lsrsp->done(lsrsp);
|
||||
|
||||
if (targetport) {
|
||||
tport = targetport->private;
|
||||
spin_lock(&tport->lock);
|
||||
list_add_tail(&tls_req->ls_list, &tport->ls_list);
|
||||
spin_unlock(&tport->lock);
|
||||
queue_work(nvmet_wq, &tport->ls_work);
|
||||
}
|
||||
tport = targetport->private;
|
||||
spin_lock(&tport->lock);
|
||||
list_add_tail(&tls_req->ls_list, &tport->ls_list);
|
||||
spin_unlock(&tport->lock);
|
||||
queue_work(nvmet_wq, &tport->ls_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -566,7 +600,8 @@ fcloop_call_host_done(struct nvmefc_fcp_req *fcpreq,
|
||||
}
|
||||
|
||||
/* release original io reference on tgt struct */
|
||||
fcloop_tfcp_req_put(tfcp_req);
|
||||
if (tfcp_req)
|
||||
fcloop_tfcp_req_put(tfcp_req);
|
||||
}
|
||||
|
||||
static bool drop_fabric_opcode;
|
||||
@@ -618,12 +653,13 @@ fcloop_fcp_recv_work(struct work_struct *work)
|
||||
{
|
||||
struct fcloop_fcpreq *tfcp_req =
|
||||
container_of(work, struct fcloop_fcpreq, fcp_rcv_work);
|
||||
struct nvmefc_fcp_req *fcpreq = tfcp_req->fcpreq;
|
||||
struct nvmefc_fcp_req *fcpreq;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
bool aborted = false;
|
||||
|
||||
spin_lock_irqsave(&tfcp_req->reqlock, flags);
|
||||
fcpreq = tfcp_req->fcpreq;
|
||||
switch (tfcp_req->inistate) {
|
||||
case INI_IO_START:
|
||||
tfcp_req->inistate = INI_IO_ACTIVE;
|
||||
@@ -638,16 +674,19 @@ fcloop_fcp_recv_work(struct work_struct *work)
|
||||
}
|
||||
spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
|
||||
|
||||
if (unlikely(aborted))
|
||||
ret = -ECANCELED;
|
||||
else {
|
||||
if (likely(!check_for_drop(tfcp_req)))
|
||||
ret = nvmet_fc_rcv_fcp_req(tfcp_req->tport->targetport,
|
||||
&tfcp_req->tgt_fcp_req,
|
||||
fcpreq->cmdaddr, fcpreq->cmdlen);
|
||||
else
|
||||
pr_info("%s: dropped command ********\n", __func__);
|
||||
if (unlikely(aborted)) {
|
||||
/* the abort handler will call fcloop_call_host_done */
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlikely(check_for_drop(tfcp_req))) {
|
||||
pr_info("%s: dropped command ********\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = nvmet_fc_rcv_fcp_req(tfcp_req->tport->targetport,
|
||||
&tfcp_req->tgt_fcp_req,
|
||||
fcpreq->cmdaddr, fcpreq->cmdlen);
|
||||
if (ret)
|
||||
fcloop_call_host_done(fcpreq, tfcp_req, ret);
|
||||
}
|
||||
@@ -662,15 +701,17 @@ fcloop_fcp_abort_recv_work(struct work_struct *work)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tfcp_req->reqlock, flags);
|
||||
fcpreq = tfcp_req->fcpreq;
|
||||
switch (tfcp_req->inistate) {
|
||||
case INI_IO_ABORTED:
|
||||
fcpreq = tfcp_req->fcpreq;
|
||||
tfcp_req->fcpreq = NULL;
|
||||
break;
|
||||
case INI_IO_COMPLETED:
|
||||
completed = true;
|
||||
break;
|
||||
default:
|
||||
spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
|
||||
fcloop_tfcp_req_put(tfcp_req);
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
@@ -686,10 +727,6 @@ fcloop_fcp_abort_recv_work(struct work_struct *work)
|
||||
nvmet_fc_rcv_fcp_abort(tfcp_req->tport->targetport,
|
||||
&tfcp_req->tgt_fcp_req);
|
||||
|
||||
spin_lock_irqsave(&tfcp_req->reqlock, flags);
|
||||
tfcp_req->fcpreq = NULL;
|
||||
spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
|
||||
|
||||
fcloop_call_host_done(fcpreq, tfcp_req, -ECANCELED);
|
||||
/* call_host_done releases reference for abort downcall */
|
||||
}
|
||||
@@ -958,13 +995,16 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport,
|
||||
|
||||
spin_lock(&inireq->inilock);
|
||||
tfcp_req = inireq->tfcp_req;
|
||||
if (tfcp_req)
|
||||
fcloop_tfcp_req_get(tfcp_req);
|
||||
if (tfcp_req) {
|
||||
if (!fcloop_tfcp_req_get(tfcp_req))
|
||||
tfcp_req = NULL;
|
||||
}
|
||||
spin_unlock(&inireq->inilock);
|
||||
|
||||
if (!tfcp_req)
|
||||
if (!tfcp_req) {
|
||||
/* abort has already been called */
|
||||
return;
|
||||
goto out_host_done;
|
||||
}
|
||||
|
||||
/* break initiator/target relationship for io */
|
||||
spin_lock_irqsave(&tfcp_req->reqlock, flags);
|
||||
@@ -979,7 +1019,7 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport,
|
||||
default:
|
||||
spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
|
||||
WARN_ON(1);
|
||||
return;
|
||||
goto out_host_done;
|
||||
}
|
||||
spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
|
||||
|
||||
@@ -993,6 +1033,11 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport,
|
||||
*/
|
||||
fcloop_tfcp_req_put(tfcp_req);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
out_host_done:
|
||||
fcloop_call_host_done(fcpreq, tfcp_req, -ECANCELED);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1019,9 +1064,18 @@ fcloop_lport_get(struct fcloop_lport *lport)
|
||||
static void
|
||||
fcloop_nport_put(struct fcloop_nport *nport)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!refcount_dec_and_test(&nport->ref))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&fcloop_lock, flags);
|
||||
list_del(&nport->nport_list);
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
if (nport->lport)
|
||||
fcloop_lport_put(nport->lport);
|
||||
|
||||
kfree(nport);
|
||||
}
|
||||
|
||||
@@ -1037,9 +1091,6 @@ fcloop_localport_delete(struct nvme_fc_local_port *localport)
|
||||
struct fcloop_lport_priv *lport_priv = localport->private;
|
||||
struct fcloop_lport *lport = lport_priv->lport;
|
||||
|
||||
/* release any threads waiting for the unreg to complete */
|
||||
complete(&lport->unreg_done);
|
||||
|
||||
fcloop_lport_put(lport);
|
||||
}
|
||||
|
||||
@@ -1047,18 +1098,38 @@ static void
|
||||
fcloop_remoteport_delete(struct nvme_fc_remote_port *remoteport)
|
||||
{
|
||||
struct fcloop_rport *rport = remoteport->private;
|
||||
bool put_port = false;
|
||||
unsigned long flags;
|
||||
|
||||
flush_work(&rport->ls_work);
|
||||
fcloop_nport_put(rport->nport);
|
||||
|
||||
spin_lock_irqsave(&fcloop_lock, flags);
|
||||
if (!test_and_set_bit(PORT_DELETED, &rport->flags))
|
||||
put_port = true;
|
||||
rport->nport->rport = NULL;
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
if (put_port)
|
||||
fcloop_nport_put(rport->nport);
|
||||
}
|
||||
|
||||
static void
|
||||
fcloop_targetport_delete(struct nvmet_fc_target_port *targetport)
|
||||
{
|
||||
struct fcloop_tport *tport = targetport->private;
|
||||
bool put_port = false;
|
||||
unsigned long flags;
|
||||
|
||||
flush_work(&tport->ls_work);
|
||||
fcloop_nport_put(tport->nport);
|
||||
|
||||
spin_lock_irqsave(&fcloop_lock, flags);
|
||||
if (!test_and_set_bit(PORT_DELETED, &tport->flags))
|
||||
put_port = true;
|
||||
tport->nport->tport = NULL;
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
if (put_port)
|
||||
fcloop_nport_put(tport->nport);
|
||||
}
|
||||
|
||||
#define FCLOOP_HW_QUEUES 4
|
||||
@@ -1082,7 +1153,6 @@ static struct nvme_fc_port_template fctemplate = {
|
||||
/* sizes of additional private data for data structures */
|
||||
.local_priv_sz = sizeof(struct fcloop_lport_priv),
|
||||
.remote_priv_sz = sizeof(struct fcloop_rport),
|
||||
.lsrqst_priv_sz = sizeof(struct fcloop_lsreq),
|
||||
.fcprqst_priv_sz = sizeof(struct fcloop_ini_fcpreq),
|
||||
};
|
||||
|
||||
@@ -1105,7 +1175,6 @@ static struct nvmet_fc_target_template tgttemplate = {
|
||||
.target_features = 0,
|
||||
/* sizes of additional private data for data structures */
|
||||
.target_priv_sz = sizeof(struct fcloop_tport),
|
||||
.lsrqst_priv_sz = sizeof(struct fcloop_lsreq),
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
@@ -1170,51 +1239,92 @@ fcloop_create_local_port(struct device *dev, struct device_attribute *attr,
|
||||
}
|
||||
|
||||
static int
|
||||
__wait_localport_unreg(struct fcloop_lport *lport)
|
||||
__localport_unreg(struct fcloop_lport *lport)
|
||||
{
|
||||
int ret;
|
||||
|
||||
init_completion(&lport->unreg_done);
|
||||
|
||||
ret = nvme_fc_unregister_localport(lport->localport);
|
||||
|
||||
if (!ret)
|
||||
wait_for_completion(&lport->unreg_done);
|
||||
|
||||
return ret;
|
||||
return nvme_fc_unregister_localport(lport->localport);
|
||||
}
|
||||
|
||||
static struct fcloop_nport *
|
||||
__fcloop_nport_lookup(u64 node_name, u64 port_name)
|
||||
{
|
||||
struct fcloop_nport *nport;
|
||||
|
||||
list_for_each_entry(nport, &fcloop_nports, nport_list) {
|
||||
if (nport->node_name != node_name ||
|
||||
nport->port_name != port_name)
|
||||
continue;
|
||||
|
||||
if (fcloop_nport_get(nport))
|
||||
return nport;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct fcloop_nport *
|
||||
fcloop_nport_lookup(u64 node_name, u64 port_name)
|
||||
{
|
||||
struct fcloop_nport *nport;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&fcloop_lock, flags);
|
||||
nport = __fcloop_nport_lookup(node_name, port_name);
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
return nport;
|
||||
}
|
||||
|
||||
static struct fcloop_lport *
|
||||
__fcloop_lport_lookup(u64 node_name, u64 port_name)
|
||||
{
|
||||
struct fcloop_lport *lport;
|
||||
|
||||
list_for_each_entry(lport, &fcloop_lports, lport_list) {
|
||||
if (lport->localport->node_name != node_name ||
|
||||
lport->localport->port_name != port_name)
|
||||
continue;
|
||||
|
||||
if (fcloop_lport_get(lport))
|
||||
return lport;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct fcloop_lport *
|
||||
fcloop_lport_lookup(u64 node_name, u64 port_name)
|
||||
{
|
||||
struct fcloop_lport *lport;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&fcloop_lock, flags);
|
||||
lport = __fcloop_lport_lookup(node_name, port_name);
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
return lport;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
fcloop_delete_local_port(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct fcloop_lport *tlport, *lport = NULL;
|
||||
struct fcloop_lport *lport;
|
||||
u64 nodename, portname;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
ret = fcloop_parse_nm_options(dev, &nodename, &portname, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&fcloop_lock, flags);
|
||||
|
||||
list_for_each_entry(tlport, &fcloop_lports, lport_list) {
|
||||
if (tlport->localport->node_name == nodename &&
|
||||
tlport->localport->port_name == portname) {
|
||||
if (!fcloop_lport_get(tlport))
|
||||
break;
|
||||
lport = tlport;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
lport = fcloop_lport_lookup(nodename, portname);
|
||||
if (!lport)
|
||||
return -ENOENT;
|
||||
|
||||
ret = __wait_localport_unreg(lport);
|
||||
ret = __localport_unreg(lport);
|
||||
fcloop_lport_put(lport);
|
||||
|
||||
return ret ? ret : count;
|
||||
@@ -1223,8 +1333,8 @@ fcloop_delete_local_port(struct device *dev, struct device_attribute *attr,
|
||||
static struct fcloop_nport *
|
||||
fcloop_alloc_nport(const char *buf, size_t count, bool remoteport)
|
||||
{
|
||||
struct fcloop_nport *newnport, *nport = NULL;
|
||||
struct fcloop_lport *tmplport, *lport = NULL;
|
||||
struct fcloop_nport *newnport, *nport;
|
||||
struct fcloop_lport *lport;
|
||||
struct fcloop_ctrl_options *opts;
|
||||
unsigned long flags;
|
||||
u32 opts_mask = (remoteport) ? RPORT_OPTS : TGTPORT_OPTS;
|
||||
@@ -1239,10 +1349,8 @@ fcloop_alloc_nport(const char *buf, size_t count, bool remoteport)
|
||||
goto out_free_opts;
|
||||
|
||||
/* everything there ? */
|
||||
if ((opts->mask & opts_mask) != opts_mask) {
|
||||
ret = -EINVAL;
|
||||
if ((opts->mask & opts_mask) != opts_mask)
|
||||
goto out_free_opts;
|
||||
}
|
||||
|
||||
newnport = kzalloc(sizeof(*newnport), GFP_KERNEL);
|
||||
if (!newnport)
|
||||
@@ -1258,60 +1366,61 @@ fcloop_alloc_nport(const char *buf, size_t count, bool remoteport)
|
||||
refcount_set(&newnport->ref, 1);
|
||||
|
||||
spin_lock_irqsave(&fcloop_lock, flags);
|
||||
|
||||
list_for_each_entry(tmplport, &fcloop_lports, lport_list) {
|
||||
if (tmplport->localport->node_name == opts->wwnn &&
|
||||
tmplport->localport->port_name == opts->wwpn)
|
||||
goto out_invalid_opts;
|
||||
|
||||
if (tmplport->localport->node_name == opts->lpwwnn &&
|
||||
tmplport->localport->port_name == opts->lpwwpn)
|
||||
lport = tmplport;
|
||||
lport = __fcloop_lport_lookup(opts->wwnn, opts->wwpn);
|
||||
if (lport) {
|
||||
/* invalid configuration */
|
||||
fcloop_lport_put(lport);
|
||||
goto out_free_newnport;
|
||||
}
|
||||
|
||||
if (remoteport) {
|
||||
if (!lport)
|
||||
goto out_invalid_opts;
|
||||
newnport->lport = lport;
|
||||
}
|
||||
|
||||
list_for_each_entry(nport, &fcloop_nports, nport_list) {
|
||||
if (nport->node_name == opts->wwnn &&
|
||||
nport->port_name == opts->wwpn) {
|
||||
if ((remoteport && nport->rport) ||
|
||||
(!remoteport && nport->tport)) {
|
||||
nport = NULL;
|
||||
goto out_invalid_opts;
|
||||
}
|
||||
|
||||
fcloop_nport_get(nport);
|
||||
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
if (remoteport)
|
||||
nport->lport = lport;
|
||||
if (opts->mask & NVMF_OPT_ROLES)
|
||||
nport->port_role = opts->roles;
|
||||
if (opts->mask & NVMF_OPT_FCADDR)
|
||||
nport->port_id = opts->fcaddr;
|
||||
lport = __fcloop_lport_lookup(opts->lpwwnn, opts->lpwwpn);
|
||||
if (!lport) {
|
||||
/* invalid configuration */
|
||||
goto out_free_newnport;
|
||||
}
|
||||
}
|
||||
|
||||
list_add_tail(&newnport->nport_list, &fcloop_nports);
|
||||
nport = __fcloop_nport_lookup(opts->wwnn, opts->wwpn);
|
||||
if (nport) {
|
||||
if ((remoteport && nport->rport) ||
|
||||
(!remoteport && nport->tport)) {
|
||||
/* invalid configuration */
|
||||
goto out_put_nport;
|
||||
}
|
||||
|
||||
/* found existing nport, discard the new nport */
|
||||
kfree(newnport);
|
||||
} else {
|
||||
list_add_tail(&newnport->nport_list, &fcloop_nports);
|
||||
nport = newnport;
|
||||
}
|
||||
|
||||
if (opts->mask & NVMF_OPT_ROLES)
|
||||
nport->port_role = opts->roles;
|
||||
if (opts->mask & NVMF_OPT_FCADDR)
|
||||
nport->port_id = opts->fcaddr;
|
||||
if (lport) {
|
||||
if (!nport->lport)
|
||||
nport->lport = lport;
|
||||
else
|
||||
fcloop_lport_put(lport);
|
||||
}
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
kfree(opts);
|
||||
return newnport;
|
||||
return nport;
|
||||
|
||||
out_invalid_opts:
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
out_put_nport:
|
||||
if (lport)
|
||||
fcloop_lport_put(lport);
|
||||
fcloop_nport_put(nport);
|
||||
out_free_newnport:
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
kfree(newnport);
|
||||
out_free_opts:
|
||||
kfree(opts);
|
||||
return nport;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -1352,6 +1461,7 @@ fcloop_create_remote_port(struct device *dev, struct device_attribute *attr,
|
||||
rport->nport = nport;
|
||||
rport->lport = nport->lport;
|
||||
nport->rport = rport;
|
||||
rport->flags = 0;
|
||||
spin_lock_init(&rport->lock);
|
||||
INIT_WORK(&rport->ls_work, fcloop_rport_lsrqst_work);
|
||||
INIT_LIST_HEAD(&rport->ls_list);
|
||||
@@ -1365,21 +1475,18 @@ __unlink_remote_port(struct fcloop_nport *nport)
|
||||
{
|
||||
struct fcloop_rport *rport = nport->rport;
|
||||
|
||||
lockdep_assert_held(&fcloop_lock);
|
||||
|
||||
if (rport && nport->tport)
|
||||
nport->tport->remoteport = NULL;
|
||||
nport->rport = NULL;
|
||||
|
||||
list_del(&nport->nport_list);
|
||||
|
||||
return rport;
|
||||
}
|
||||
|
||||
static int
|
||||
__remoteport_unreg(struct fcloop_nport *nport, struct fcloop_rport *rport)
|
||||
{
|
||||
if (!rport)
|
||||
return -EALREADY;
|
||||
|
||||
return nvme_fc_unregister_remoteport(rport->remoteport);
|
||||
}
|
||||
|
||||
@@ -1387,8 +1494,8 @@ static ssize_t
|
||||
fcloop_delete_remote_port(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct fcloop_nport *nport = NULL, *tmpport;
|
||||
static struct fcloop_rport *rport;
|
||||
struct fcloop_nport *nport;
|
||||
struct fcloop_rport *rport;
|
||||
u64 nodename, portname;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
@@ -1397,24 +1504,24 @@ fcloop_delete_remote_port(struct device *dev, struct device_attribute *attr,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&fcloop_lock, flags);
|
||||
|
||||
list_for_each_entry(tmpport, &fcloop_nports, nport_list) {
|
||||
if (tmpport->node_name == nodename &&
|
||||
tmpport->port_name == portname && tmpport->rport) {
|
||||
nport = tmpport;
|
||||
rport = __unlink_remote_port(nport);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
nport = fcloop_nport_lookup(nodename, portname);
|
||||
if (!nport)
|
||||
return -ENOENT;
|
||||
|
||||
spin_lock_irqsave(&fcloop_lock, flags);
|
||||
rport = __unlink_remote_port(nport);
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
if (!rport) {
|
||||
ret = -ENOENT;
|
||||
goto out_nport_put;
|
||||
}
|
||||
|
||||
ret = __remoteport_unreg(nport, rport);
|
||||
|
||||
out_nport_put:
|
||||
fcloop_nport_put(nport);
|
||||
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
@@ -1452,6 +1559,7 @@ fcloop_create_target_port(struct device *dev, struct device_attribute *attr,
|
||||
tport->nport = nport;
|
||||
tport->lport = nport->lport;
|
||||
nport->tport = tport;
|
||||
tport->flags = 0;
|
||||
spin_lock_init(&tport->lock);
|
||||
INIT_WORK(&tport->ls_work, fcloop_tport_lsrqst_work);
|
||||
INIT_LIST_HEAD(&tport->ls_list);
|
||||
@@ -1465,6 +1573,8 @@ __unlink_target_port(struct fcloop_nport *nport)
|
||||
{
|
||||
struct fcloop_tport *tport = nport->tport;
|
||||
|
||||
lockdep_assert_held(&fcloop_lock);
|
||||
|
||||
if (tport && nport->rport)
|
||||
nport->rport->targetport = NULL;
|
||||
nport->tport = NULL;
|
||||
@@ -1475,9 +1585,6 @@ __unlink_target_port(struct fcloop_nport *nport)
|
||||
static int
|
||||
__targetport_unreg(struct fcloop_nport *nport, struct fcloop_tport *tport)
|
||||
{
|
||||
if (!tport)
|
||||
return -EALREADY;
|
||||
|
||||
return nvmet_fc_unregister_targetport(tport->targetport);
|
||||
}
|
||||
|
||||
@@ -1485,8 +1592,8 @@ static ssize_t
|
||||
fcloop_delete_target_port(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct fcloop_nport *nport = NULL, *tmpport;
|
||||
struct fcloop_tport *tport = NULL;
|
||||
struct fcloop_nport *nport;
|
||||
struct fcloop_tport *tport;
|
||||
u64 nodename, portname;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
@@ -1495,24 +1602,24 @@ fcloop_delete_target_port(struct device *dev, struct device_attribute *attr,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&fcloop_lock, flags);
|
||||
|
||||
list_for_each_entry(tmpport, &fcloop_nports, nport_list) {
|
||||
if (tmpport->node_name == nodename &&
|
||||
tmpport->port_name == portname && tmpport->tport) {
|
||||
nport = tmpport;
|
||||
tport = __unlink_target_port(nport);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
nport = fcloop_nport_lookup(nodename, portname);
|
||||
if (!nport)
|
||||
return -ENOENT;
|
||||
|
||||
spin_lock_irqsave(&fcloop_lock, flags);
|
||||
tport = __unlink_target_port(nport);
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
if (!tport) {
|
||||
ret = -ENOENT;
|
||||
goto out_nport_put;
|
||||
}
|
||||
|
||||
ret = __targetport_unreg(nport, tport);
|
||||
|
||||
out_nport_put:
|
||||
fcloop_nport_put(nport);
|
||||
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
@@ -1578,15 +1685,20 @@ static const struct class fcloop_class = {
|
||||
};
|
||||
static struct device *fcloop_device;
|
||||
|
||||
|
||||
static int __init fcloop_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lsreq_cache = kmem_cache_create("lsreq_cache",
|
||||
sizeof(struct fcloop_lsreq), 0,
|
||||
0, NULL);
|
||||
if (!lsreq_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = class_register(&fcloop_class);
|
||||
if (ret) {
|
||||
pr_err("couldn't register class fcloop\n");
|
||||
return ret;
|
||||
goto out_destroy_cache;
|
||||
}
|
||||
|
||||
fcloop_device = device_create_with_groups(
|
||||
@@ -1604,13 +1716,15 @@ static int __init fcloop_init(void)
|
||||
|
||||
out_destroy_class:
|
||||
class_unregister(&fcloop_class);
|
||||
out_destroy_cache:
|
||||
kmem_cache_destroy(lsreq_cache);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit fcloop_exit(void)
|
||||
{
|
||||
struct fcloop_lport *lport = NULL;
|
||||
struct fcloop_nport *nport = NULL;
|
||||
struct fcloop_lport *lport;
|
||||
struct fcloop_nport *nport;
|
||||
struct fcloop_tport *tport;
|
||||
struct fcloop_rport *rport;
|
||||
unsigned long flags;
|
||||
@@ -1621,7 +1735,7 @@ static void __exit fcloop_exit(void)
|
||||
for (;;) {
|
||||
nport = list_first_entry_or_null(&fcloop_nports,
|
||||
typeof(*nport), nport_list);
|
||||
if (!nport)
|
||||
if (!nport || !fcloop_nport_get(nport))
|
||||
break;
|
||||
|
||||
tport = __unlink_target_port(nport);
|
||||
@@ -1629,13 +1743,21 @@ static void __exit fcloop_exit(void)
|
||||
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
ret = __targetport_unreg(nport, tport);
|
||||
if (ret)
|
||||
pr_warn("%s: Failed deleting target port\n", __func__);
|
||||
if (tport) {
|
||||
ret = __targetport_unreg(nport, tport);
|
||||
if (ret)
|
||||
pr_warn("%s: Failed deleting target port\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
ret = __remoteport_unreg(nport, rport);
|
||||
if (ret)
|
||||
pr_warn("%s: Failed deleting remote port\n", __func__);
|
||||
if (rport) {
|
||||
ret = __remoteport_unreg(nport, rport);
|
||||
if (ret)
|
||||
pr_warn("%s: Failed deleting remote port\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
fcloop_nport_put(nport);
|
||||
|
||||
spin_lock_irqsave(&fcloop_lock, flags);
|
||||
}
|
||||
@@ -1648,7 +1770,7 @@ static void __exit fcloop_exit(void)
|
||||
|
||||
spin_unlock_irqrestore(&fcloop_lock, flags);
|
||||
|
||||
ret = __wait_localport_unreg(lport);
|
||||
ret = __localport_unreg(lport);
|
||||
if (ret)
|
||||
pr_warn("%s: Failed deleting local port\n", __func__);
|
||||
|
||||
@@ -1663,6 +1785,7 @@ static void __exit fcloop_exit(void)
|
||||
|
||||
device_destroy(&fcloop_class, MKDEV(0, 0));
|
||||
class_unregister(&fcloop_class);
|
||||
kmem_cache_destroy(lsreq_cache);
|
||||
}
|
||||
|
||||
module_init(fcloop_init);
|
||||
|
||||
@@ -33,10 +33,12 @@ struct nvme_loop_ctrl {
|
||||
|
||||
struct list_head list;
|
||||
struct blk_mq_tag_set tag_set;
|
||||
struct nvme_loop_iod async_event_iod;
|
||||
struct nvme_ctrl ctrl;
|
||||
|
||||
struct nvmet_port *port;
|
||||
|
||||
/* Must be last --ends in a flexible-array member. */
|
||||
struct nvme_loop_iod async_event_iod;
|
||||
};
|
||||
|
||||
static inline struct nvme_loop_ctrl *to_loop_ctrl(struct nvme_ctrl *ctrl)
|
||||
@@ -148,8 +150,7 @@ static blk_status_t nvme_loop_queue_rq(struct blk_mq_hw_ctx *hctx,
|
||||
nvme_start_request(req);
|
||||
iod->cmd.common.flags |= NVME_CMD_SGL_METABUF;
|
||||
iod->req.port = queue->ctrl->port;
|
||||
if (!nvmet_req_init(&iod->req, &queue->nvme_cq,
|
||||
&queue->nvme_sq, &nvme_loop_ops))
|
||||
if (!nvmet_req_init(&iod->req, &queue->nvme_sq, &nvme_loop_ops))
|
||||
return BLK_STS_OK;
|
||||
|
||||
if (blk_rq_nr_phys_segments(req)) {
|
||||
@@ -181,8 +182,7 @@ static void nvme_loop_submit_async_event(struct nvme_ctrl *arg)
|
||||
iod->cmd.common.command_id = NVME_AQ_BLK_MQ_DEPTH;
|
||||
iod->cmd.common.flags |= NVME_CMD_SGL_METABUF;
|
||||
|
||||
if (!nvmet_req_init(&iod->req, &queue->nvme_cq, &queue->nvme_sq,
|
||||
&nvme_loop_ops)) {
|
||||
if (!nvmet_req_init(&iod->req, &queue->nvme_sq, &nvme_loop_ops)) {
|
||||
dev_err(ctrl->ctrl.device, "failed async event work\n");
|
||||
return;
|
||||
}
|
||||
@@ -273,6 +273,7 @@ static void nvme_loop_destroy_admin_queue(struct nvme_loop_ctrl *ctrl)
|
||||
nvme_unquiesce_admin_queue(&ctrl->ctrl);
|
||||
|
||||
nvmet_sq_destroy(&ctrl->queues[0].nvme_sq);
|
||||
nvmet_cq_put(&ctrl->queues[0].nvme_cq);
|
||||
nvme_remove_admin_tag_set(&ctrl->ctrl);
|
||||
}
|
||||
|
||||
@@ -302,6 +303,7 @@ static void nvme_loop_destroy_io_queues(struct nvme_loop_ctrl *ctrl)
|
||||
for (i = 1; i < ctrl->ctrl.queue_count; i++) {
|
||||
clear_bit(NVME_LOOP_Q_LIVE, &ctrl->queues[i].flags);
|
||||
nvmet_sq_destroy(&ctrl->queues[i].nvme_sq);
|
||||
nvmet_cq_put(&ctrl->queues[i].nvme_cq);
|
||||
}
|
||||
ctrl->ctrl.queue_count = 1;
|
||||
/*
|
||||
@@ -327,9 +329,13 @@ static int nvme_loop_init_io_queues(struct nvme_loop_ctrl *ctrl)
|
||||
|
||||
for (i = 1; i <= nr_io_queues; i++) {
|
||||
ctrl->queues[i].ctrl = ctrl;
|
||||
ret = nvmet_sq_init(&ctrl->queues[i].nvme_sq);
|
||||
if (ret)
|
||||
nvmet_cq_init(&ctrl->queues[i].nvme_cq);
|
||||
ret = nvmet_sq_init(&ctrl->queues[i].nvme_sq,
|
||||
&ctrl->queues[i].nvme_cq);
|
||||
if (ret) {
|
||||
nvmet_cq_put(&ctrl->queues[i].nvme_cq);
|
||||
goto out_destroy_queues;
|
||||
}
|
||||
|
||||
ctrl->ctrl.queue_count++;
|
||||
}
|
||||
@@ -360,9 +366,13 @@ static int nvme_loop_configure_admin_queue(struct nvme_loop_ctrl *ctrl)
|
||||
int error;
|
||||
|
||||
ctrl->queues[0].ctrl = ctrl;
|
||||
error = nvmet_sq_init(&ctrl->queues[0].nvme_sq);
|
||||
if (error)
|
||||
nvmet_cq_init(&ctrl->queues[0].nvme_cq);
|
||||
error = nvmet_sq_init(&ctrl->queues[0].nvme_sq,
|
||||
&ctrl->queues[0].nvme_cq);
|
||||
if (error) {
|
||||
nvmet_cq_put(&ctrl->queues[0].nvme_cq);
|
||||
return error;
|
||||
}
|
||||
ctrl->ctrl.queue_count = 1;
|
||||
|
||||
error = nvme_alloc_admin_tag_set(&ctrl->ctrl, &ctrl->admin_tag_set,
|
||||
@@ -401,6 +411,7 @@ static int nvme_loop_configure_admin_queue(struct nvme_loop_ctrl *ctrl)
|
||||
nvme_remove_admin_tag_set(&ctrl->ctrl);
|
||||
out_free_sq:
|
||||
nvmet_sq_destroy(&ctrl->queues[0].nvme_sq);
|
||||
nvmet_cq_put(&ctrl->queues[0].nvme_cq);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@@ -141,13 +141,16 @@ static inline struct device *nvmet_ns_dev(struct nvmet_ns *ns)
|
||||
}
|
||||
|
||||
struct nvmet_cq {
|
||||
struct nvmet_ctrl *ctrl;
|
||||
u16 qid;
|
||||
u16 size;
|
||||
refcount_t ref;
|
||||
};
|
||||
|
||||
struct nvmet_sq {
|
||||
struct nvmet_ctrl *ctrl;
|
||||
struct percpu_ref ref;
|
||||
struct nvmet_cq *cq;
|
||||
u16 qid;
|
||||
u16 size;
|
||||
u32 sqhd;
|
||||
@@ -247,6 +250,7 @@ struct nvmet_pr_log_mgr {
|
||||
struct nvmet_ctrl {
|
||||
struct nvmet_subsys *subsys;
|
||||
struct nvmet_sq **sqs;
|
||||
struct nvmet_cq **cqs;
|
||||
|
||||
void *drvdata;
|
||||
|
||||
@@ -424,7 +428,7 @@ struct nvmet_fabrics_ops {
|
||||
u16 (*get_max_queue_size)(const struct nvmet_ctrl *ctrl);
|
||||
|
||||
/* Operations mandatory for PCI target controllers */
|
||||
u16 (*create_sq)(struct nvmet_ctrl *ctrl, u16 sqid, u16 flags,
|
||||
u16 (*create_sq)(struct nvmet_ctrl *ctrl, u16 sqid, u16 cqid, u16 flags,
|
||||
u16 qsize, u64 prp1);
|
||||
u16 (*delete_sq)(struct nvmet_ctrl *ctrl, u16 sqid);
|
||||
u16 (*create_cq)(struct nvmet_ctrl *ctrl, u16 cqid, u16 flags,
|
||||
@@ -557,8 +561,8 @@ u32 nvmet_fabrics_admin_cmd_data_len(struct nvmet_req *req);
|
||||
u16 nvmet_parse_fabrics_io_cmd(struct nvmet_req *req);
|
||||
u32 nvmet_fabrics_io_cmd_data_len(struct nvmet_req *req);
|
||||
|
||||
bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq,
|
||||
struct nvmet_sq *sq, const struct nvmet_fabrics_ops *ops);
|
||||
bool nvmet_req_init(struct nvmet_req *req, struct nvmet_sq *sq,
|
||||
const struct nvmet_fabrics_ops *ops);
|
||||
void nvmet_req_uninit(struct nvmet_req *req);
|
||||
size_t nvmet_req_transfer_len(struct nvmet_req *req);
|
||||
bool nvmet_check_transfer_len(struct nvmet_req *req, size_t len);
|
||||
@@ -571,18 +575,24 @@ void nvmet_execute_set_features(struct nvmet_req *req);
|
||||
void nvmet_execute_get_features(struct nvmet_req *req);
|
||||
void nvmet_execute_keep_alive(struct nvmet_req *req);
|
||||
|
||||
u16 nvmet_check_cqid(struct nvmet_ctrl *ctrl, u16 cqid);
|
||||
u16 nvmet_check_cqid(struct nvmet_ctrl *ctrl, u16 cqid, bool create);
|
||||
u16 nvmet_check_io_cqid(struct nvmet_ctrl *ctrl, u16 cqid, bool create);
|
||||
void nvmet_cq_init(struct nvmet_cq *cq);
|
||||
void nvmet_cq_setup(struct nvmet_ctrl *ctrl, struct nvmet_cq *cq, u16 qid,
|
||||
u16 size);
|
||||
u16 nvmet_cq_create(struct nvmet_ctrl *ctrl, struct nvmet_cq *cq, u16 qid,
|
||||
u16 size);
|
||||
void nvmet_cq_destroy(struct nvmet_cq *cq);
|
||||
bool nvmet_cq_get(struct nvmet_cq *cq);
|
||||
void nvmet_cq_put(struct nvmet_cq *cq);
|
||||
bool nvmet_cq_in_use(struct nvmet_cq *cq);
|
||||
u16 nvmet_check_sqid(struct nvmet_ctrl *ctrl, u16 sqid, bool create);
|
||||
void nvmet_sq_setup(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, u16 qid,
|
||||
u16 size);
|
||||
u16 nvmet_sq_create(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, u16 qid,
|
||||
u16 size);
|
||||
u16 nvmet_sq_create(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq,
|
||||
struct nvmet_cq *cq, u16 qid, u16 size);
|
||||
void nvmet_sq_destroy(struct nvmet_sq *sq);
|
||||
int nvmet_sq_init(struct nvmet_sq *sq);
|
||||
int nvmet_sq_init(struct nvmet_sq *sq, struct nvmet_cq *cq);
|
||||
|
||||
void nvmet_ctrl_fatal_error(struct nvmet_ctrl *ctrl);
|
||||
|
||||
|
||||
@@ -1346,15 +1346,17 @@ static u16 nvmet_pci_epf_delete_cq(struct nvmet_ctrl *tctrl, u16 cqid)
|
||||
nvmet_pci_epf_drain_queue(cq);
|
||||
nvmet_pci_epf_remove_irq_vector(ctrl, cq->vector);
|
||||
nvmet_pci_epf_mem_unmap(ctrl->nvme_epf, &cq->pci_map);
|
||||
nvmet_cq_put(&cq->nvme_cq);
|
||||
|
||||
return NVME_SC_SUCCESS;
|
||||
}
|
||||
|
||||
static u16 nvmet_pci_epf_create_sq(struct nvmet_ctrl *tctrl,
|
||||
u16 sqid, u16 flags, u16 qsize, u64 pci_addr)
|
||||
u16 sqid, u16 cqid, u16 flags, u16 qsize, u64 pci_addr)
|
||||
{
|
||||
struct nvmet_pci_epf_ctrl *ctrl = tctrl->drvdata;
|
||||
struct nvmet_pci_epf_queue *sq = &ctrl->sq[sqid];
|
||||
struct nvmet_pci_epf_queue *cq = &ctrl->cq[cqid];
|
||||
u16 status;
|
||||
|
||||
if (test_bit(NVMET_PCI_EPF_Q_LIVE, &sq->flags))
|
||||
@@ -1377,7 +1379,8 @@ static u16 nvmet_pci_epf_create_sq(struct nvmet_ctrl *tctrl,
|
||||
sq->qes = ctrl->io_sqes;
|
||||
sq->pci_size = sq->qes * sq->depth;
|
||||
|
||||
status = nvmet_sq_create(tctrl, &sq->nvme_sq, sqid, sq->depth);
|
||||
status = nvmet_sq_create(tctrl, &sq->nvme_sq, &cq->nvme_cq, sqid,
|
||||
sq->depth);
|
||||
if (status != NVME_SC_SUCCESS)
|
||||
return status;
|
||||
|
||||
@@ -1594,8 +1597,7 @@ static void nvmet_pci_epf_exec_iod_work(struct work_struct *work)
|
||||
goto complete;
|
||||
}
|
||||
|
||||
if (!nvmet_req_init(req, &iod->cq->nvme_cq, &iod->sq->nvme_sq,
|
||||
&nvmet_pci_epf_fabrics_ops))
|
||||
if (!nvmet_req_init(req, &iod->sq->nvme_sq, &nvmet_pci_epf_fabrics_ops))
|
||||
goto complete;
|
||||
|
||||
iod->data_len = nvmet_req_transfer_len(req);
|
||||
@@ -1872,8 +1874,8 @@ static int nvmet_pci_epf_enable_ctrl(struct nvmet_pci_epf_ctrl *ctrl)
|
||||
|
||||
qsize = aqa & 0x00000fff;
|
||||
pci_addr = asq & GENMASK_ULL(63, 12);
|
||||
status = nvmet_pci_epf_create_sq(ctrl->tctrl, 0, NVME_QUEUE_PHYS_CONTIG,
|
||||
qsize, pci_addr);
|
||||
status = nvmet_pci_epf_create_sq(ctrl->tctrl, 0, 0,
|
||||
NVME_QUEUE_PHYS_CONTIG, qsize, pci_addr);
|
||||
if (status != NVME_SC_SUCCESS) {
|
||||
dev_err(ctrl->dev, "Failed to create admin submission queue\n");
|
||||
nvmet_pci_epf_delete_cq(ctrl->tctrl, 0);
|
||||
|
||||
@@ -976,8 +976,7 @@ static void nvmet_rdma_handle_command(struct nvmet_rdma_queue *queue,
|
||||
cmd->send_sge.addr, cmd->send_sge.length,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
if (!nvmet_req_init(&cmd->req, &queue->nvme_cq,
|
||||
&queue->nvme_sq, &nvmet_rdma_ops))
|
||||
if (!nvmet_req_init(&cmd->req, &queue->nvme_sq, &nvmet_rdma_ops))
|
||||
return;
|
||||
|
||||
status = nvmet_rdma_map_sgl(cmd);
|
||||
@@ -1353,6 +1352,7 @@ static void nvmet_rdma_free_queue(struct nvmet_rdma_queue *queue)
|
||||
pr_debug("freeing queue %d\n", queue->idx);
|
||||
|
||||
nvmet_sq_destroy(&queue->nvme_sq);
|
||||
nvmet_cq_put(&queue->nvme_cq);
|
||||
|
||||
nvmet_rdma_destroy_queue_ib(queue);
|
||||
if (!queue->nsrq) {
|
||||
@@ -1436,7 +1436,8 @@ nvmet_rdma_alloc_queue(struct nvmet_rdma_device *ndev,
|
||||
goto out_reject;
|
||||
}
|
||||
|
||||
ret = nvmet_sq_init(&queue->nvme_sq);
|
||||
nvmet_cq_init(&queue->nvme_cq);
|
||||
ret = nvmet_sq_init(&queue->nvme_sq, &queue->nvme_cq);
|
||||
if (ret) {
|
||||
ret = NVME_RDMA_CM_NO_RSC;
|
||||
goto out_free_queue;
|
||||
@@ -1517,6 +1518,7 @@ nvmet_rdma_alloc_queue(struct nvmet_rdma_device *ndev,
|
||||
out_destroy_sq:
|
||||
nvmet_sq_destroy(&queue->nvme_sq);
|
||||
out_free_queue:
|
||||
nvmet_cq_put(&queue->nvme_cq);
|
||||
kfree(queue);
|
||||
out_reject:
|
||||
nvmet_rdma_cm_reject(cm_id, ret);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/crc32c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/nvme-tcp.h>
|
||||
#include <linux/nvme-keyring.h>
|
||||
@@ -17,7 +18,6 @@
|
||||
#include <net/handshake.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/llist.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <trace/events/sock.h>
|
||||
|
||||
#include "nvmet.h"
|
||||
@@ -172,8 +172,6 @@ struct nvmet_tcp_queue {
|
||||
/* digest state */
|
||||
bool hdr_digest;
|
||||
bool data_digest;
|
||||
struct ahash_request *snd_hash;
|
||||
struct ahash_request *rcv_hash;
|
||||
|
||||
/* TLS state */
|
||||
key_serial_t tls_pskid;
|
||||
@@ -294,14 +292,9 @@ static inline u8 nvmet_tcp_ddgst_len(struct nvmet_tcp_queue *queue)
|
||||
return queue->data_digest ? NVME_TCP_DIGEST_LENGTH : 0;
|
||||
}
|
||||
|
||||
static inline void nvmet_tcp_hdgst(struct ahash_request *hash,
|
||||
void *pdu, size_t len)
|
||||
static inline void nvmet_tcp_hdgst(void *pdu, size_t len)
|
||||
{
|
||||
struct scatterlist sg;
|
||||
|
||||
sg_init_one(&sg, pdu, len);
|
||||
ahash_request_set_crypt(hash, &sg, pdu + len, len);
|
||||
crypto_ahash_digest(hash);
|
||||
put_unaligned_le32(~crc32c(~0, pdu, len), pdu + len);
|
||||
}
|
||||
|
||||
static int nvmet_tcp_verify_hdgst(struct nvmet_tcp_queue *queue,
|
||||
@@ -318,7 +311,7 @@ static int nvmet_tcp_verify_hdgst(struct nvmet_tcp_queue *queue,
|
||||
}
|
||||
|
||||
recv_digest = *(__le32 *)(pdu + hdr->hlen);
|
||||
nvmet_tcp_hdgst(queue->rcv_hash, pdu, len);
|
||||
nvmet_tcp_hdgst(pdu, len);
|
||||
exp_digest = *(__le32 *)(pdu + hdr->hlen);
|
||||
if (recv_digest != exp_digest) {
|
||||
pr_err("queue %d: header digest error: recv %#x expected %#x\n",
|
||||
@@ -441,12 +434,24 @@ static int nvmet_tcp_map_data(struct nvmet_tcp_cmd *cmd)
|
||||
return NVME_SC_INTERNAL;
|
||||
}
|
||||
|
||||
static void nvmet_tcp_calc_ddgst(struct ahash_request *hash,
|
||||
struct nvmet_tcp_cmd *cmd)
|
||||
static void nvmet_tcp_calc_ddgst(struct nvmet_tcp_cmd *cmd)
|
||||
{
|
||||
ahash_request_set_crypt(hash, cmd->req.sg,
|
||||
(void *)&cmd->exp_ddgst, cmd->req.transfer_len);
|
||||
crypto_ahash_digest(hash);
|
||||
size_t total_len = cmd->req.transfer_len;
|
||||
struct scatterlist *sg = cmd->req.sg;
|
||||
u32 crc = ~0;
|
||||
|
||||
while (total_len) {
|
||||
size_t len = min_t(size_t, total_len, sg->length);
|
||||
|
||||
/*
|
||||
* Note that the scatterlist does not contain any highmem pages,
|
||||
* as it was allocated by sgl_alloc() with GFP_KERNEL.
|
||||
*/
|
||||
crc = crc32c(crc, sg_virt(sg), len);
|
||||
total_len -= len;
|
||||
sg = sg_next(sg);
|
||||
}
|
||||
cmd->exp_ddgst = cpu_to_le32(~crc);
|
||||
}
|
||||
|
||||
static void nvmet_setup_c2h_data_pdu(struct nvmet_tcp_cmd *cmd)
|
||||
@@ -473,19 +478,18 @@ static void nvmet_setup_c2h_data_pdu(struct nvmet_tcp_cmd *cmd)
|
||||
|
||||
if (queue->data_digest) {
|
||||
pdu->hdr.flags |= NVME_TCP_F_DDGST;
|
||||
nvmet_tcp_calc_ddgst(queue->snd_hash, cmd);
|
||||
nvmet_tcp_calc_ddgst(cmd);
|
||||
}
|
||||
|
||||
if (cmd->queue->hdr_digest) {
|
||||
pdu->hdr.flags |= NVME_TCP_F_HDGST;
|
||||
nvmet_tcp_hdgst(queue->snd_hash, pdu, sizeof(*pdu));
|
||||
nvmet_tcp_hdgst(pdu, sizeof(*pdu));
|
||||
}
|
||||
}
|
||||
|
||||
static void nvmet_setup_r2t_pdu(struct nvmet_tcp_cmd *cmd)
|
||||
{
|
||||
struct nvme_tcp_r2t_pdu *pdu = cmd->r2t_pdu;
|
||||
struct nvmet_tcp_queue *queue = cmd->queue;
|
||||
u8 hdgst = nvmet_tcp_hdgst_len(cmd->queue);
|
||||
|
||||
cmd->offset = 0;
|
||||
@@ -503,14 +507,13 @@ static void nvmet_setup_r2t_pdu(struct nvmet_tcp_cmd *cmd)
|
||||
pdu->r2t_offset = cpu_to_le32(cmd->rbytes_done);
|
||||
if (cmd->queue->hdr_digest) {
|
||||
pdu->hdr.flags |= NVME_TCP_F_HDGST;
|
||||
nvmet_tcp_hdgst(queue->snd_hash, pdu, sizeof(*pdu));
|
||||
nvmet_tcp_hdgst(pdu, sizeof(*pdu));
|
||||
}
|
||||
}
|
||||
|
||||
static void nvmet_setup_response_pdu(struct nvmet_tcp_cmd *cmd)
|
||||
{
|
||||
struct nvme_tcp_rsp_pdu *pdu = cmd->rsp_pdu;
|
||||
struct nvmet_tcp_queue *queue = cmd->queue;
|
||||
u8 hdgst = nvmet_tcp_hdgst_len(cmd->queue);
|
||||
|
||||
cmd->offset = 0;
|
||||
@@ -523,7 +526,7 @@ static void nvmet_setup_response_pdu(struct nvmet_tcp_cmd *cmd)
|
||||
pdu->hdr.plen = cpu_to_le32(pdu->hdr.hlen + hdgst);
|
||||
if (cmd->queue->hdr_digest) {
|
||||
pdu->hdr.flags |= NVME_TCP_F_HDGST;
|
||||
nvmet_tcp_hdgst(queue->snd_hash, pdu, sizeof(*pdu));
|
||||
nvmet_tcp_hdgst(pdu, sizeof(*pdu));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -857,42 +860,6 @@ static void nvmet_prepare_receive_pdu(struct nvmet_tcp_queue *queue)
|
||||
smp_store_release(&queue->rcv_state, NVMET_TCP_RECV_PDU);
|
||||
}
|
||||
|
||||
static void nvmet_tcp_free_crypto(struct nvmet_tcp_queue *queue)
|
||||
{
|
||||
struct crypto_ahash *tfm = crypto_ahash_reqtfm(queue->rcv_hash);
|
||||
|
||||
ahash_request_free(queue->rcv_hash);
|
||||
ahash_request_free(queue->snd_hash);
|
||||
crypto_free_ahash(tfm);
|
||||
}
|
||||
|
||||
static int nvmet_tcp_alloc_crypto(struct nvmet_tcp_queue *queue)
|
||||
{
|
||||
struct crypto_ahash *tfm;
|
||||
|
||||
tfm = crypto_alloc_ahash("crc32c", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(tfm))
|
||||
return PTR_ERR(tfm);
|
||||
|
||||
queue->snd_hash = ahash_request_alloc(tfm, GFP_KERNEL);
|
||||
if (!queue->snd_hash)
|
||||
goto free_tfm;
|
||||
ahash_request_set_callback(queue->snd_hash, 0, NULL, NULL);
|
||||
|
||||
queue->rcv_hash = ahash_request_alloc(tfm, GFP_KERNEL);
|
||||
if (!queue->rcv_hash)
|
||||
goto free_snd_hash;
|
||||
ahash_request_set_callback(queue->rcv_hash, 0, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
free_snd_hash:
|
||||
ahash_request_free(queue->snd_hash);
|
||||
free_tfm:
|
||||
crypto_free_ahash(tfm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
||||
static int nvmet_tcp_handle_icreq(struct nvmet_tcp_queue *queue)
|
||||
{
|
||||
struct nvme_tcp_icreq_pdu *icreq = &queue->pdu.icreq;
|
||||
@@ -921,11 +888,6 @@ static int nvmet_tcp_handle_icreq(struct nvmet_tcp_queue *queue)
|
||||
|
||||
queue->hdr_digest = !!(icreq->digest & NVME_TCP_HDR_DIGEST_ENABLE);
|
||||
queue->data_digest = !!(icreq->digest & NVME_TCP_DATA_DIGEST_ENABLE);
|
||||
if (queue->hdr_digest || queue->data_digest) {
|
||||
ret = nvmet_tcp_alloc_crypto(queue);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(icresp, 0, sizeof(*icresp));
|
||||
icresp->hdr.type = nvme_tcp_icresp;
|
||||
@@ -1077,8 +1039,7 @@ static int nvmet_tcp_done_recv_pdu(struct nvmet_tcp_queue *queue)
|
||||
req = &queue->cmd->req;
|
||||
memcpy(req->cmd, nvme_cmd, sizeof(*nvme_cmd));
|
||||
|
||||
if (unlikely(!nvmet_req_init(req, &queue->nvme_cq,
|
||||
&queue->nvme_sq, &nvmet_tcp_ops))) {
|
||||
if (unlikely(!nvmet_req_init(req, &queue->nvme_sq, &nvmet_tcp_ops))) {
|
||||
pr_err("failed cmd %p id %d opcode %d, data_len: %d, status: %04x\n",
|
||||
req->cmd, req->cmd->common.command_id,
|
||||
req->cmd->common.opcode,
|
||||
@@ -1247,7 +1208,7 @@ static void nvmet_tcp_prep_recv_ddgst(struct nvmet_tcp_cmd *cmd)
|
||||
{
|
||||
struct nvmet_tcp_queue *queue = cmd->queue;
|
||||
|
||||
nvmet_tcp_calc_ddgst(queue->rcv_hash, cmd);
|
||||
nvmet_tcp_calc_ddgst(cmd);
|
||||
queue->offset = 0;
|
||||
queue->left = NVME_TCP_DIGEST_LENGTH;
|
||||
queue->rcv_state = NVMET_TCP_RECV_DDGST;
|
||||
@@ -1615,13 +1576,12 @@ static void nvmet_tcp_release_queue_work(struct work_struct *w)
|
||||
nvmet_sq_put_tls_key(&queue->nvme_sq);
|
||||
nvmet_tcp_uninit_data_in_cmds(queue);
|
||||
nvmet_sq_destroy(&queue->nvme_sq);
|
||||
nvmet_cq_put(&queue->nvme_cq);
|
||||
cancel_work_sync(&queue->io_work);
|
||||
nvmet_tcp_free_cmd_data_in_buffers(queue);
|
||||
/* ->sock will be released by fput() */
|
||||
fput(queue->sock->file);
|
||||
nvmet_tcp_free_cmds(queue);
|
||||
if (queue->hdr_digest || queue->data_digest)
|
||||
nvmet_tcp_free_crypto(queue);
|
||||
ida_free(&nvmet_tcp_queue_ida, queue->idx);
|
||||
page_frag_cache_drain(&queue->pf_cache);
|
||||
kfree(queue);
|
||||
@@ -1950,7 +1910,8 @@ static void nvmet_tcp_alloc_queue(struct nvmet_tcp_port *port,
|
||||
if (ret)
|
||||
goto out_ida_remove;
|
||||
|
||||
ret = nvmet_sq_init(&queue->nvme_sq);
|
||||
nvmet_cq_init(&queue->nvme_cq);
|
||||
ret = nvmet_sq_init(&queue->nvme_sq, &queue->nvme_cq);
|
||||
if (ret)
|
||||
goto out_free_connect;
|
||||
|
||||
@@ -1993,6 +1954,7 @@ static void nvmet_tcp_alloc_queue(struct nvmet_tcp_port *port,
|
||||
mutex_unlock(&nvmet_tcp_queue_mutex);
|
||||
nvmet_sq_destroy(&queue->nvme_sq);
|
||||
out_free_connect:
|
||||
nvmet_cq_put(&queue->nvme_cq);
|
||||
nvmet_tcp_free_cmd(&queue->connect);
|
||||
out_ida_remove:
|
||||
ida_free(&nvmet_tcp_queue_ida, queue->idx);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#ifndef LINUX_DMAPOOL_H
|
||||
#define LINUX_DMAPOOL_H
|
||||
|
||||
#include <linux/nodemask_types.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
@@ -18,8 +19,8 @@ struct device;
|
||||
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
|
||||
struct dma_pool *dma_pool_create(const char *name, struct device *dev,
|
||||
size_t size, size_t align, size_t allocation);
|
||||
struct dma_pool *dma_pool_create_node(const char *name, struct device *dev,
|
||||
size_t size, size_t align, size_t boundary, int node);
|
||||
|
||||
void dma_pool_destroy(struct dma_pool *pool);
|
||||
|
||||
@@ -35,9 +36,12 @@ struct dma_pool *dmam_pool_create(const char *name, struct device *dev,
|
||||
void dmam_pool_destroy(struct dma_pool *pool);
|
||||
|
||||
#else /* !CONFIG_HAS_DMA */
|
||||
static inline struct dma_pool *dma_pool_create(const char *name,
|
||||
struct device *dev, size_t size, size_t align, size_t allocation)
|
||||
{ return NULL; }
|
||||
static inline struct dma_pool *dma_pool_create_node(const char *name,
|
||||
struct device *dev, size_t size, size_t align, size_t boundary,
|
||||
int node)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void dma_pool_destroy(struct dma_pool *pool) { }
|
||||
static inline void *dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags,
|
||||
dma_addr_t *handle) { return NULL; }
|
||||
@@ -49,6 +53,13 @@ static inline struct dma_pool *dmam_pool_create(const char *name,
|
||||
static inline void dmam_pool_destroy(struct dma_pool *pool) { }
|
||||
#endif /* !CONFIG_HAS_DMA */
|
||||
|
||||
static inline struct dma_pool *dma_pool_create(const char *name,
|
||||
struct device *dev, size_t size, size_t align, size_t boundary)
|
||||
{
|
||||
return dma_pool_create_node(name, dev, size, align, boundary,
|
||||
NUMA_NO_NODE);
|
||||
}
|
||||
|
||||
static inline void *dma_pool_zalloc(struct dma_pool *pool, gfp_t mem_flags,
|
||||
dma_addr_t *handle)
|
||||
{
|
||||
|
||||
15
mm/dmapool.c
15
mm/dmapool.c
@@ -56,6 +56,7 @@ struct dma_pool { /* the pool */
|
||||
unsigned int size;
|
||||
unsigned int allocation;
|
||||
unsigned int boundary;
|
||||
int node;
|
||||
char name[32];
|
||||
struct list_head pools;
|
||||
};
|
||||
@@ -199,12 +200,13 @@ static void pool_block_push(struct dma_pool *pool, struct dma_block *block,
|
||||
|
||||
|
||||
/**
|
||||
* dma_pool_create - Creates a pool of consistent memory blocks, for dma.
|
||||
* dma_pool_create_node - Creates a pool of consistent memory blocks, for dma.
|
||||
* @name: name of pool, for diagnostics
|
||||
* @dev: device that will be doing the DMA
|
||||
* @size: size of the blocks in this pool.
|
||||
* @align: alignment requirement for blocks; must be a power of two
|
||||
* @boundary: returned blocks won't cross this power of two boundary
|
||||
* @node: optional NUMA node to allocate structs 'dma_pool' and 'dma_page' on
|
||||
* Context: not in_interrupt()
|
||||
*
|
||||
* Given one of these pools, dma_pool_alloc()
|
||||
@@ -221,8 +223,8 @@ static void pool_block_push(struct dma_pool *pool, struct dma_block *block,
|
||||
* Return: a dma allocation pool with the requested characteristics, or
|
||||
* %NULL if one can't be created.
|
||||
*/
|
||||
struct dma_pool *dma_pool_create(const char *name, struct device *dev,
|
||||
size_t size, size_t align, size_t boundary)
|
||||
struct dma_pool *dma_pool_create_node(const char *name, struct device *dev,
|
||||
size_t size, size_t align, size_t boundary, int node)
|
||||
{
|
||||
struct dma_pool *retval;
|
||||
size_t allocation;
|
||||
@@ -251,7 +253,7 @@ struct dma_pool *dma_pool_create(const char *name, struct device *dev,
|
||||
|
||||
boundary = min(boundary, allocation);
|
||||
|
||||
retval = kzalloc(sizeof(*retval), GFP_KERNEL);
|
||||
retval = kzalloc_node(sizeof(*retval), GFP_KERNEL, node);
|
||||
if (!retval)
|
||||
return retval;
|
||||
|
||||
@@ -264,6 +266,7 @@ struct dma_pool *dma_pool_create(const char *name, struct device *dev,
|
||||
retval->size = size;
|
||||
retval->boundary = boundary;
|
||||
retval->allocation = allocation;
|
||||
retval->node = node;
|
||||
INIT_LIST_HEAD(&retval->pools);
|
||||
|
||||
/*
|
||||
@@ -295,7 +298,7 @@ struct dma_pool *dma_pool_create(const char *name, struct device *dev,
|
||||
mutex_unlock(&pools_reg_lock);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_pool_create);
|
||||
EXPORT_SYMBOL(dma_pool_create_node);
|
||||
|
||||
static void pool_initialise_page(struct dma_pool *pool, struct dma_page *page)
|
||||
{
|
||||
@@ -335,7 +338,7 @@ static struct dma_page *pool_alloc_page(struct dma_pool *pool, gfp_t mem_flags)
|
||||
{
|
||||
struct dma_page *page;
|
||||
|
||||
page = kmalloc(sizeof(*page), mem_flags);
|
||||
page = kmalloc_node(sizeof(*page), mem_flags, pool->node);
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user