From patchwork Sun Aug 5 11:39:49 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Avri Altman X-Patchwork-Id: 10556115 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 DB13E157B for ; Sun, 5 Aug 2018 11:40:30 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C630B2991F for ; Sun, 5 Aug 2018 11:40:30 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id BA6C02993B; Sun, 5 Aug 2018 11:40:30 +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 C8A902991F for ; Sun, 5 Aug 2018 11:40:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726159AbeHENoq (ORCPT ); Sun, 5 Aug 2018 09:44:46 -0400 Received: from esa5.hgst.iphmx.com ([216.71.153.144]:63955 "EHLO esa5.hgst.iphmx.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726091AbeHENoq (ORCPT ); Sun, 5 Aug 2018 09:44:46 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=wdc.com; i=@wdc.com; q=dns/txt; s=dkim.wdc.com; t=1533469226; x=1565005226; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=WdSEcLyTThMDOVovVsPFMMdayWz4vnbXBguKGlRP1Zk=; b=VSJpEtjdqY5YIOdRhpEbvfe/U4Qo2CU+Rf7lYSxyCfLWcCmH6q9e1Kvo oPyiDRl8QUTVDHbIkcTiRsTTJaj5qEMeLRsmveIiaufix702EsJl/ygvo jiHLkKLFGqDJOyNxZJbOqu1CPQLQcxJmKuoXqIfRCiuCBl70lLfBBRKHa 9lohekAcVJN/u7TdzhDDQFW2xplkCnq/VipNDcAEvT+Wjdcfnbgc1ChTs laHc5CuS4k/NXyQB21Q5wgthcjkbzsH5+TWfUJFOrq20IZPjNBwDjLBh2 YlVVHH9sEaoh3YIsUImc7R7rxKT/Ewi6w1icNOJo0cOJUgEZnYOia4bWJ Q==; X-IronPort-AV: E=Sophos;i="5.51,447,1526313600"; d="scan'208";a="87459763" 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; 05 Aug 2018 19:40:26 +0800 Received: from uls-op-cesaip02.wdc.com ([10.248.3.37]) by uls-op-cesaep01.wdc.com with ESMTP; 05 Aug 2018 04:28:44 -0700 Received: from kfae422988.sdcorp.global.sandisk.com ([10.0.231.37]) by uls-op-cesaip02.wdc.com with ESMTP; 05 Aug 2018 04:40:23 -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: [PATCH v2 1/8] scsi: Add ufs transport class Date: Sun, 5 Aug 2018 14:39:49 +0300 Message-Id: <1533469196-300-2-git-send-email-avri.altman@wdc.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1533469196-300-1-git-send-email-avri.altman@wdc.com> References: <1533469196-300-1-git-send-email-avri.altman@wdc.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 | 354 ++++++++++++++++++++++++++++++++++++++ include/scsi/scsi_transport_ufs.h | 44 +++++ 4 files changed, 412 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..06349c6 --- /dev/null +++ b/drivers/scsi/scsi_transport_ufs.c @@ -0,0 +1,354 @@ +// 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 + + +#define UFS_HOST_ATTRS 0 +#define UFS_PORT_ATTRS 0 + +#define MAX_PORTS 256 +static DEFINE_IDA(ufs_ida); + +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; +}; + +static inline struct ufs_internal * +to_ufs_internal(struct scsi_transport_template *tmpl) +{ + return container_of(tmpl, struct ufs_internal, t); +} + +struct ufs_host_attrs { + atomic_t active_ports; +}; + +static inline struct ufs_host_attrs * +to_ufs_host_attrs(struct Scsi_Host *shost) +{ + return (struct ufs_host_attrs *)shost->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); + 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->active_ports, 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); + struct Scsi_Host *shost = dev_to_shost(dev->parent); + struct ufs_host_attrs *ufs_host = to_ufs_host_attrs(shost); + + ida_simple_remove(&ufs_ida, port->id); + atomic_dec(&ufs_host->active_ports); + 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 = ida_simple_get(&ufs_ida, 0, MAX_PORTS, GFP_KERNEL); + if (port->id < 0) + return NULL; + atomic_inc(&ufs_host->active_ports); + + 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..c45dfb8 --- /dev/null +++ b/include/scsi/scsi_transport_ufs.h @@ -0,0 +1,44 @@ +/* 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 +#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; +}; + +static inline struct ufs_port *dev_to_ufs_port(struct device *d) +{ + return 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