[PATCH 5/5] nvme-fabrics: Add FC LLDD loopback driver to test FC host and target transport
J Freyensee
james_p_freyensee at linux.intel.com
Tue Aug 2 17:15:44 PDT 2016
On Fri, 2016-07-22 at 17:23 -0700, James Smart wrote:
A couple comments.
> Add FC LLDD loopback driver to test FC host and target transport
> within
> nvme-fabrics
>
> To aid in the development and testing of the lower-level api of the
> FC
> transport, this loopback driver has been created to act as if it were
> a
> FC hba driver supporting both the host interfaces as well as the
> target
> interfaces with the nvme FC transport.
>
>
> Signed-off-by: James Smart <james.smart at broadcom.com>
>
> ---
snip...
> +int
> +fcloop_fcp_op(struct nvmet_fc_target_port *tgtport,
> + struct nvmefc_tgt_fcp_req *tgt_fcpreq)
> +{
> + struct fcloop_fcpreq *tfcp_req =
> + container_of(tgt_fcpreq, struct fcloop_fcpreq,
> tgt_fcp_req);
> + struct nvmefc_fcp_req *fcpreq = tfcp_req->fcpreq;
> + u32 rsplen = 0, xfrlen = 0;
> + int fcp_err = 0;
> + u8 op = tgt_fcpreq->op;
> +
> + switch (op) {
> + case NVMET_FCOP_WRITEDATA:
> + xfrlen = tgt_fcpreq->transfer_length;
> + fcloop_fcp_copy_data(op, tgt_fcpreq->sg, fcpreq
> ->first_sgl,
> + tgt_fcpreq->offset, xfrlen);
> + fcpreq->transferred_length += xfrlen;
> + break;
> +
> + case NVMET_FCOP_READDATA:
> + case NVMET_FCOP_READDATA_RSP:
> + xfrlen = tgt_fcpreq->transfer_length;
> + fcloop_fcp_copy_data(op, tgt_fcpreq->sg, fcpreq
> ->first_sgl,
> + tgt_fcpreq->offset, xfrlen);
> + fcpreq->transferred_length += xfrlen;
> + if (op == NVMET_FCOP_READDATA)
> + break;
> +
> + /* Fall-Thru to RSP handling */
> +
> + case NVMET_FCOP_RSP:
> + rsplen = ((fcpreq->rsplen < tgt_fcpreq->rsplen) ?
> + fcpreq->rsplen : tgt_fcpreq
> ->rsplen);
> + memcpy(fcpreq->rspaddr, tgt_fcpreq->rspaddr,
> rsplen);
> + if (rsplen < tgt_fcpreq->rsplen)
> + fcp_err = -E2BIG;
> + fcpreq->rcv_rsplen = rsplen;
> + fcpreq->status = 0;
> + tfcp_req->status = 0;
> + break;
> +
> + case NVMET_FCOP_ABORT:
> + tfcp_req->status = NVME_SC_FC_TRANSPORT_ABORTED;
> + break;
> +
> + default:
> + fcp_err = -EINVAL;
> + break;
> + }
> +
> + tgt_fcpreq->transferred_length = xfrlen;
> + tgt_fcpreq->fcp_error = fcp_err;
> + tgt_fcpreq->done(tgt_fcpreq);
> +
> + if ((!fcp_err) && (op == NVMET_FCOP_RSP ||
> + op == NVMET_FCOP_READDATA_RSP ||
> + op == NVMET_FCOP_ABORT))
> + schedule_work(&tfcp_req->work);
> +
> + return 0;
if this function returns an 'int', why would it always return 0 and not
the fcp_err values (if there is an error)?
> +}
> +
> +void
> +fcloop_ls_abort(struct nvme_fc_local_port *localport,
> + struct nvme_fc_remote_port *remoteport,
> + struct nvmefc_ls_req *lsreq)
> +{
> +}
> +
> +void
> +fcloop_fcp_abort(struct nvme_fc_local_port *localport,
> + struct nvme_fc_remote_port *remoteport,
> + void *hw_queue_handle,
> + struct nvmefc_fcp_req *fcpreq)
> +{
> +}
> +
> +
> +struct nvme_fc_port_template fctemplate = {
> + .create_queue = fcloop_create_queue,
> + .delete_queue = fcloop_delete_queue,
> + .ls_req = fcloop_ls_req,
> + .fcp_io = fcloop_fcp_req,
> + .ls_abort = fcloop_ls_abort,
> + .fcp_abort = fcloop_fcp_abort,
> +
> + .max_hw_queues = 1,
> + .max_sgl_segments = 256,
> + .max_dif_sgl_segments = 256,
> + .dma_boundary = 0xFFFFFFFF,
Between here and "struct nvmet_fc_target_template tgttemplate" they are
assigning the same magic values to the same variable names, so why not
have these values as #defines for a tad easier maintainability?
> + /* sizes of additional private data for data structures */
> + .local_priv_sz = sizeof(struct fcloop_lport),
> + .remote_priv_sz = sizeof(struct fcloop_rport),
> + .lsrqst_priv_sz = sizeof(struct fcloop_lsreq),
> + .fcprqst_priv_sz = sizeof(struct fcloop_fcpreq),
> +};
> +
> +struct nvmet_fc_target_template tgttemplate = {
> + .xmt_ls_rsp = fcloop_xmt_ls_rsp,
> + .fcp_op = fcloop_fcp_op,
> +
> + .max_hw_queues = 1,
> + .max_sgl_segments = 256,
> + .max_dif_sgl_segments = 256,
> + .dma_boundary = 0xFFFFFFFF,
> +
see above comment.
> + /* optional features */
> + .target_features = NVMET_FCTGTFEAT_READDATA_RSP,
> +
> + /* sizes of additional private data for data structures */
> + .target_priv_sz = sizeof(struct fcloop_tgtport),
> +};
> +
> +static ssize_t
> +fcloop_create_local_port(struct device *dev, struct device_attribute
> *attr,
> + const char *buf, size_t count)
> +{
> + struct nvme_fc_port_info pinfo;
> + struct fcloop_ctrl_options *opts;
> + struct nvme_fc_local_port *localport;
> + struct fcloop_lport *lport;
> + int ret;
> +
> + opts = kzalloc(sizeof(*opts), GFP_KERNEL);
> + if (!opts)
> + return -ENOMEM;
> +
> + ret = fcloop_parse_options(opts, buf);
> + if (ret)
> + goto out_free_opts;
> +
> + /* everything there ? */
> + if ((opts->mask & LPORT_OPTS) != LPORT_OPTS) {
> + ret = -EINVAL;
> + goto out_free_opts;
> + }
> +
> + pinfo.fabric_name = opts->fabric;
> + pinfo.node_name = opts->wwnn;
> + pinfo.port_name = opts->wwpn;
> + pinfo.port_role = opts->roles;
> + pinfo.port_id = opts->fcaddr;
> +
> + ret = nvme_fc_register_localport(&pinfo, &fctemplate, NULL,
> &localport);
> + if (!ret) {
> + /* success */
> + lport = localport->private;
> + lport->localport = localport;
> + INIT_LIST_HEAD(&lport->list);
> + INIT_LIST_HEAD(&lport->rport_list);
> + list_add_tail(&lport->list, &fcloop_lports);
> +
> + /* mark all of the input buffer consumed */
> + ret = count;
> + }
> +
> +out_free_opts:
> + kfree(opts);
> + return ret;
> +}
> +
> +static int __delete_local_port(struct fcloop_lport *lport)
> +{
> + int ret;
> +
> + if (!list_empty(&lport->rport_list))
> + return -EBUSY;
> +
> + list_del(&lport->list);
> +
Is a mutex or locking mechanism not needed here for this list?
> + ret = nvme_fc_unregister_localport(lport->localport);
> +
> + return ret;
> +}
> +
> +static ssize_t
> +fcloop_delete_local_port(struct device *dev, struct device_attribute
> *attr,
> + const char *buf, size_t count)
> +{
> + struct fcloop_lport *lport, *lnext;
> + u64 fabric, portname;
> + int ret;
> +
> + ret = fcloop_parse_nm_options(dev, &fabric, &portname, buf);
> + if (ret)
> + return ret;
> +
> + list_for_each_entry_safe(lport, lnext, &fcloop_lports, list)
> {
> + if ((lport->localport->fabric_name == fabric) &&
> + (lport->localport->port_name == portname)) {
> + break;
> + }
> + }
> + if (is_end_of_list(lport, &fcloop_lports, list))
> + return -ENOENT;
> +
> + ret = __delete_local_port(lport);
> +
> + if (!ret)
> + return count;
> +
> + return ret;
> +}
> +
> +static ssize_t
> +fcloop_create_remote_port(struct device *dev, struct
> device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct fcloop_ctrl_options *opts;
> + struct fcloop_lport *lport, *lnext;
> + struct nvme_fc_remote_port *remoteport;
> + struct fcloop_rport *rport;
> + struct nvme_fc_port_info pinfo;
> + struct nvmet_fc_port_info tinfo;
> + int ret;
> +
> + opts = kzalloc(sizeof(*opts), GFP_KERNEL);
> + if (!opts)
> + return -ENOMEM;
> +
> + ret = fcloop_parse_options(opts, buf);
> + if (ret)
> + goto out_free_opts;
> +
> + /* everything there ? */
> + if ((opts->mask & RPORT_OPTS) != RPORT_OPTS) {
> + ret = -EINVAL;
> + goto out_free_opts;
> + }
> +
> + pinfo.fabric_name = tinfo.fabric_name = opts->fabric;
> + pinfo.node_name = tinfo.node_name = opts->wwnn;
> + pinfo.port_name = tinfo.port_name = opts->wwpn;
> + pinfo.port_role = opts->roles;
> + pinfo.port_id = tinfo.port_id = opts->fcaddr;
> +
> + list_for_each_entry_safe(lport, lnext, &fcloop_lports, list)
> {
> + if (lport->localport->fabric_name == opts->fabric)
> + break;
> + }
> + if (is_end_of_list(lport, &fcloop_lports, list)) {
> + ret = -ENOENT;
> + goto out_free_opts;
> + }
> +
> + ret = nvme_fc_register_remoteport(lport->localport, &pinfo,
> + &remoteport);
> + if (ret)
> + goto out_free_opts;
> +
> + /* success */
> + rport = remoteport->private;
> + rport->remoteport = remoteport;
> + INIT_LIST_HEAD(&rport->list);
> + list_add_tail(&rport->list, &lport->rport_list);
is there not a mutex or locking mechanism needed when manipulating this
list?
> +
> + /* tie into nvme target side */
> + ret = nvmet_fc_register_targetport(&tinfo, &tgttemplate,
> NULL,
> + &rport->targetport);
> + if (ret) {
> + list_del(&rport->list);
> + (void)nvme_fc_unregister_remoteport(remoteport);
> + } else {
> + struct fcloop_tgtport *tport;
> +
> + tport = rport->targetport->private;
> + tport->rport = rport;
> + tport->lport = lport;
> + tport->tgtport = rport->targetport;
> +
> + /* mark all of the input buffer consumed */
> + ret = count;
> + }
> +
> +out_free_opts:
> + kfree(opts);
> + return ret;
> +}
> +
Thanks,
J
More information about the Linux-nvme
mailing list