From patchwork Sun Jul 29 13:33:21 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Avri Altman X-Patchwork-Id: 10548251 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7E0DE139A for ; Sun, 29 Jul 2018 13:34:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6C2E72A4F9 for ; Sun, 29 Jul 2018 13:34:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 603612A4FD; Sun, 29 Jul 2018 13:34:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 68DC42A4F9 for ; Sun, 29 Jul 2018 13:34:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726751AbeG2PF0 (ORCPT ); Sun, 29 Jul 2018 11:05:26 -0400 Received: from esa4.hgst.iphmx.com ([216.71.154.42]:25577 "EHLO esa4.hgst.iphmx.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726461AbeG2PF0 (ORCPT ); Sun, 29 Jul 2018 11:05:26 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=sandisk.com; i=@sandisk.com; q=dns/txt; s=dkim.sandisk.com; t=1532871297; x=1564407297; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Mi5uq22bK8X8H25hOLrxNOX3B79/gAKRFMEjdk9AJk8=; b=QyScs8nMWZNY+DOM4bU62nX45TscCZAtLrJxiKvbZRWMfVdVYthU3QMt 286s0MYBSbUaCmhcSapHu9CEfx/nsICbcRUC8urO5wgW7IYkr5Ksf4Voy XSqfA3J/apSmTInZ5gtl4y0eFt6LBtkMs8VFSRTnSYW0+3++xrAbxuo0F 2hLxjOobtgi/ZE7ypjSBuxHe6CLdADa1I71P+LG6jIcHq1GlLT0fn1Jjg fr5ZzeZCgRQtqXXBwfIMpeZU7kTs4HTx5khtLYJSYZN4wg/zS9/B/SJTb SW+XLcpRTFYHXNjE8+3VonrVyvHtUhTuYae79y2sDogWoCr6YIPJXcJ+R Q==; X-IronPort-AV: E=Sophos;i="5.51,418,1526313600"; d="scan'208";a="85332496" Received: from h199-255-45-14.hgst.com (HELO uls-op-cesaep01.wdc.com) ([199.255.45.14]) by ob1.hgst.iphmx.com with ESMTP; 29 Jul 2018 21:34:57 +0800 Received: from uls-op-cesaip01.wdc.com ([10.248.3.36]) by uls-op-cesaep01.wdc.com with ESMTP; 29 Jul 2018 06:23:27 -0700 Received: from kfae422988.sdcorp.global.sandisk.com ([10.0.231.37]) by uls-op-cesaip01.wdc.com with ESMTP; 29 Jul 2018 06:34:55 -0700 From: Avri Altman To: Christoph Hellwig , Johannes Thumshirn , Hannes Reinecke , Bart Van Assche , "James E.J. Bottomley" , "Martin K. Petersen" , linux-scsi@vger.kernel.org Cc: Stanislav Nijnikov , Avi Shchislowski , Alex Lemberg , Subhash Jadavani , Vinayak Holikatti , Avri Altman Subject: [RFC 1/6] scsi: Add ufs transport class Date: Sun, 29 Jul 2018 16:33:21 +0300 Message-Id: <1532871206-6311-2-git-send-email-avri.altman@sandisk.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1532871206-6311-1-git-send-email-avri.altman@sandisk.com> References: <1532871206-6311-1-git-send-email-avri.altman@sandisk.com> MIME-Version: 1.0 Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Scsi transport is a framework that allow to send scsi commands to a non-scsi devices. Still, it is flexible enough to allow sending non-scsi commands as well. We will use this framework to manage ufs devices by sending UPIU transactions. In addition to the basic SCSI core objects this transport class introduces two additional (currently empty) class objects: “ufs-host” and “ufs-port”. There is only one “ufs-host” in the system, but can be more-than-one “ufs-ports”. Those classes are left empty for now, as ufs-sysfs already contains an abundant amount of attributes. A “ufs-port” is purely a software object. Evidently, the function template takes no port as an argument, as the driver has no concept of "port". We only need it as a hanging point in the bsg device tree, so maybe a more appropriate term would be: "ufs-bsg-dev-node". The ufs-port has a pretty lean structure. This is because we are using upiu transactions that contains the outmost detailed info, so we don't really need complex constructs to support it. The transport will keep track of its ufs-ports via its scsi host. Signed-off-by: Avri Altman --- drivers/scsi/Kconfig | 13 ++ drivers/scsi/Makefile | 1 + drivers/scsi/scsi_transport_ufs.c | 337 ++++++++++++++++++++++++++++++++++++++ include/scsi/scsi_transport_ufs.h | 40 +++++ 4 files changed, 391 insertions(+) create mode 100644 drivers/scsi/scsi_transport_ufs.c create mode 100644 include/scsi/scsi_transport_ufs.h diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 8fc851a..9e99275 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -290,6 +290,19 @@ config SCSI_SAS_ATTRS If you wish to export transport-specific information about each attached SAS device to sysfs, say Y. +config SCSI_UFS_ATTRS + tristate "UFS Transport Attributes" + depends on SCSI + select BLK_DEV_BSGLIB + help + Scsi transport is a framework that allow to send scsi commands to + a non-scsi devices. Still, it is flexible enough to allow sending + non-scsi commands as well. We will use this framework to manage + ufs devices by sending UPIU transactions. + + If you wish to export transport-specific information about + each attached UFS device to sysfs, say Y. + source "drivers/scsi/libsas/Kconfig" config SCSI_SRP_ATTRS diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index d1bad43..e0efc06 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o obj-$(CONFIG_SCSI_SAS_ATTRS) += scsi_transport_sas.o obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas/ obj-$(CONFIG_SCSI_SRP_ATTRS) += scsi_transport_srp.o +obj-$(CONFIG_SCSI_UFS_ATTRS) += scsi_transport_ufs.o obj-$(CONFIG_SCSI_DH) += device_handler/ obj-$(CONFIG_LIBFC) += libfc/ diff --git a/drivers/scsi/scsi_transport_ufs.c b/drivers/scsi/scsi_transport_ufs.c new file mode 100644 index 0000000..62aec49 --- /dev/null +++ b/drivers/scsi/scsi_transport_ufs.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SCSI UFS transport class + * + * Copyright (C) 2018 Western Digital Corporation + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#define UFS_HOST_ATTRS 0 +#define UFS_PORT_ATTRS 0 + +struct ufs_internal { + struct scsi_transport_template t; + struct ufs_function_template *f; + + struct device_attribute *host_attrs[UFS_HOST_ATTRS + 1]; + struct device_attribute *port_attrs[UFS_PORT_ATTRS + 1]; + struct transport_container port_attr_cont; +}; +#define to_ufs_internal(tmpl) container_of(tmpl, struct ufs_internal, t) + +struct ufs_host_attrs { + atomic_t next_port_id; +}; +#define to_ufs_host_attrs(x) ((struct ufs_host_attrs *)(x)->shost_data) + +static int ufs_bsg_dispatch(struct bsg_job *job) +{ + struct Scsi_Host *shost = dev_to_shost(job->dev); + struct ufs_internal *i = to_ufs_internal(shost->transportt); + + /* check for payload_len when we'll support data transfer upiu */ + + return i->f->bsg_request(job); +} + +static int ufs_bsg_initialize(struct Scsi_Host *shost, struct ufs_port *port) +{ + struct request_queue *q; + struct ufs_internal *i = to_ufs_internal(shost->transportt); + + if (!i->f->bsg_request) { + pr_info("%s can't handle ufs requests\n", shost->hostt->name); + return 0; + } + + q = bsg_setup_queue(&port->dev, dev_name(&port->dev), + ufs_bsg_dispatch, 0, NULL); + if (IS_ERR(q)) + return PTR_ERR(q); + port->q = q; + + return 0; +} + +static int ufs_host_setup(struct transport_container *tc, struct device *dev, + struct device *cdev) +{ + struct Scsi_Host *shost = dev_to_shost(dev); + struct ufs_host_attrs *ufs_host = to_ufs_host_attrs(shost); + + atomic_set(&ufs_host->next_port_id, 0); + + return 0; +} + +static DECLARE_TRANSPORT_CLASS(ufs_host_class, "ufs_host", ufs_host_setup, + NULL, NULL); + +static DECLARE_TRANSPORT_CLASS(ufs_port_class, "ufs_ports", + NULL, NULL, NULL); + +static int ufs_host_match(struct attribute_container *cont, + struct device *dev) +{ + struct Scsi_Host *shost; + struct ufs_internal *i; + + if (!scsi_is_host_device(dev)) + return 0; + + shost = dev_to_shost(dev); + if (!shost->transportt) + return 0; + + if (shost->transportt->host_attrs.ac.class != &ufs_host_class.class) + return 0; + + i = to_ufs_internal(shost->transportt); + + return &i->t.host_attrs.ac == cont; +} + +static void ufs_port_release(struct device *dev) +{ + struct ufs_port *port = dev_to_ufs_port(dev); + + put_device(dev->parent); + kfree(port); +} + +static inline int scsi_is_ufs_port(const struct device *dev) +{ + return dev->release == ufs_port_release; +} + +static int ufs_port_match(struct attribute_container *cont, struct device *dev) +{ + struct Scsi_Host *shost; + struct ufs_internal *i; + + if (!scsi_is_ufs_port(dev)) + return 0; + + shost = dev_to_shost(dev->parent); + + if (!shost->transportt) + return 0; + + if (shost->transportt->host_attrs.ac.class != &ufs_host_class.class) + return 0; + + i = to_ufs_internal(shost->transportt); + return &i->port_attr_cont.ac == cont; +} + +/** + * ufs_attach_transport - instantiate UFS transport template + * @ft: UFS transport class function template + */ +struct scsi_transport_template * +ufs_attach_transport(struct ufs_function_template *ft) +{ + struct ufs_internal *i = kzalloc(sizeof(struct ufs_internal), + GFP_KERNEL); + + if (unlikely(!i)) + return NULL; + + i->f = ft; + i->t.host_attrs.ac.attrs = &i->host_attrs[0]; + i->t.host_attrs.ac.class = &ufs_host_class.class; + i->t.host_attrs.ac.match = ufs_host_match; + i->t.host_size = sizeof(struct ufs_host_attrs); + i->host_attrs[0] = NULL; + transport_container_register(&i->t.host_attrs); + + i->port_attr_cont.ac.class = &ufs_port_class.class; + i->port_attr_cont.ac.attrs = &i->port_attrs[0]; + i->port_attr_cont.ac.match = ufs_port_match; + i->port_attrs[0] = NULL; + transport_container_register(&i->port_attr_cont); + + return &i->t; +} +EXPORT_SYMBOL(ufs_attach_transport); + +/** + * ufs_release_transport - release UFS transport template instance + * @t: transport template instance + */ +void ufs_release_transport(struct scsi_transport_template *t) +{ + struct ufs_internal *i = to_ufs_internal(t); + + transport_container_unregister(&i->t.host_attrs); + transport_container_unregister(&i->port_attr_cont); + kfree(i); +} +EXPORT_SYMBOL(ufs_release_transport); + +/** ufs_port_alloc - allocate and initialize a UFS port structure + * @shost: scsi host that the port is attached to + * + * Allocates a UFS port structure. It will be added to the device tree + * below the device specified by its Scsi_Host parent. + * Returns %NULL on error + */ +struct ufs_port *ufs_port_alloc(struct Scsi_Host *shost) +{ + struct ufs_port *port; + struct device *parent = &shost->shost_gendev; + struct ufs_host_attrs *ufs_host = to_ufs_host_attrs(shost); + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return NULL; + + port->id = atomic_inc_return(&ufs_host->next_port_id); + + device_initialize(&port->dev); + + port->dev.parent = get_device(parent); + port->dev.release = ufs_port_release; + + dev_set_name(&port->dev, "ufs-port-%d:%d", shost->host_no, port->id); + + transport_setup_device(&port->dev); + + return port; +} +EXPORT_SYMBOL(ufs_port_alloc); + +/** + * ufs_port_add - add a UFS port to the device hierarchy + * @port: port to be added + * + * publishes a port to the rest of the system + */ +int ufs_port_add(struct ufs_port *port) +{ + struct device *dev = &port->dev; + struct Scsi_Host *shost = dev_to_shost(dev->parent); + int error; + + if (!port) { + pr_err("%s failed with null port\n", __func__); + return -EINVAL; + } + + error = device_add(dev); + + if (error) + return error; + + transport_add_device(dev); + transport_configure_device(dev); + + if (ufs_bsg_initialize(shost, port)) + dev_err(dev, "fail to initialize a bsg dev %d\n", + shost->host_no); + + return 0; +} +EXPORT_SYMBOL(ufs_port_add); + +/** + * ufs_port_free - free a ufs port + * @port: ufs port to free + * + * Frees the specified ufs port. Should be called on ufs_port_add() failure. + */ +void ufs_port_free(struct ufs_port *port) +{ + transport_destroy_device(&port->dev); + put_device(&port->dev); +} +EXPORT_SYMBOL(ufs_port_free); + +/** + * ufs_port_delete - remove a ufs port + * @port: ufs port to remove + * + */ +void ufs_port_delete(struct ufs_port *port) +{ + struct device *dev = &port->dev; + + if (port->q) + bsg_unregister_queue(port->q); + + transport_remove_device(dev); + device_del(dev); + transport_destroy_device(dev); + + put_device(dev); +} +EXPORT_SYMBOL(ufs_port_delete); + +static int do_ufs_port_del(struct device *dev, void *data) +{ + if (scsi_is_ufs_port(dev)) + ufs_port_delete(dev_to_ufs_port(dev)); + + return 0; +} + +/** + * ufs_remove_host - tear down a ufs port devices + * @shost: the scsi host which is parenting the ufs port objects + * + * Removes all ufs port devices. + * Unharm Other devices that are attached to that host. + */ +void ufs_remove_host(struct Scsi_Host *shost) +{ + device_for_each_child(&shost->shost_gendev, NULL, do_ufs_port_del); +} +EXPORT_SYMBOL_GPL(ufs_remove_host); + +static __init int ufs_transport_init(void) +{ + int error; + + error = transport_class_register(&ufs_host_class); + if (error) + goto out; + + error = transport_class_register(&ufs_port_class); + if (error) + goto out_unregister_transport; + + return 0; + +out_unregister_transport: + transport_class_unregister(&ufs_host_class); +out: + return error; +} + +static void __exit ufs_transport_exit(void) +{ + transport_class_unregister(&ufs_host_class); + transport_class_unregister(&ufs_port_class); +} + +MODULE_AUTHOR("WDC"); +MODULE_DESCRIPTION("UFS Transport Attributes"); +MODULE_LICENSE("GPL"); + +module_init(ufs_transport_init); +module_exit(ufs_transport_exit); diff --git a/include/scsi/scsi_transport_ufs.h b/include/scsi/scsi_transport_ufs.h new file mode 100644 index 0000000..483bce4 --- /dev/null +++ b/include/scsi/scsi_transport_ufs.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Western Digital Corporation + */ + +#ifndef SCSI_TRANSPORT_UFS_H +#define SCSI_TRANSPORT_UFS_H + +#include +#include +#include + + +struct ufs_function_template { + int (*bsg_request)(struct bsg_job *job); +}; + +/* One might expect a more complex structure – + * some representation of a list containing all the luns of the systems, + * including the w-luns. However, as we are using upiu transactions that + * contains the lun index in its header, we need not to have its representation + * in the device tree. + */ +struct ufs_port { + struct device dev; + int id; + struct request_queue *q; +}; +#define dev_to_ufs_port(d) \ + container_of((d), struct ufs_port, dev) + +struct scsi_transport_template * +ufs_attach_transport(struct ufs_function_template *ft); +void ufs_release_transport(struct scsi_transport_template *t); +struct ufs_port *ufs_port_alloc(struct Scsi_Host *shost); +int ufs_port_add(struct ufs_port *port); +void ufs_port_free(struct ufs_port *port); +void ufs_port_delete(struct ufs_port *port); +void ufs_remove_host(struct Scsi_Host *shost); +#endif