* [PATCH 01/13] firewire: Add function to get speed from opaque struct fw_request
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-11 19:44 ` [PATCH 02/13] firewire: Add EXPORT_SYMBOL_GPL(fw_card_release) Chris Boot
` (13 subsequent siblings)
14 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
Sometimes it's useful to know the FireWire speed of the request that has
just come in to a fw_address_handler callback. As struct fw_request is
opaque we can't peek inside to get the speed out of the struct fw_packet
that's just inside. For example, the SBP-2 spec says:
"The speed at which the block write request to the MANAGEMENT_AGENT
register is received shall determine the speed used by the target for
all subsequent requests to read the initiator’s configuration ROM, fetch
ORB’s from initiator memory or store status at the initiator’s
status_FIFO. Command block ORB’s separately specify the speed for
requests addressed to the data buffer or page table."
[ ANSI T10/1155D Revision 4 page 53/54 ]
Signed-off-by: Chris Boot <bootc@bootc.net>
Acked-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Cc: Clemens Ladisch <clemens@ladisch.de>
---
drivers/firewire/core-transaction.c | 16 ++++++++++++++++
include/linux/firewire.h | 1 +
2 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index 855ab3f..614f592 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -820,6 +820,22 @@ void fw_send_response(struct fw_card *card,
}
EXPORT_SYMBOL(fw_send_response);
+/**
+ * fw_get_request_speed() - Discover bus speed used for this request
+ * @request: The struct fw_request from which to obtain the speed.
+ *
+ * In certain circumstances it's important to be able to obtain the speed at
+ * which a request was made to an address handler, for example when
+ * implementing an SBP-2 or SBP-3 target. This function inspects the response
+ * object to obtain the speed, which is copied from the request packet in
+ * allocate_request().
+ */
+int fw_get_request_speed(struct fw_request *request)
+{
+ return request->response.speed;
+}
+EXPORT_SYMBOL(fw_get_request_speed);
+
static void handle_exclusive_region_request(struct fw_card *card,
struct fw_packet *p,
struct fw_request *request,
diff --git a/include/linux/firewire.h b/include/linux/firewire.h
index 84ccf8e..f010307 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -340,6 +340,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler,
void fw_core_remove_address_handler(struct fw_address_handler *handler);
void fw_send_response(struct fw_card *card,
struct fw_request *request, int rcode);
+int fw_get_request_speed(struct fw_request *request);
void fw_send_request(struct fw_card *card, struct fw_transaction *t,
int tcode, int destination_id, int generation, int speed,
unsigned long long offset, void *payload, size_t length,
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 02/13] firewire: Add EXPORT_SYMBOL_GPL(fw_card_release)
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
2012-02-11 19:44 ` [PATCH 01/13] firewire: Add function to get speed from opaque struct fw_request Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-11 19:44 ` [PATCH 03/13] firewire-sbp-target: Add Kconfig, Makefile and TODO Chris Boot
` (12 subsequent siblings)
14 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
The firewire-sbp-target module requires this so it can keep a reference
to the fw_card object in order that it can fetch ORBs to execute and
read/write related data and status information.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/firewire/core-card.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index 85661b0..42b180b 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -654,6 +654,7 @@ void fw_card_release(struct kref *kref)
complete(&card->done);
}
+EXPORT_SYMBOL_GPL(fw_card_release);
void fw_core_remove_card(struct fw_card *card)
{
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 03/13] firewire-sbp-target: Add Kconfig, Makefile and TODO
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
2012-02-11 19:44 ` [PATCH 01/13] firewire: Add function to get speed from opaque struct fw_request Chris Boot
2012-02-11 19:44 ` [PATCH 02/13] firewire: Add EXPORT_SYMBOL_GPL(fw_card_release) Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-13 12:50 ` Nicholas A. Bellinger
2012-02-11 19:44 ` [PATCH 04/13] firewire-sbp-target: Add sbp_base.h header Chris Boot
` (11 subsequent siblings)
14 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection
as a SCSI transport. This module uses the SCSI Target framework to
expose LUNs to other machines attached to a FireWire bus, in effect
acting as a FireWire hard disk similar to FireWire Target Disk mode on
older Apple computers.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/Kconfig | 14 ++++++++++++++
drivers/target/sbp/Makefile | 13 +++++++++++++
drivers/target/sbp/TODO | 7 +++++++
3 files changed, 34 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/Kconfig
create mode 100644 drivers/target/sbp/Makefile
create mode 100644 drivers/target/sbp/TODO
diff --git a/drivers/target/sbp/Kconfig b/drivers/target/sbp/Kconfig
new file mode 100644
index 0000000..8544010
--- /dev/null
+++ b/drivers/target/sbp/Kconfig
@@ -0,0 +1,14 @@
+
+config FIREWIRE_SBP_TARGET
+ tristate "FireWire SBP-2 fabric module"
+ depends on TARGET_CORE && CONFIGFS_FS && FIREWIRE && EXPERIMENTAL
+ default n
+ help
+ Say Y or M here to enable SCSI target functionality over FireWire.
+ This enables you to expose SCSI devices to other nodes on the FireWire
+ bus, for example hard disks. Similar to FireWire Target Disk mode on
+ older Apple computers.
+
+ To compile this driver as a module, say M here: The module will be
+ called firewire-sbp-target.
+
diff --git a/drivers/target/sbp/Makefile b/drivers/target/sbp/Makefile
new file mode 100644
index 0000000..3a3e155
--- /dev/null
+++ b/drivers/target/sbp/Makefile
@@ -0,0 +1,13 @@
+
+firewire-sbp-target-y += \
+ sbp_configfs.o \
+ sbp_fabric.o \
+ sbp_login.o \
+ sbp_management_agent.o \
+ sbp_proto.o \
+ sbp_scsi_cmnd.o \
+ sbp_target_agent.o \
+ sbp_util.o
+
+obj-$(CONFIG_FIREWIRE_SBP_TARGET) += firewire-sbp-target.o
+
diff --git a/drivers/target/sbp/TODO b/drivers/target/sbp/TODO
new file mode 100644
index 0000000..eaec1c9
--- /dev/null
+++ b/drivers/target/sbp/TODO
@@ -0,0 +1,7 @@
+* Force-terminate sessions when disabling targets
+* Ability to have several SCSI commands in-flight (TCQ?)
+* Retry failed FireWire transactions a few times with exponential backoff
+* Take into account the page_size field for transfers and/or page tables
+* Handle descriptor format sense data
+* Implement QUERY LOGINS management ORB
+* Implement TASK MANAGEMENT functionality
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH 03/13] firewire-sbp-target: Add Kconfig, Makefile and TODO
2012-02-11 19:44 ` [PATCH 03/13] firewire-sbp-target: Add Kconfig, Makefile and TODO Chris Boot
@ 2012-02-13 12:50 ` Nicholas A. Bellinger
0 siblings, 0 replies; 104+ messages in thread
From: Nicholas A. Bellinger @ 2012-02-13 12:50 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, stefanr
On Sat, 2012-02-11 at 19:44 +0000, Chris Boot wrote:
> The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection
> as a SCSI transport. This module uses the SCSI Target framework to
> expose LUNs to other machines attached to a FireWire bus, in effect
> acting as a FireWire hard disk similar to FireWire Target Disk mode on
> older Apple computers.
>
> Signed-off-by: Chris Boot <bootc@bootc.net>
> Cc: Andy Grover <agrover@redhat.com>
> Cc: Clemens Ladisch <clemens@ladisch.de>
> Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
> Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
> ---
> drivers/target/sbp/Kconfig | 14 ++++++++++++++
> drivers/target/sbp/Makefile | 13 +++++++++++++
> drivers/target/sbp/TODO | 7 +++++++
> 3 files changed, 34 insertions(+), 0 deletions(-)
> create mode 100644 drivers/target/sbp/Kconfig
> create mode 100644 drivers/target/sbp/Makefile
> create mode 100644 drivers/target/sbp/TODO
>
> diff --git a/drivers/target/sbp/Kconfig b/drivers/target/sbp/Kconfig
> new file mode 100644
> index 0000000..8544010
> --- /dev/null
> +++ b/drivers/target/sbp/Kconfig
> @@ -0,0 +1,14 @@
> +
> +config FIREWIRE_SBP_TARGET
> + tristate "FireWire SBP-2 fabric module"
> + depends on TARGET_CORE && CONFIGFS_FS && FIREWIRE && EXPERIMENTAL
> + default n
> + help
> + Say Y or M here to enable SCSI target functionality over FireWire.
> + This enables you to expose SCSI devices to other nodes on the FireWire
> + bus, for example hard disks. Similar to FireWire Target Disk mode on
> + older Apple computers.
> +
> + To compile this driver as a module, say M here: The module will be
> + called firewire-sbp-target.
> +
Hi Chris,
Just a minor Kconfig related item here. You can go ahead and drop the
extra depends on TARGET_CORE && CONFIGFS_FS here, as
drivers/target/Kconfig is already handling the dependencies for these
two config options before including drivers/target/sbp/Kconfig.
Thanks!
--nab
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH 04/13] firewire-sbp-target: Add sbp_base.h header
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (2 preceding siblings ...)
2012-02-11 19:44 ` [PATCH 03/13] firewire-sbp-target: Add Kconfig, Makefile and TODO Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-11 19:44 ` [PATCH 05/13] firewire-sbp-target: Add sbp_configfs.c Chris Boot
` (10 subsequent siblings)
14 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This header contains defines and structures that are common to many of
the modules of the target code. This includes SBP-2 protocol structures
and constants as well as a few structs for setting up the target, LUN
login information and session setup.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_base.h | 196 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 196 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_base.h
diff --git a/drivers/target/sbp/sbp_base.h b/drivers/target/sbp/sbp_base.h
new file mode 100644
index 0000000..629ae1a
--- /dev/null
+++ b/drivers/target/sbp/sbp_base.h
@@ -0,0 +1,196 @@
+
+#define SBP_VERSION "v0.1"
+#define SBP_NAMELEN 32
+
+#define SBP_ORB_FETCH_SIZE 8
+
+#define MANAGEMENT_AGENT_STATE_IDLE 0
+#define MANAGEMENT_AGENT_STATE_BUSY 1
+
+#define ORB_NOTIFY(v) (((v) >> 31) & 0x01)
+#define ORB_REQUEST_FORMAT(v) (((v) >> 29) & 0x03)
+
+#define MANAGEMENT_ORB_FUNCTION(v) (((v) >> 16) & 0x0f)
+
+#define MANAGEMENT_ORB_FUNCTION_LOGIN 0x0
+#define MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS 0x1
+#define MANAGEMENT_ORB_FUNCTION_RECONNECT 0x3
+#define MANAGEMENT_ORB_FUNCTION_SET_PASSWORD 0x4
+#define MANAGEMENT_ORB_FUNCTION_LOGOUT 0x7
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK 0xb
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET 0xc
+#define MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET 0xe
+#define MANAGEMENT_ORB_FUNCTION_TARGET_RESET 0xf
+
+#define LOGIN_ORB_EXCLUSIVE(v) (((v) >> 28) & 0x01)
+#define LOGIN_ORB_RESERVED(v) (((v) >> 24) & 0x0f)
+#define LOGIN_ORB_RECONNECT(v) (((v) >> 20) & 0x0f)
+#define LOGIN_ORB_LUN(v) (((v) >> 0) & 0xffff)
+#define LOGIN_ORB_PASSWORD_LENGTH(v) (((v) >> 16) & 0xffff)
+#define LOGIN_ORB_RESPONSE_LENGTH(v) (((v) >> 0) & 0xffff)
+
+#define RECONNECT_ORB_LOGIN_ID(v) (((v) >> 0) & 0xffff)
+#define LOGOUT_ORB_LOGIN_ID(v) (((v) >> 0) & 0xffff)
+
+#define CMDBLK_ORB_DIRECTION(v) (((v) >> 27) & 0x01)
+#define CMDBLK_ORB_SPEED(v) (((v) >> 24) & 0x07)
+#define CMDBLK_ORB_MAX_PAYLOAD(v) (((v) >> 20) & 0x0f)
+#define CMDBLK_ORB_PG_TBL_PRESENT(v) (((v) >> 19) & 0x01)
+#define CMDBLK_ORB_PG_SIZE(v) (((v) >> 16) & 0x07)
+#define CMDBLK_ORB_DATA_SIZE(v) (((v) >> 0) & 0xffff)
+
+#define STATUS_BLOCK_SRC(v) (((v) & 0x03) << 30)
+#define STATUS_BLOCK_RESP(v) (((v) & 0x03) << 28)
+#define STATUS_BLOCK_DEAD(v) (((v) ? 1 : 0) << 27)
+#define STATUS_BLOCK_LEN(v) (((v) & 0x07) << 24)
+#define STATUS_BLOCK_SBP_STATUS(v) (((v) & 0xff) << 16)
+#define STATUS_BLOCK_ORB_OFFSET_HIGH(v) (((v) & 0xffff) << 0)
+
+#define STATUS_SRC_ORB_CONTINUING 0
+#define STATUS_SRC_ORB_FINISHED 1
+#define STATUS_SRC_UNSOLICITED 2
+
+#define STATUS_RESP_REQUEST_COMPLETE 0
+#define STATUS_RESP_TRANSPORT_FAILURE 1
+#define STATUS_RESP_ILLEGAL_REQUEST 2
+#define STATUS_RESP_VENDOR_DEPENDENT 3
+
+#define SBP_STATUS_OK 0
+#define SBP_STATUS_REQ_TYPE_NOTSUPP 1
+#define SBP_STATUS_SPEED_NOTSUPP 2
+#define SBP_STATUS_PAGE_SIZE_NOTSUPP 3
+#define SBP_STATUS_ACCESS_DENIED 4
+#define SBP_STATUS_LUN_NOTSUPP 5
+#define SBP_STATUS_PAYLOAD_TOO_SMALL 6
+/* 7 is reserved */
+#define SBP_STATUS_RESOURCES_UNAVAIL 8
+#define SBP_STATUS_FUNCTION_REJECTED 9
+#define SBP_STATUS_LOGIN_ID_UNKNOWN 10
+#define SBP_STATUS_DUMMY_ORB_COMPLETE 11
+#define SBP_STATUS_REQUEST_ABORTED 12
+#define SBP_STATUS_UNSPECIFIED_ERROR 0xff
+
+#define AGENT_STATE_RESET 0
+#define AGENT_STATE_ACTIVE 1
+#define AGENT_STATE_SUSPENDED 2
+#define AGENT_STATE_DEAD 3
+
+struct sbp2_pointer {
+ __be32 high;
+ __be32 low;
+};
+
+struct sbp_command_block_orb {
+ struct sbp2_pointer next_orb;
+ struct sbp2_pointer data_descriptor;
+ __be32 misc;
+ u8 command_block[12];
+};
+
+struct sbp_page_table_entry {
+ __be16 segment_length;
+ __be16 segment_base_hi;
+ __be32 segment_base_lo;
+};
+
+struct sbp_management_orb {
+ struct sbp2_pointer ptr1;
+ struct sbp2_pointer ptr2;
+ __be32 misc;
+ __be32 length;
+ struct sbp2_pointer status_fifo;
+};
+
+struct sbp_status_block {
+ __be32 status;
+ __be32 orb_low;
+ u8 data[24];
+};
+
+struct sbp_login_response_block {
+ __be32 misc;
+ struct sbp2_pointer command_block_agent;
+ __be32 reconnect_hold;
+};
+
+struct sbp_login_descriptor {
+ struct sbp_session *sess;
+ struct list_head link;
+
+ struct se_lun *lun;
+
+ u64 status_fifo_addr;
+ int exclusive;
+ u16 login_id;
+ atomic_t unsolicited_status_enable;
+
+ struct sbp_target_agent *tgt_agt;
+};
+
+struct sbp_session {
+ struct se_session *se_sess;
+ struct list_head login_list;
+ struct delayed_work maint_work;
+
+ u64 guid; /* login_owner_EUI_64 */
+ int node_id; /* login_owner_ID */
+
+ struct fw_card *card;
+ int generation;
+ int speed;
+
+ int reconnect_hold;
+ u64 reconnect_expires;
+};
+
+struct sbp_nacl {
+ /* Initiator EUI-64 */
+ u64 guid;
+ /* ASCII formatted GUID for SBP Initiator port */
+ char iport_name[SBP_NAMELEN];
+ /* Returned by sbp_make_nodeacl() */
+ struct se_node_acl se_node_acl;
+};
+
+struct sbp_lun {
+ struct list_head link;
+ struct se_lun *se_lun;
+};
+
+struct sbp_tpg {
+ /* Target portal group tag for TCM */
+ u16 tport_tpgt;
+ /* Pointer back to sbp_tport */
+ struct sbp_tport *tport;
+ /* Returned by sbp_make_tpg() */
+ struct se_portal_group se_tpg;
+
+ struct list_head lun_list;
+};
+
+struct sbp_tport {
+ /* Target Unit Identifier (EUI-64) */
+ u64 guid;
+ /* Target port name */
+ char tport_name[SBP_NAMELEN];
+ /* Returned by sbp_make_tport() */
+ struct se_wwn tport_wwn;
+
+ struct sbp_tpg *tpg;
+
+ /* FireWire unit directory */
+ struct fw_descriptor unit_directory;
+
+ /* SBP Management Agent */
+ struct sbp_management_agent *mgt_agt;
+
+ /* Parameters */
+ int enable;
+ s32 directory_id;
+ int mgt_orb_timeout;
+ int max_reconnect_timeout;
+ int max_logins_per_lun;
+};
+
+extern struct target_fabric_configfs *sbp_fabric_configfs;
+
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 05/13] firewire-sbp-target: Add sbp_configfs.c
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (3 preceding siblings ...)
2012-02-11 19:44 ` [PATCH 04/13] firewire-sbp-target: Add sbp_base.h header Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-11 19:44 ` [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h} Chris Boot
` (9 subsequent siblings)
14 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This is used to glue the target framework's configfs code to the target
code, and what is used to create targets and link them to LUNs to
export. The code to create the FireWire unit directory to advertise
targets on the FireWire bus is also in here.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_configfs.c | 759 +++++++++++++++++++++++++++++++++++++
1 files changed, 759 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_configfs.c
diff --git a/drivers/target/sbp/sbp_configfs.c b/drivers/target/sbp/sbp_configfs.c
new file mode 100644
index 0000000..530d1d7
--- /dev/null
+++ b/drivers/target/sbp/sbp_configfs.c
@@ -0,0 +1,759 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_management_agent.h"
+#include "sbp_proto.h"
+
+/* Local pointer to allocated TCM configfs fabric module */
+struct target_fabric_configfs *sbp_fabric_configfs;
+
+static const u32 sbp_unit_directory_template[] = {
+ 0x1200609e, /* unit_specifier_id: NCITS/T10 */
+ 0x13010483, /* unit_sw_version: 1155D Rev 4 */
+ 0x3800609e, /* command_set_specifier_id: NCITS/T10 */
+ 0x390104d8, /* command_set: SPC-2 */
+ 0x3b000000, /* command_set_revision: 0 */
+ 0x3c000001, /* firmware_revision: 1 */
+};
+
+static int sbp_update_unit_directory(struct sbp_tport *tport)
+{
+ struct sbp_lun *lun;
+ int num_luns = 0, num_entries, idx = 0, mgt_agt_addr, ret;
+ u32 *data;
+
+ /* unregister existing descriptor */
+ if (tport->unit_directory.data) {
+ fw_core_remove_descriptor(&tport->unit_directory);
+ kfree(tport->unit_directory.data);
+ tport->unit_directory.data = NULL;
+ }
+
+ if (!tport->enable || !tport->tpg)
+ return 0;
+
+ /* count how many LUNs to publish */
+ list_for_each_entry(lun, &tport->tpg->lun_list, link)
+ num_luns++;
+
+ /*
+ * Number of entries in the final unit directory:
+ * - all of those in the template
+ * - management_agent
+ * - unit_characteristics
+ * - reconnect_timeout
+ * - unit unique ID
+ * - one for each LUN
+ *
+ * MUST NOT include leaf or sub-directory entries
+ */
+ num_entries = ARRAY_SIZE(sbp_unit_directory_template) + 4 + num_luns;
+
+ if (tport->directory_id != -1)
+ num_entries++;
+
+ /* allocate num_entries + 4 for the header and unique ID leaf */
+ data = kcalloc((num_entries + 4), sizeof(u32), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* directory_length */
+ data[idx++] = num_entries << 16;
+
+ /* directory_id */
+ if (tport->directory_id != -1)
+ data[idx++] = (CSR_DIRECTORY_ID << 24) | tport->directory_id;
+
+ /* directory template */
+ memcpy(&data[idx], sbp_unit_directory_template,
+ sizeof(sbp_unit_directory_template));
+ idx += ARRAY_SIZE(sbp_unit_directory_template);
+
+ /* management_agent */
+ mgt_agt_addr = (tport->mgt_agt->handler.offset - CSR_REGISTER_BASE) / 4;
+ data[idx++] = 0x54000000 | (mgt_agt_addr & 0x00ffffff);
+
+ /* unit_characteristics */
+ data[idx++] = 0x3a000000 |
+ (((tport->mgt_orb_timeout * 2) << 8) & 0xff00) |
+ SBP_ORB_FETCH_SIZE;
+
+ /* reconnect_timeout */
+ data[idx++] = 0x3d000000 | (tport->max_reconnect_timeout & 0xffff);
+
+ /* unit unique ID (leaf is just after LUNs) */
+ data[idx++] = 0x8d000000 | (num_luns + 1);
+
+ /* LUNs */
+ list_for_each_entry(lun, &tport->tpg->lun_list, link) {
+ struct se_lun *se_lun = lun->se_lun;
+ struct se_device *dev = se_lun->lun_se_dev;
+ int type = dev->transport->get_device_type(dev);
+
+ /* logical_unit_number */
+ data[idx++] = 0x14000000 |
+ ((type << 16) & 0x1f0000) |
+ (se_lun->unpacked_lun & 0xffff);
+ }
+
+ /* unit unique ID leaf */
+ data[idx++] = 2 << 16;
+ data[idx++] = tport->guid >> 32;
+ data[idx++] = tport->guid;
+
+ tport->unit_directory.length = idx;
+ tport->unit_directory.key = (CSR_DIRECTORY | CSR_UNIT) << 24;
+ tport->unit_directory.data = data;
+
+ ret = fw_core_add_descriptor(&tport->unit_directory);
+ if (ret < 0) {
+ kfree(tport->unit_directory.data);
+ tport->unit_directory.data = NULL;
+ }
+
+ return ret;
+}
+
+static ssize_t sbp_parse_wwn(const char *name, u64 *wwn, int strict)
+{
+ const char *cp;
+ char c, nibble;
+ int pos = 0, err;
+
+ *wwn = 0;
+ for (cp = name; cp < &name[SBP_NAMELEN - 1]; cp++) {
+ c = *cp;
+ if (c == '\n' && cp[1] == '\0')
+ continue;
+ if (c == '\0') {
+ err = 2;
+ if (pos != 16)
+ goto fail;
+ return cp - name;
+ }
+ err = 3;
+ if (isdigit(c))
+ nibble = c - '0';
+ else if (isxdigit(c) && (islower(c) || !strict))
+ nibble = tolower(c) - 'a' + 10;
+ else
+ goto fail;
+ *wwn = (*wwn << 4) | nibble;
+ pos++;
+ }
+ err = 4;
+fail:
+ printk(KERN_INFO "err %u len %zu pos %u\n",
+ err, cp - name, pos);
+ return -1;
+}
+
+static ssize_t sbp_format_wwn(char *buf, size_t len, u64 wwn)
+{
+ return snprintf(buf, len, "%016llx", wwn);
+}
+
+static struct se_node_acl *sbp_make_nodeacl(
+ struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
+{
+ struct se_node_acl *se_nacl, *se_nacl_new;
+ struct sbp_nacl *nacl;
+ u64 guid = 0;
+ u32 nexus_depth = 1;
+
+ pr_info("sbp_make_nodeacl: %s\n", name);
+
+ if (sbp_parse_wwn(name, &guid, 1) < 0)
+ return ERR_PTR(-EINVAL);
+
+ se_nacl_new = sbp_alloc_fabric_acl(se_tpg);
+ if (!se_nacl_new)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+ * when converting a NodeACL from demo mode -> explict
+ */
+ se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+ name, nexus_depth);
+ if (IS_ERR(se_nacl)) {
+ sbp_release_fabric_acl(se_tpg, se_nacl_new);
+ return se_nacl;
+ }
+
+ /*
+ * Locate our struct sbp_nacl and set the FC Nport WWPN
+ */
+ nacl = container_of(se_nacl, struct sbp_nacl, se_node_acl);
+ nacl->guid = guid;
+ sbp_format_wwn(nacl->iport_name, SBP_NAMELEN, guid);
+
+ return se_nacl;
+}
+
+static void sbp_drop_nodeacl(struct se_node_acl *se_acl)
+{
+ struct sbp_nacl *nacl =
+ container_of(se_acl, struct sbp_nacl, se_node_acl);
+
+ core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+ kfree(nacl);
+}
+
+static int sbp_post_link_lun(
+ struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_lun *lun;
+
+ pr_info("sbp_post_link_lun: LUN %d\n", se_lun->unpacked_lun);
+
+ lun = kmalloc(sizeof(*lun), GFP_KERNEL);
+ if (!lun)
+ return -ENOMEM;
+
+ lun->se_lun = se_lun;
+ list_add_tail(&lun->link, &tpg->lun_list);
+
+ return sbp_update_unit_directory(tpg->tport);
+}
+
+static void sbp_pre_unlink_lun(
+ struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ struct sbp_lun *lun;
+ int ret;
+
+ pr_info("sbp_pre_unlink_lun: LUN %d\n", se_lun->unpacked_lun);
+
+ list_for_each_entry(lun, &tpg->lun_list, link) {
+ if (lun->se_lun == se_lun)
+ break;
+ }
+
+ BUG_ON(lun == NULL);
+
+ list_del(&lun->link);
+ kfree(lun);
+
+ if (list_empty(&tpg->lun_list))
+ tport->enable = 0;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0)
+ pr_err("unlink LUN: failed to update unit directory\n");
+}
+
+static struct se_portal_group *sbp_make_tpg(
+ struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct sbp_tport *tport =
+ container_of(wwn, struct sbp_tport, tport_wwn);
+
+ struct sbp_tpg *tpg;
+ unsigned long tpgt;
+ int ret;
+
+ if (strstr(name, "tpgt_") != name)
+ return ERR_PTR(-EINVAL);
+ if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
+ return ERR_PTR(-EINVAL);
+
+ if (tport->tpg) {
+ pr_err("Only one TPG per Unit is possible.\n");
+ return ERR_PTR(-EBUSY);
+ }
+
+ tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
+ if (!tpg) {
+ pr_err("Unable to allocate struct sbp_tpg\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ tpg->tport = tport;
+ tpg->tport_tpgt = tpgt;
+ tport->tpg = tpg;
+
+ /* default attribute values */
+ tport->enable = 0;
+ tport->directory_id = -1;
+ tport->mgt_orb_timeout = 15;
+ tport->max_reconnect_timeout = 5;
+ tport->max_logins_per_lun = 1;
+
+ INIT_LIST_HEAD(&tpg->lun_list);
+
+ tport->mgt_agt = sbp_management_agent_register(tport);
+ if (IS_ERR(tport->mgt_agt)) {
+ ret = PTR_ERR(tport->mgt_agt);
+ kfree(tpg);
+ return ERR_PTR(ret);
+ }
+
+ ret = core_tpg_register(&sbp_fabric_configfs->tf_ops, wwn,
+ &tpg->se_tpg, (void *)tpg,
+ TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0) {
+ sbp_management_agent_unregister(tport->mgt_agt);
+ kfree(tpg);
+ return ERR_PTR(ret);
+ }
+
+ return &tpg->se_tpg;
+}
+
+static void sbp_drop_tpg(struct se_portal_group *se_tpg)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+
+ core_tpg_deregister(se_tpg);
+ sbp_management_agent_unregister(tport->mgt_agt);
+ tport->tpg = NULL;
+ kfree(tpg);
+}
+
+static struct se_wwn *sbp_make_tport(
+ struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct sbp_tport *tport;
+ u64 guid = 0;
+
+ pr_info("sbp_make_tport: %s\n", name);
+
+ if (sbp_parse_wwn(name, &guid, 1) < 0)
+ return ERR_PTR(-EINVAL);
+
+ tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+ if (!tport) {
+ pr_err("Unable to allocate struct sbp_tport\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ tport->guid = guid;
+ sbp_format_wwn(tport->tport_name, SBP_NAMELEN, guid);
+
+ return &tport->tport_wwn;
+}
+
+static void sbp_drop_tport(struct se_wwn *wwn)
+{
+ struct sbp_tport *tport =
+ container_of(wwn, struct sbp_tport, tport_wwn);
+
+ kfree(tport);
+}
+
+static ssize_t sbp_wwn_show_attr_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "FireWire SBP fabric module %s\n", SBP_VERSION);
+}
+
+TF_WWN_ATTR_RO(sbp, version);
+
+static struct configfs_attribute *sbp_wwn_attrs[] = {
+ &sbp_wwn_version.attr,
+ NULL,
+};
+
+
+static ssize_t sbp_tpg_show_directory_id(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+
+ if (tport->directory_id == -1)
+ return sprintf(page, "implicit\n");
+ else
+ return sprintf(page, "%06x\n", tport->directory_id);
+}
+
+static ssize_t sbp_tpg_store_directory_id(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+
+ if (tport->enable) {
+ pr_err("Cannot change the directory_id on an active target.\n");
+ return -EBUSY;
+ }
+
+ if (strstr(page, "implicit") == page) {
+ tport->directory_id = -1;
+ } else {
+ if (kstrtoul(page, 16, &val) < 0)
+ return -EINVAL;
+ if (val > 0xffffff)
+ return -EINVAL;
+
+ tport->directory_id = val;
+ }
+
+ return count;
+}
+
+static ssize_t sbp_tpg_show_enable(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->enable);
+}
+
+static ssize_t sbp_tpg_store_enable(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val != 0) && (val != 1))
+ return -EINVAL;
+
+ if (tport->enable == val)
+ return count;
+
+ if (val) {
+ if (list_empty(&tpg->lun_list)) {
+ pr_err("Cannot enable a target with no LUNs!\n");
+ return -EINVAL;
+ }
+ } else {
+ /* FIXME: force-shutdown sessions instead */
+ if (!list_empty(&se_tpg->tpg_sess_list))
+ return -EBUSY;
+ }
+
+ tport->enable = val;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0) {
+ pr_err("Could not update Config ROM\n");
+ return ret;
+ }
+
+ return count;
+}
+
+TF_TPG_BASE_ATTR(sbp, directory_id, S_IRUGO | S_IWUSR);
+TF_TPG_BASE_ATTR(sbp, enable, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_base_attrs[] = {
+ &sbp_tpg_directory_id.attr,
+ &sbp_tpg_enable.attr,
+ NULL,
+};
+
+static ssize_t sbp_tpg_attrib_show_mgt_orb_timeout(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->mgt_orb_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_mgt_orb_timeout(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val < 1) || (val > 127))
+ return -EINVAL;
+
+ if (tport->mgt_orb_timeout == val)
+ return count;
+
+ tport->mgt_orb_timeout = val;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_reconnect_timeout(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->max_reconnect_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_reconnect_timeout(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val < 1) || (val > 32767))
+ return -EINVAL;
+
+ if (tport->max_reconnect_timeout == val)
+ return count;
+
+ tport->max_reconnect_timeout = val;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_logins_per_lun(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->max_logins_per_lun);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_logins_per_lun(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val < 1) || (val > 127))
+ return -EINVAL;
+
+ /* XXX: also check against current count? */
+
+ tport->max_logins_per_lun = val;
+
+ return count;
+}
+
+TF_TPG_ATTRIB_ATTR(sbp, mgt_orb_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_reconnect_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_logins_per_lun, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_attrib_attrs[] = {
+ &sbp_tpg_attrib_mgt_orb_timeout.attr,
+ &sbp_tpg_attrib_max_reconnect_timeout.attr,
+ &sbp_tpg_attrib_max_logins_per_lun.attr,
+ NULL,
+};
+
+static struct target_core_fabric_ops sbp_ops = {
+ .get_fabric_name = sbp_get_fabric_name,
+ .get_fabric_proto_ident = sbp_get_fabric_proto_ident,
+ .tpg_get_wwn = sbp_get_fabric_wwn,
+ .tpg_get_tag = sbp_get_tag,
+ .tpg_get_default_depth = sbp_get_default_depth,
+ .tpg_get_pr_transport_id = sbp_get_pr_transport_id,
+ .tpg_get_pr_transport_id_len = sbp_get_pr_transport_id_len,
+ .tpg_parse_pr_out_transport_id = sbp_parse_pr_out_transport_id,
+ .tpg_check_demo_mode = sbp_check_true,
+ .tpg_check_demo_mode_cache = sbp_check_true,
+ .tpg_check_demo_mode_write_protect = sbp_check_false,
+ .tpg_check_prod_mode_write_protect = sbp_check_false,
+ .tpg_alloc_fabric_acl = sbp_alloc_fabric_acl,
+ .tpg_release_fabric_acl = sbp_release_fabric_acl,
+ .tpg_get_inst_index = sbp_tpg_get_inst_index,
+ .new_cmd_map = sbp_new_cmd,
+ .release_cmd = sbp_release_cmd,
+ .shutdown_session = sbp_shutdown_session,
+ .close_session = sbp_close_session,
+ .stop_session = sbp_stop_session,
+ .fall_back_to_erl0 = sbp_reset_nexus,
+ .sess_logged_in = sbp_sess_logged_in,
+ .sess_get_index = sbp_sess_get_index,
+ .sess_get_initiator_sid = NULL,
+ .write_pending = sbp_write_pending,
+ .write_pending_status = sbp_write_pending_status,
+ .set_default_node_attributes = sbp_set_default_node_attrs,
+ .get_task_tag = sbp_get_task_tag,
+ .get_cmd_state = sbp_get_cmd_state,
+ .queue_data_in = sbp_queue_data_in,
+ .queue_status = sbp_queue_status,
+ .queue_tm_rsp = sbp_queue_tm_rsp,
+ .get_fabric_sense_len = sbp_get_fabric_sense_len,
+ .set_fabric_sense_len = sbp_set_fabric_sense_len,
+ .is_state_remove = sbp_is_state_remove,
+ .check_stop_free = sbp_check_stop_free,
+
+ .fabric_make_wwn = sbp_make_tport,
+ .fabric_drop_wwn = sbp_drop_tport,
+ .fabric_make_tpg = sbp_make_tpg,
+ .fabric_drop_tpg = sbp_drop_tpg,
+ .fabric_post_link = sbp_post_link_lun,
+ .fabric_pre_unlink = sbp_pre_unlink_lun,
+ .fabric_make_np = NULL,
+ .fabric_drop_np = NULL,
+ .fabric_make_nodeacl = sbp_make_nodeacl,
+ .fabric_drop_nodeacl = sbp_drop_nodeacl,
+};
+
+static int sbp_register_configfs(void)
+{
+ struct target_fabric_configfs *fabric;
+ int ret;
+
+ pr_info("FireWire SBP fabric module %s\n", SBP_VERSION);
+
+ /*
+ * Register the top level struct config_item_type with TCM core
+ */
+ fabric = target_fabric_configfs_init(THIS_MODULE, "sbp");
+ if (!fabric) {
+ pr_err("target_fabric_configfs_init() failed\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Setup fabric->tf_ops from our local sbp_ops
+ */
+ fabric->tf_ops = sbp_ops;
+
+ /*
+ * Setup default attribute lists for various fabric->tf_cit_tmpl
+ */
+ TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = sbp_wwn_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = sbp_tpg_base_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = sbp_tpg_attrib_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+
+ /*
+ * Register the fabric for use within TCM
+ */
+ ret = target_fabric_configfs_register(fabric);
+ if (ret < 0) {
+ pr_err("target_fabric_configfs_register() failed for SBP\n");
+ return ret;
+ }
+
+ /*
+ * Setup our local pointer to *fabric
+ */
+ sbp_fabric_configfs = fabric;
+
+ pr_info("SBP[0] - Set fabric -> sbp_fabric_configfs\n");
+ return 0;
+};
+
+static void sbp_deregister_configfs(void)
+{
+ if (!sbp_fabric_configfs)
+ return;
+
+ target_fabric_configfs_deregister(sbp_fabric_configfs);
+ sbp_fabric_configfs = NULL;
+ pr_info("SBP[0] - Cleared sbp_fabric_configfs\n");
+};
+
+static int __init sbp_init(void)
+{
+ int ret;
+
+ ret = sbp_register_configfs();
+ if (ret < 0)
+ return ret;
+
+ return 0;
+};
+
+static void sbp_exit(void)
+{
+ sbp_deregister_configfs();
+};
+
+MODULE_DESCRIPTION("FireWire SBP fabric driver");
+MODULE_LICENSE("GPL");
+module_init(sbp_init);
+module_exit(sbp_exit);
+
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h}
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (4 preceding siblings ...)
2012-02-11 19:44 ` [PATCH 05/13] firewire-sbp-target: Add sbp_configfs.c Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-13 13:06 ` Nicholas A. Bellinger
2012-02-11 19:44 ` [PATCH 07/13] firewire-sbp-target: Add sbp_proto.{c,h} Chris Boot
` (8 subsequent siblings)
14 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This serves as further glue between the target framework and SBP-2, in
this case dealing with SCSI command submission and data in/out.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_fabric.c | 321 +++++++++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_fabric.h | 32 ++++
2 files changed, 353 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_fabric.c
create mode 100644 drivers/target/sbp/sbp_fabric.h
diff --git a/drivers/target/sbp/sbp_fabric.c b/drivers/target/sbp/sbp_fabric.c
new file mode 100644
index 0000000..edc6fda
--- /dev/null
+++ b/drivers/target/sbp/sbp_fabric.c
@@ -0,0 +1,321 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+int sbp_check_true(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+int sbp_check_false(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+char *sbp_get_fabric_name(void)
+{
+ return "sbp";
+}
+
+char *sbp_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+
+ return &tport->tport_name[0];
+}
+
+u16 sbp_get_tag(struct se_portal_group *se_tpg)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ return tpg->tport_tpgt;
+}
+
+u32 sbp_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+ struct sbp_nacl *nacl;
+
+ nacl = kzalloc(sizeof(struct sbp_nacl), GFP_KERNEL);
+ if (!nacl) {
+ pr_err("Unable to alocate struct sbp_nacl\n");
+ return NULL;
+ }
+
+ return &nacl->se_node_acl;
+}
+
+void sbp_release_fabric_acl(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl)
+{
+ struct sbp_nacl *nacl =
+ container_of(se_nacl, struct sbp_nacl, se_node_acl);
+ kfree(nacl);
+}
+
+u32 sbp_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+int sbp_new_cmd(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+ int ret;
+
+ ret = transport_generic_allocate_tasks(se_cmd, req->cmd_buf);
+ if (ret)
+ return ret;
+
+ return transport_generic_map_mem_to_cmd(se_cmd, NULL, 0, NULL, 0);
+}
+
+void sbp_release_cmd(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ sbp_free_request(req);
+}
+
+int sbp_shutdown_session(struct se_session *se_sess)
+{
+ return 0;
+}
+
+void sbp_close_session(struct se_session *se_sess)
+{
+ return;
+}
+
+void sbp_stop_session(struct se_session *se_sess, int sess_sleep,
+ int conn_sleep)
+{
+ return;
+}
+
+void sbp_reset_nexus(struct se_session *se_sess)
+{
+ return;
+}
+
+int sbp_sess_logged_in(struct se_session *se_sess)
+{
+ return 0;
+}
+
+u32 sbp_sess_get_index(struct se_session *se_sess)
+{
+ return 0;
+}
+
+int sbp_write_pending(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+ int ret;
+
+ if (!req->data_len)
+ return -EINVAL;
+
+ if (req->data_dir != DMA_TO_DEVICE) {
+ pr_err("sbp_write_pending: incorrect data direction\n");
+ return -EINVAL;
+ }
+
+ if (req->data_len != se_cmd->data_length) {
+ pr_warn("sbp_write_pending: dodgy data length (%d != %d)\n",
+ req->data_len, se_cmd->data_length);
+ }
+
+ req->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
+ if (!req->data_buf)
+ return -ENOMEM;
+
+ ret = sbp_rw_data(req);
+ if (ret) {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ pr_warn("sbp_write_pending: data write error\n");
+ return ret;
+ }
+
+ sg_copy_from_buffer(se_cmd->t_data_sg,
+ se_cmd->t_data_nents,
+ req->data_buf,
+ se_cmd->data_length);
+
+ transport_generic_process_write(se_cmd);
+
+ return 0;
+}
+
+int sbp_write_pending_status(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+void sbp_set_default_node_attrs(struct se_node_acl *nacl)
+{
+ return;
+}
+
+u32 sbp_get_task_tag(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ /* only used for printk and family? */
+ return (u32)req->orb_pointer;
+}
+
+int sbp_get_cmd_state(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+int sbp_queue_data_in(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+ int ret;
+
+ if (!req->data_len) {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_ILLEGAL_REQUEST) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ pr_err("sbp_queue_data_in: no initiator data buffers\n");
+ return 0;
+ }
+
+ if (req->data_dir != DMA_FROM_DEVICE) {
+ pr_err("sbp_queue_data_in: incorrect data direction\n");
+ return -EINVAL;
+ }
+
+ if (req->data_len != se_cmd->data_length) {
+ pr_warn("sbp_write_pending: dodgy data length (%d != %d)\n",
+ req->data_len, se_cmd->data_length);
+ }
+
+ req->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
+ if (!req->data_buf)
+ return -ENOMEM;
+
+ sg_copy_to_buffer(se_cmd->t_data_sg,
+ se_cmd->t_data_nents,
+ req->data_buf,
+ se_cmd->data_length);
+
+ ret = sbp_rw_data(req);
+ if (ret) {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ return ret;
+ }
+
+ return sbp_send_sense(req);
+}
+
+/*
+ * Called after command (no data transfer) or after the write (to device)
+ * operation is completed
+ */
+int sbp_queue_status(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ return sbp_send_sense(req);
+}
+
+int sbp_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+u16 sbp_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length)
+{
+ return 0;
+}
+
+u16 sbp_get_fabric_sense_len(void)
+{
+ return 0;
+}
+
+int sbp_is_state_remove(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+int sbp_check_stop_free(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ transport_generic_free_cmd(&req->se_cmd, 0);
+ return 1;
+}
+
diff --git a/drivers/target/sbp/sbp_fabric.h b/drivers/target/sbp/sbp_fabric.h
new file mode 100644
index 0000000..2a600f1c
--- /dev/null
+++ b/drivers/target/sbp/sbp_fabric.h
@@ -0,0 +1,32 @@
+
+int sbp_check_true(struct se_portal_group *);
+int sbp_check_false(struct se_portal_group *);
+char *sbp_get_fabric_name(void);
+char *sbp_get_fabric_wwn(struct se_portal_group *);
+u16 sbp_get_tag(struct se_portal_group *);
+u32 sbp_get_default_depth(struct se_portal_group *);
+struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *);
+void sbp_release_fabric_acl(struct se_portal_group *,
+ struct se_node_acl *);
+u32 sbp_tpg_get_inst_index(struct se_portal_group *);
+int sbp_new_cmd(struct se_cmd *se_cmd);
+void sbp_release_cmd(struct se_cmd *se_cmd);
+int sbp_shutdown_session(struct se_session *);
+void sbp_close_session(struct se_session *);
+void sbp_stop_session(struct se_session *, int, int);
+void sbp_reset_nexus(struct se_session *);
+int sbp_sess_logged_in(struct se_session *);
+u32 sbp_sess_get_index(struct se_session *);
+int sbp_write_pending(struct se_cmd *);
+int sbp_write_pending_status(struct se_cmd *);
+void sbp_set_default_node_attrs(struct se_node_acl *);
+u32 sbp_get_task_tag(struct se_cmd *);
+int sbp_get_cmd_state(struct se_cmd *);
+int sbp_queue_data_in(struct se_cmd *);
+int sbp_queue_status(struct se_cmd *);
+int sbp_queue_tm_rsp(struct se_cmd *);
+u16 sbp_set_fabric_sense_len(struct se_cmd *, u32);
+u16 sbp_get_fabric_sense_len(void);
+int sbp_is_state_remove(struct se_cmd *);
+int sbp_check_stop_free(struct se_cmd *se_cmd);
+
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h}
2012-02-11 19:44 ` [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h} Chris Boot
@ 2012-02-13 13:06 ` Nicholas A. Bellinger
[not found] ` <337FFBD7-6B4A-41CA-BB57-6038C935B5BF@bootc.net>
0 siblings, 1 reply; 104+ messages in thread
From: Nicholas A. Bellinger @ 2012-02-13 13:06 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, stefanr
On Sat, 2012-02-11 at 19:44 +0000, Chris Boot wrote:
> This serves as further glue between the target framework and SBP-2, in
> this case dealing with SCSI command submission and data in/out.
>
> Signed-off-by: Chris Boot <bootc@bootc.net>
> Cc: Andy Grover <agrover@redhat.com>
> Cc: Clemens Ladisch <clemens@ladisch.de>
> Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
> Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
> ---
> drivers/target/sbp/sbp_fabric.c | 321 +++++++++++++++++++++++++++++++++++++++
> drivers/target/sbp/sbp_fabric.h | 32 ++++
> 2 files changed, 353 insertions(+), 0 deletions(-)
> create mode 100644 drivers/target/sbp/sbp_fabric.c
> create mode 100644 drivers/target/sbp/sbp_fabric.h
>
> diff --git a/drivers/target/sbp/sbp_fabric.c b/drivers/target/sbp/sbp_fabric.c
> new file mode 100644
> index 0000000..edc6fda
> --- /dev/null
> +++ b/drivers/target/sbp/sbp_fabric.c
> @@ -0,0 +1,321 @@
> +/*
> + * SBP2 target driver (SCSI over IEEE1394 in target mode)
> + *
> + * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software Foundation,
> + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + */
> +
<SNIP>
> +int sbp_new_cmd(struct se_cmd *se_cmd)
> +{
> + struct sbp_target_request *req = container_of(se_cmd,
> + struct sbp_target_request, se_cmd);
> + int ret;
> +
> + ret = transport_generic_allocate_tasks(se_cmd, req->cmd_buf);
> + if (ret)
> + return ret;
> +
> + return transport_generic_map_mem_to_cmd(se_cmd, NULL, 0, NULL, 0);
> +}
> +
Because sbp_scsi_cmnd.c:sbp_handle_command() is using the new
target_submit_cmd() logic, target-core will not be calling
TFO->new_cmd_map() -> sbp_new_cmd() for setup here.
Go ahead and drop this now.
> +
> +u32 sbp_get_task_tag(struct se_cmd *se_cmd)
> +{
> + struct sbp_target_request *req = container_of(se_cmd,
> + struct sbp_target_request, se_cmd);
> +
> + /* only used for printk and family? */
> + return (u32)req->orb_pointer;
> +}
> +
So an the ABORT_TASK TMR patches use TFO->get_task_tag() to locate a
referenced tag to locate the se_cmd descriptor. Since we don't
support TMRs yet in sbp, this value will only be used for informational
purposes.
> +
> +int sbp_queue_data_in(struct se_cmd *se_cmd)
> +{
> + struct sbp_target_request *req = container_of(se_cmd,
> + struct sbp_target_request, se_cmd);
> + int ret;
> +
> + if (!req->data_len) {
> + req->status.status |= cpu_to_be32(
> + STATUS_BLOCK_RESP(STATUS_RESP_ILLEGAL_REQUEST) |
> + STATUS_BLOCK_DEAD(0) |
> + STATUS_BLOCK_LEN(1) |
> + STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
> + sbp_send_status(req);
> + pr_err("sbp_queue_data_in: no initiator data buffers\n");
> + return 0;
> + }
> +
> + if (req->data_dir != DMA_FROM_DEVICE) {
> + pr_err("sbp_queue_data_in: incorrect data direction\n");
> + return -EINVAL;
> + }
> +
> + if (req->data_len != se_cmd->data_length) {
> + pr_warn("sbp_write_pending: dodgy data length (%d != %d)\n",
> + req->data_len, se_cmd->data_length);
> + }
> +
> + req->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
> + if (!req->data_buf)
> + return -ENOMEM;
> +
> + sg_copy_to_buffer(se_cmd->t_data_sg,
> + se_cmd->t_data_nents,
> + req->data_buf,
> + se_cmd->data_length);
> +
> + ret = sbp_rw_data(req);
> + if (ret) {
> + req->status.status |= cpu_to_be32(
> + STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
> + STATUS_BLOCK_DEAD(0) |
> + STATUS_BLOCK_LEN(1) |
> + STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
> + sbp_send_status(req);
> + return ret;
> + }
> +
> + return sbp_send_sense(req);
> +}
> +
> +/*
> + * Called after command (no data transfer) or after the write (to device)
> + * operation is completed
> + */
> +int sbp_queue_status(struct se_cmd *se_cmd)
> +{
> + struct sbp_target_request *req = container_of(se_cmd,
> + struct sbp_target_request, se_cmd);
> +
> + return sbp_send_sense(req);
> +}
> +
Thanks!
--nab
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH 07/13] firewire-sbp-target: Add sbp_proto.{c,h}
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (5 preceding siblings ...)
2012-02-11 19:44 ` [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h} Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-11 19:44 ` [PATCH 08/13] firewire-sbp-target: add sbp_management_agent.{c,h} Chris Boot
` (7 subsequent siblings)
14 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
These are functions to generate TransportID identifiers as per SPC-4
revision 17.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_proto.c | 113 ++++++++++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_proto.h | 12 ++++
2 files changed, 125 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_proto.c
create mode 100644 drivers/target/sbp/sbp_proto.h
diff --git a/drivers/target/sbp/sbp_proto.c b/drivers/target/sbp/sbp_proto.c
new file mode 100644
index 0000000..5744b5c
--- /dev/null
+++ b/drivers/target/sbp/sbp_proto.c
@@ -0,0 +1,113 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <scsi/scsi.h>
+#include <target/target_core_base.h>
+
+#include "sbp_proto.h"
+
+/*
+ * Handlers for Serial Attached SCSI (SBP)
+ */
+u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+ /*
+ * Return a IEEE 1394 SCSI Protocol identifier for loopback operations
+ * This is defined in section 7.5.1 Table 362 in spc4r17
+ */
+ return SCSI_PROTOCOL_SBP;
+}
+EXPORT_SYMBOL(sbp_get_fabric_proto_ident);
+
+u32 sbp_get_pr_transport_id(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code,
+ unsigned char *buf)
+{
+ int ret;
+
+ /*
+ * Set PROTOCOL IDENTIFIER to 3h for SBP
+ */
+ buf[0] = SCSI_PROTOCOL_SBP;
+ /*
+ * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+ * over IEEE 1394
+ */
+ ret = hex2bin(&buf[8], se_nacl->initiatorname, 8);
+ if (ret < 0)
+ pr_debug("sbp transport_id: invalid hex string\n");
+
+ /*
+ * The IEEE 1394 Transport ID is a hardcoded 24-byte length
+ */
+ return 24;
+}
+EXPORT_SYMBOL(sbp_get_pr_transport_id);
+
+u32 sbp_get_pr_transport_id_len(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code)
+{
+ *format_code = 0;
+ /*
+ * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+ * over IEEE 1394
+ *
+ * The SBP Transport ID is a hardcoded 24-byte length
+ */
+ return 24;
+}
+EXPORT_SYMBOL(sbp_get_pr_transport_id_len);
+
+/*
+ * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above
+ * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations.
+ */
+char *sbp_parse_pr_out_transport_id(
+ struct se_portal_group *se_tpg,
+ const char *buf,
+ u32 *out_tid_len,
+ char **port_nexus_ptr)
+{
+ /*
+ * Assume the FORMAT CODE 00b from spc4r17, 7.5.4.4 TransportID
+ * for initiator ports using SCSI over SBP Serial SCSI Protocol
+ *
+ * The TransportID for a IEEE 1394 Initiator Port is of fixed size of
+ * 24 bytes, and IEEE 1394 does not contain a I_T nexus identifier,
+ * so we return the **port_nexus_ptr set to NULL.
+ */
+ *port_nexus_ptr = NULL;
+ *out_tid_len = 24;
+
+ return (char *)&buf[8];
+}
+EXPORT_SYMBOL(sbp_parse_pr_out_transport_id);
+
diff --git a/drivers/target/sbp/sbp_proto.h b/drivers/target/sbp/sbp_proto.h
new file mode 100644
index 0000000..1bd3e16
--- /dev/null
+++ b/drivers/target/sbp/sbp_proto.h
@@ -0,0 +1,12 @@
+
+u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg);
+u32 sbp_get_pr_transport_id(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl, struct t10_pr_registration *pr_reg,
+ int *format_code, unsigned char *buf);
+u32 sbp_get_pr_transport_id_len(
+ struct se_portal_group *se_tpg, struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg, int *format_code);
+char *sbp_parse_pr_out_transport_id(
+ struct se_portal_group *se_tpg, const char *buf,
+ u32 *out_tid_len, char **port_nexus_ptr);
+
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 08/13] firewire-sbp-target: add sbp_management_agent.{c,h}
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (6 preceding siblings ...)
2012-02-11 19:44 ` [PATCH 07/13] firewire-sbp-target: Add sbp_proto.{c,h} Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-11 19:44 ` [PATCH 09/13] firewire-sbp-target: Add sbp_login.{c,h} Chris Boot
` (6 subsequent siblings)
14 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This code implements the SBP-2 Management Agent. This is the first of
two firewire address handlers that are used to communicate with the
target. The Management Agent is used to handle login, reconnect and
logout to a SCSI LUN as well as task management functions.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_management_agent.c | 266 +++++++++++++++++++++++++++++
drivers/target/sbp/sbp_management_agent.h | 23 +++
2 files changed, 289 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_management_agent.c
create mode 100644 drivers/target/sbp/sbp_management_agent.h
diff --git a/drivers/target/sbp/sbp_management_agent.c b/drivers/target/sbp/sbp_management_agent.c
new file mode 100644
index 0000000..d9a2124
--- /dev/null
+++ b/drivers/target/sbp/sbp_management_agent.c
@@ -0,0 +1,266 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/kref.h>
+
+#include <target/target_core_base.h>
+
+#include "../../firewire/core.h"
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_util.h"
+
+static void sbp_mgt_agent_process(struct work_struct *work)
+{
+ struct sbp_management_agent *agent =
+ container_of(work, struct sbp_management_agent, work);
+ struct sbp_management_request *req = agent->request;
+ int ret;
+ int status_data_len = 0;
+
+ /* fetch the ORB from the initiator */
+ ret = fw_run_transaction(req->card, TCODE_READ_BLOCK_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ agent->orb_offset, &req->orb, sizeof(req->orb));
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("mgt_orb fetch failed: %x\n", ret);
+ goto out;
+ }
+
+ pr_debug("mgt_orb ptr1:0x%llx ptr2:0x%llx misc:0x%x len:0x%x "
+ "status_fifo:0x%llx\n",
+ sbp2_pointer_to_addr(&req->orb.ptr1),
+ sbp2_pointer_to_addr(&req->orb.ptr2),
+ be32_to_cpu(req->orb.misc), be32_to_cpu(req->orb.length),
+ sbp2_pointer_to_addr(&req->orb.status_fifo));
+
+ /* sanity check basic fields */
+ if (!ORB_NOTIFY(be32_to_cpu(req->orb.misc)) ||
+ ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc)) != 0) {
+ pr_err("mgt_orb bad request\n");
+ goto out;
+ }
+
+ switch (MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc))) {
+ case MANAGEMENT_ORB_FUNCTION_LOGIN:
+ sbp_management_request_login(agent, req, &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS:
+ sbp_management_request_query_logins(agent, req,
+ &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_RECONNECT:
+ sbp_management_request_reconnect(agent, req, &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_SET_PASSWORD:
+ pr_notice("SET PASSWORD not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_LOGOUT:
+ sbp_management_request_logout(agent, req, &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_ABORT_TASK:
+ pr_notice("ABORT TASK not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET:
+ pr_notice("ABORT TASK SET not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET:
+ pr_notice("LOGICAL UNIT RESET not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_TARGET_RESET:
+ pr_notice("TARGET RESET not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ default:
+ pr_notice("unknown management function 0x%x\n",
+ MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc)));
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+ }
+
+ /* set up the status block we'll send to the initiator */
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_SRC(1) | /* Response to ORB, next_ORB absent */
+ STATUS_BLOCK_LEN(DIV_ROUND_UP(status_data_len, 4) + 1) |
+ STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_offset >> 32));
+ req->status.orb_low = cpu_to_be32(agent->orb_offset);
+
+ /* write the status block back to the initiator */
+ ret = fw_run_transaction(req->card, TCODE_WRITE_BLOCK_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ sbp2_pointer_to_addr(&req->orb.status_fifo),
+ &req->status, 8 + status_data_len);
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("mgt_orb status write failed: %x\n", ret);
+ goto out;
+ }
+
+out:
+ fw_card_put(req->card);
+ kfree(req);
+ atomic_set(&agent->state, MANAGEMENT_AGENT_STATE_IDLE);
+}
+
+static void sbp_mgt_agent_rw(struct fw_card *card,
+ struct fw_request *request, int tcode, int destination, int source,
+ int generation, unsigned long long offset, void *data, size_t length,
+ void *callback_data)
+{
+ struct sbp_management_agent *agent = callback_data;
+ struct sbp2_pointer *ptr = data;
+
+ if (!agent->tport->enable) {
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ return;
+ }
+
+ if ((offset != agent->handler.offset) || (length != 8)) {
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ return;
+ }
+
+ if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+ struct sbp_management_request *req;
+ int ret;
+
+ smp_wmb();
+ if (atomic_cmpxchg(&agent->state,
+ MANAGEMENT_AGENT_STATE_IDLE,
+ MANAGEMENT_AGENT_STATE_BUSY) !=
+ MANAGEMENT_AGENT_STATE_IDLE) {
+ pr_notice("ignoring management request while busy\n");
+
+ fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+ return;
+ }
+
+ req = kzalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+ return;
+ }
+
+ req->card = fw_card_get(card);
+ req->generation = generation;
+ req->node_addr = source;
+ req->speed = fw_get_request_speed(request);
+
+ agent->orb_offset = sbp2_pointer_to_addr(ptr);
+ agent->request = req;
+
+ ret = queue_work(fw_workqueue, &agent->work);
+ if (!ret) {
+ /* pretend we're busy */
+ kfree(req);
+ fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+ return;
+ }
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+ } else if (tcode == TCODE_READ_BLOCK_REQUEST) {
+ addr_to_sbp2_pointer(agent->orb_offset, ptr);
+ fw_send_response(card, request, RCODE_COMPLETE);
+ } else {
+ fw_send_response(card, request, RCODE_TYPE_ERROR);
+ }
+}
+
+struct sbp_management_agent *sbp_management_agent_register(
+ struct sbp_tport *tport)
+{
+ int ret;
+ struct sbp_management_agent *agent;
+
+ agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+ if (!agent)
+ return ERR_PTR(-ENOMEM);
+
+ agent->tport = tport;
+ agent->handler.length = 0x08;
+ agent->handler.address_callback = sbp_mgt_agent_rw;
+ agent->handler.callback_data = agent;
+ atomic_set(&agent->state, MANAGEMENT_AGENT_STATE_IDLE);
+ INIT_WORK(&agent->work, sbp_mgt_agent_process);
+ agent->orb_offset = 0;
+ agent->request = NULL;
+
+ ret = fw_core_add_address_handler(&agent->handler,
+ &sbp_register_region);
+ if (ret < 0) {
+ kfree(agent);
+ return ERR_PTR(ret);
+ }
+
+ return agent;
+}
+
+void sbp_management_agent_unregister(struct sbp_management_agent *agent)
+{
+ if (atomic_read(&agent->state) != MANAGEMENT_AGENT_STATE_IDLE)
+ flush_work_sync(&agent->work);
+
+ fw_core_remove_address_handler(&agent->handler);
+ kfree(agent);
+}
+
diff --git a/drivers/target/sbp/sbp_management_agent.h b/drivers/target/sbp/sbp_management_agent.h
new file mode 100644
index 0000000..615b90e
--- /dev/null
+++ b/drivers/target/sbp/sbp_management_agent.h
@@ -0,0 +1,23 @@
+
+struct sbp_management_agent {
+ struct sbp_tport *tport;
+ struct fw_address_handler handler;
+ atomic_t state;
+ struct work_struct work;
+ u64 orb_offset;
+ struct sbp_management_request *request;
+};
+
+struct sbp_management_request {
+ struct sbp_management_orb orb;
+ struct sbp_status_block status;
+ struct fw_card *card;
+ int generation;
+ int node_addr;
+ int speed;
+};
+
+struct sbp_management_agent *sbp_management_agent_register(
+ struct sbp_tport *tport);
+void sbp_management_agent_unregister(struct sbp_management_agent *agent);
+
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 09/13] firewire-sbp-target: Add sbp_login.{c,h}
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (7 preceding siblings ...)
2012-02-11 19:44 ` [PATCH 08/13] firewire-sbp-target: add sbp_management_agent.{c,h} Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-11 19:44 ` [PATCH 10/13] firewire-sbp-target: Add sbp_target_agent.{c,h} Chris Boot
` (5 subsequent siblings)
14 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This file contains the implementation of the login, reconnect and logout
management ORBs in SBP-2.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_login.c | 637 ++++++++++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_login.h | 14 +
2 files changed, 651 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_login.c
create mode 100644 drivers/target/sbp/sbp_login.h
diff --git a/drivers/target/sbp/sbp_login.c b/drivers/target/sbp/sbp_login.c
new file mode 100644
index 0000000..fb1accf
--- /dev/null
+++ b/drivers/target/sbp/sbp_login.c
@@ -0,0 +1,637 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kref.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "../../firewire/core.h"
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+#include "sbp_util.h"
+
+#define SESSION_MAINTENANCE_INTERVAL HZ
+
+static atomic_t login_id = ATOMIC_INIT(0);
+
+static void session_maintenance_work(struct work_struct *work);
+
+static int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
+{
+ int ret;
+ __be32 high, low;
+
+ ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
+ &high, sizeof(high));
+ if (ret != RCODE_COMPLETE)
+ return ret;
+
+ ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
+ &low, sizeof(low));
+ if (ret != RCODE_COMPLETE)
+ return ret;
+
+ *guid = (u64)be32_to_cpu(high) << 32 | be32_to_cpu(low);
+
+ return RCODE_COMPLETE;
+}
+
+static struct sbp_session *sbp_session_find_by_guid(
+ struct sbp_tpg *tpg, u64 guid)
+{
+ struct se_session *se_sess;
+
+ list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+ struct sbp_session *sess = se_sess->fabric_sess_ptr;
+ if (sess->guid == guid)
+ return sess;
+ }
+
+ return NULL;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_lun(
+ struct sbp_session *session, struct se_lun *lun)
+{
+ struct sbp_login_descriptor *login;
+
+ list_for_each_entry(login, &session->login_list, link) {
+ if (login->lun == lun)
+ return login;
+ }
+
+ return NULL;
+}
+
+static int sbp_login_count_all_by_lun(
+ struct sbp_tpg *tpg,
+ struct se_lun *lun,
+ int exclusive)
+{
+ struct se_session *se_sess;
+ int count = 0;
+
+ list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+ struct sbp_session *sess = se_sess->fabric_sess_ptr;
+ struct sbp_login_descriptor *login;
+
+ list_for_each_entry(login, &sess->login_list, link) {
+ if (login->lun != lun)
+ continue;
+
+ if (!exclusive) {
+ count++;
+ continue;
+ }
+
+ if (login->exclusive)
+ count++;
+ }
+ }
+
+ return count;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_id(
+ struct sbp_tpg *tpg, int login_id)
+{
+ struct se_session *se_sess;
+
+ list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+ struct sbp_session *sess = se_sess->fabric_sess_ptr;
+ struct sbp_login_descriptor *login;
+
+ list_for_each_entry(login, &sess->login_list, link) {
+ if (login->login_id == login_id)
+ return login;
+ }
+ }
+
+ return NULL;
+}
+
+static struct se_lun *sbp_get_lun_from_tpg(struct sbp_tpg *tpg, int lun)
+{
+ struct se_portal_group *se_tpg = &tpg->se_tpg;
+ struct se_lun *se_lun;
+
+ if (lun >= TRANSPORT_MAX_LUNS_PER_TPG)
+ return ERR_PTR(-ENODEV);
+
+ spin_lock(&se_tpg->tpg_lun_lock);
+ se_lun = &se_tpg->tpg_lun_list[lun];
+
+ if (se_lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE)
+ se_lun = ERR_PTR(-EINVAL);
+
+ spin_unlock(&se_tpg->tpg_lun_lock);
+
+ return se_lun;
+}
+
+static struct sbp_session *sbp_session_create(
+ struct sbp_tpg *tpg,
+ u64 guid)
+{
+ struct sbp_session *sess;
+ int ret;
+ char guid_str[17];
+ struct se_node_acl *se_nacl;
+
+ sess = kmalloc(sizeof(*sess), GFP_KERNEL);
+ if (!sess) {
+ pr_err("failed to allocate session descriptor\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sess->se_sess = transport_init_session();
+ if (IS_ERR(sess->se_sess)) {
+ pr_err("failed to init se_session\n");
+
+ ret = PTR_ERR(sess->se_sess);
+ kfree(sess);
+ return ERR_PTR(ret);
+ }
+
+ snprintf(guid_str, sizeof(guid_str), "%016llx", guid);
+
+ se_nacl = core_tpg_check_initiator_node_acl(&tpg->se_tpg, guid_str);
+ if (!se_nacl) {
+ pr_warn("Node ACL not found for %s\n", guid_str);
+
+ transport_free_session(sess->se_sess);
+ kfree(sess);
+
+ return ERR_PTR(-EPERM);
+ }
+
+ INIT_LIST_HEAD(&sess->login_list);
+ sess->se_sess->se_node_acl = se_nacl;
+ INIT_DELAYED_WORK(&sess->maint_work, session_maintenance_work);
+ sess->guid = guid;
+
+ transport_register_session(&tpg->se_tpg, se_nacl, sess->se_sess, sess);
+
+ return sess;
+}
+
+static void sbp_session_release(struct sbp_session *sess, bool cancel_work)
+{
+ if (!list_empty(&sess->login_list))
+ return;
+
+ transport_deregister_session_configfs(sess->se_sess);
+ transport_deregister_session(sess->se_sess);
+
+ if (sess->card)
+ fw_card_put(sess->card);
+
+ if (cancel_work)
+ cancel_delayed_work_sync(&sess->maint_work);
+
+ kfree(sess);
+}
+
+static void sbp_login_release(struct sbp_login_descriptor *login,
+ bool cancel_work)
+{
+ /* FIXME: abort/wait on tasks */
+
+ list_del(&login->link);
+ sbp_target_agent_unregister(login->tgt_agt);
+ sbp_session_release(login->sess, cancel_work);
+ kfree(login);
+}
+
+void sbp_management_request_login(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ struct sbp_tport *tport = agent->tport;
+ struct sbp_tpg *tpg = tport->tpg;
+ struct se_lun *lun;
+ int ret;
+ u64 guid;
+ struct sbp_session *sess;
+ struct sbp_login_descriptor *login;
+ struct sbp_login_response_block *response;
+ int login_response_len;
+
+ /* find the LUN we want to login to */
+ lun = sbp_get_lun_from_tpg(tpg,
+ LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+ if (IS_ERR(lun)) {
+ pr_notice("login to unknown LUN: %d\n",
+ LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LUN_NOTSUPP));
+ return;
+ }
+
+ /* read the peer's GUID */
+ ret = read_peer_guid(&guid, req);
+ if (ret != RCODE_COMPLETE) {
+ pr_warn("failed to read peer GUID: %d\n", ret);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ return;
+ }
+
+ pr_notice("mgt_agent LOGIN to LUN %d from %016llx\n",
+ lun->unpacked_lun, guid);
+
+ /* locate an existing session if there is one */
+ sess = sbp_session_find_by_guid(tpg, guid);
+
+ /*
+ * check for any existing logins by comparing GUIDs
+ * reject with access_denied if present
+ */
+ if (sess) {
+ login = sbp_login_find_by_lun(sess, lun);
+ if (login) {
+ pr_notice("initiator already logged-in\n");
+
+ /*
+ * SBP-2 R4 says we should return access denied, but
+ * that can confuse initiators. Instead we need to
+ * treat this like a reconnect, but send the login
+ * response block like a fresh login.
+ */
+
+ goto already_logged_in;
+ }
+ }
+
+ /*
+ * check exclusive bit in login request
+ * reject with access_denied if any logins present
+ */
+ if (LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc)) &&
+ sbp_login_count_all_by_lun(tpg, lun, 0)) {
+ pr_warn("refusing exclusive login with other active logins\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ /*
+ * check exclusive bit in any existing login descriptor
+ * reject with access_denied if any exclusive logins present
+ */
+ if (sbp_login_count_all_by_lun(tpg, lun, 1)) {
+ pr_warn("refusing login while another exclusive login present\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ /*
+ * check we haven't exceeded the number of allowed logins
+ * reject with resources_unavailable if we have
+ */
+ if (sbp_login_count_all_by_lun(tpg, lun, 0) >=
+ tport->max_logins_per_lun) {
+ pr_warn("max number of logins reached\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ if (!sess) {
+ sess = sbp_session_create(tpg, guid);
+ if (IS_ERR(sess)) {
+ switch (PTR_ERR(sess)) {
+ case -EPERM:
+ ret = SBP_STATUS_ACCESS_DENIED;
+ break;
+ default:
+ ret = SBP_STATUS_RESOURCES_UNAVAIL;
+ break;
+ }
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(
+ STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(ret));
+ return;
+ }
+
+ sess->node_id = req->node_addr;
+ sess->card = fw_card_get(req->card);
+ sess->generation = req->generation;
+ sess->speed = req->speed;
+
+ queue_delayed_work(fw_workqueue, &sess->maint_work,
+ SESSION_MAINTENANCE_INTERVAL);
+ }
+
+ /* only take the latest reconnect_hold into account */
+ sess->reconnect_hold = min(
+ 1 << LOGIN_ORB_RECONNECT(be32_to_cpu(req->orb.misc)),
+ tport->max_reconnect_timeout) - 1;
+
+ /* create new login descriptor */
+ login = kmalloc(sizeof(*login), GFP_KERNEL);
+ if (!login) {
+ pr_err("failed to allocate login descriptor\n");
+
+ sbp_session_release(sess, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ login->sess = sess;
+ login->lun = lun;
+ login->status_fifo_addr = sbp2_pointer_to_addr(&req->orb.status_fifo);
+ login->exclusive = LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc));
+ login->login_id = atomic_inc_return(&login_id);
+ atomic_set(&login->unsolicited_status_enable, 0);
+
+ /* set up address handler */
+ login->tgt_agt = sbp_target_agent_register(login);
+ if (IS_ERR(login->tgt_agt)) {
+ ret = PTR_ERR(login->tgt_agt);
+ pr_err("failed to map command block handler: %d\n", ret);
+
+ sbp_session_release(sess, true);
+ kfree(login);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ /* add to logins list */
+ list_add_tail(&login->link, &sess->login_list);
+
+already_logged_in:
+ /* send login response */
+ response = kzalloc(sizeof(*response), GFP_KERNEL);
+ if (!response) {
+ pr_err("failed to allocate login response block\n");
+
+ sbp_login_release(login, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ login_response_len = max(12, min((int)sizeof(response),
+ (int)LOGIN_ORB_RESPONSE_LENGTH(be32_to_cpu(req->orb.length))));
+ response->misc = cpu_to_be32(
+ ((login_response_len & 0xffff) << 16) |
+ (login->login_id & 0xffff));
+ response->reconnect_hold = cpu_to_be32(sess->reconnect_hold & 0xffff);
+ addr_to_sbp2_pointer(login->tgt_agt->handler.offset,
+ &response->command_block_agent);
+
+ ret = fw_run_transaction(sess->card, TCODE_WRITE_BLOCK_REQUEST,
+ sess->node_id, sess->generation, sess->speed,
+ sbp2_pointer_to_addr(&req->orb.ptr2), response,
+ login_response_len);
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("failed to write login response block: %x\n", ret);
+
+ kfree(response);
+ sbp_login_release(login, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ return;
+ }
+
+ kfree(response);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+void sbp_management_request_query_logins(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ pr_notice("QUERY LOGINS not implemented\n");
+ /* FIXME: implement */
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+}
+
+void sbp_management_request_reconnect(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ struct sbp_tport *tport = agent->tport;
+ struct sbp_tpg *tpg = tport->tpg;
+ int ret;
+ u64 guid;
+ struct sbp_login_descriptor *login;
+
+ /* read the peer's GUID */
+ ret = read_peer_guid(&guid, req);
+ if (ret != RCODE_COMPLETE) {
+ pr_warn("failed to read peer GUID: %d\n", ret);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ return;
+ }
+
+ pr_notice("mgt_agent RECONNECT from %016llx\n", guid);
+
+ /* find the login */
+ login = sbp_login_find_by_id(tpg,
+ RECONNECT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc)));
+
+ if (!login) {
+ pr_err("mgt_agent RECONNECT unknown login ID\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ if (login->sess->guid != guid) {
+ pr_err("mgt_agent RECONNECT login GUID doesn't match\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ if (login->sess->card)
+ fw_card_put(login->sess->card);
+
+ /* update the node details */
+ login->sess->generation = req->generation;
+ login->sess->node_id = req->node_addr;
+ login->sess->card = fw_card_get(req->card);
+ login->sess->speed = req->speed;
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+void sbp_management_request_logout(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ struct sbp_tport *tport = agent->tport;
+ struct sbp_tpg *tpg = tport->tpg;
+ int login_id;
+ struct sbp_login_descriptor *login;
+
+ login_id = LOGOUT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc));
+
+ /* Find login by ID */
+ login = sbp_login_find_by_id(tpg, login_id);
+ if (!login) {
+ pr_warn("cannot find login: %d\n", login_id);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LOGIN_ID_UNKNOWN));
+ return;
+ }
+
+ pr_info("mgt_agent LOGOUT from LUN %d session %d\n",
+ login->lun->unpacked_lun, login->login_id);
+
+ /* Check source against login's node_id */
+ if (req->node_addr != login->sess->node_id) {
+ pr_warn("logout from different node ID\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ /* Perform logout */
+ sbp_login_release(login, true);
+
+ pr_info("logout successful!\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static void session_check_for_reset(struct sbp_session *sess)
+{
+ bool card_valid = false;
+
+ if (sess->card) {
+ spin_lock_irq(&sess->card->lock);
+ card_valid = (sess->card->local_node != NULL);
+ spin_unlock_irq(&sess->card->lock);
+
+ if (!card_valid) {
+ fw_card_put(sess->card);
+ sess->card = NULL;
+ }
+ }
+
+ if (!card_valid || (sess->generation != sess->card->generation)) {
+ pr_info("Waiting for reconnect from node: %016llx\n",
+ sess->guid);
+
+ sess->node_id = -1;
+ sess->reconnect_expires = get_jiffies_64() +
+ ((sess->reconnect_hold + 1) * HZ);
+ }
+}
+
+static void session_reconnect_expired(struct sbp_session *sess)
+{
+ struct sbp_login_descriptor *login, *temp;
+
+ pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
+
+ list_for_each_entry_safe(login, temp, &sess->login_list, link) {
+ sbp_login_release(login, false);
+ }
+
+ /* sbp_login_release() calls sbp_session_release() */
+}
+
+static void session_maintenance_work(struct work_struct *work)
+{
+ struct sbp_session *sess = container_of(work, struct sbp_session,
+ maint_work.work);
+
+ /* could be called while tearing down the session */
+ if (list_empty(&sess->login_list))
+ return;
+
+ if (sess->node_id != -1) {
+ /* check for bus reset and make node_id invalid */
+ session_check_for_reset(sess);
+
+ queue_delayed_work(fw_workqueue, &sess->maint_work,
+ SESSION_MAINTENANCE_INTERVAL);
+ } else if (!time_after64(get_jiffies_64(), sess->reconnect_expires)) {
+ /* still waiting for reconnect */
+ queue_delayed_work(fw_workqueue, &sess->maint_work,
+ SESSION_MAINTENANCE_INTERVAL);
+ } else {
+ /* reconnect timeout has expired */
+ session_reconnect_expired(sess);
+ }
+}
+
diff --git a/drivers/target/sbp/sbp_login.h b/drivers/target/sbp/sbp_login.h
new file mode 100644
index 0000000..4c86dcb
--- /dev/null
+++ b/drivers/target/sbp/sbp_login.h
@@ -0,0 +1,14 @@
+
+extern void sbp_management_request_login(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size);
+extern void sbp_management_request_query_logins(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size);
+extern void sbp_management_request_reconnect(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size);
+extern void sbp_management_request_logout(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size);
+
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 10/13] firewire-sbp-target: Add sbp_target_agent.{c,h}
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (8 preceding siblings ...)
2012-02-11 19:44 ` [PATCH 09/13] firewire-sbp-target: Add sbp_login.{c,h} Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-11 19:44 ` [PATCH 11/13] firewire-sbp-target: Add sbp_scsi_cmnd.{c,h} Chris Boot
` (4 subsequent siblings)
14 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This implements the SBP-2 Command Block Agent, or Target Agent. This is
what receives SCSI commands and forwards them to the target framework.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_target_agent.c | 360 +++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_target_agent.h | 30 +++
2 files changed, 390 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_target_agent.c
create mode 100644 drivers/target/sbp/sbp_target_agent.h
diff --git a/drivers/target/sbp/sbp_target_agent.c b/drivers/target/sbp/sbp_target_agent.c
new file mode 100644
index 0000000..4ab2000
--- /dev/null
+++ b/drivers/target/sbp/sbp_target_agent.c
@@ -0,0 +1,360 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+#include "sbp_util.h"
+
+static int tgt_agent_rw_agent_state(struct fw_card *card,
+ int tcode, int generation, void *data,
+ struct sbp_target_agent *agent)
+{
+ if (tcode == TCODE_READ_QUADLET_REQUEST) {
+ __be32 state;
+
+ pr_debug("tgt_agent AGENT_STATE READ\n");
+
+ state = cpu_to_be32(atomic_read(&agent->state));
+ memcpy(data, &state, sizeof(state));
+
+ return RCODE_COMPLETE;
+ } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
+ /* ignored */
+ return RCODE_COMPLETE;
+ } else {
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_agent_reset(struct fw_card *card,
+ int tcode, int generation, void *data,
+ struct sbp_target_agent *agent)
+{
+ if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
+ pr_debug("tgt_agent AGENT_RESET\n");
+ atomic_set(&agent->state, AGENT_STATE_RESET);
+ return RCODE_COMPLETE;
+ } else {
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_orb_pointer(struct fw_card *card,
+ int tcode, int generation, void *data,
+ struct sbp_target_agent *agent)
+{
+ struct sbp2_pointer *ptr = data;
+
+ if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+ int ret;
+
+ smp_wmb();
+ atomic_cmpxchg(&agent->state,
+ AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
+ smp_wmb();
+ if (atomic_cmpxchg(&agent->state,
+ AGENT_STATE_SUSPENDED,
+ AGENT_STATE_ACTIVE)
+ != AGENT_STATE_SUSPENDED)
+ return RCODE_CONFLICT_ERROR;
+ smp_wmb();
+
+ agent->orb_pointer = sbp2_pointer_to_addr(ptr);
+
+ pr_debug("tgt_agent ORB_POINTER write: %llu\n",
+ agent->orb_pointer);
+
+ ret = queue_work(fw_workqueue, &agent->work);
+ if (!ret)
+ return RCODE_CONFLICT_ERROR;
+
+ return RCODE_COMPLETE;
+ } else if (tcode == TCODE_READ_BLOCK_REQUEST) {
+ pr_debug("tgt_agent ORB_POINTER READ\n");
+ addr_to_sbp2_pointer(agent->orb_pointer, ptr);
+ return RCODE_COMPLETE;
+ } else {
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_doorbell(struct fw_card *card,
+ int tcode, int generation, void *data,
+ struct sbp_target_agent *agent)
+{
+ if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
+ int ret;
+
+ smp_wmb();
+ if (atomic_cmpxchg(&agent->state,
+ AGENT_STATE_SUSPENDED,
+ AGENT_STATE_ACTIVE)
+ != AGENT_STATE_SUSPENDED)
+ return RCODE_CONFLICT_ERROR;
+ smp_wmb();
+
+ pr_debug("tgt_agent DOORBELL\n");
+
+ ret = queue_work(fw_workqueue, &agent->work);
+ if (!ret)
+ return RCODE_CONFLICT_ERROR;
+
+ return RCODE_COMPLETE;
+ } else if (tcode == TCODE_READ_QUADLET_REQUEST) {
+ return RCODE_COMPLETE;
+ } else {
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_unsolicited_status_enable(struct fw_card *card,
+ int tcode, int generation, void *data,
+ struct sbp_target_agent *agent)
+{
+ if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
+ pr_debug("tgt_agent UNSOLICITED_STATUS_ENABLE\n");
+ atomic_set(&agent->login->unsolicited_status_enable, 1);
+ return RCODE_COMPLETE;
+ } else if (tcode == TCODE_READ_QUADLET_REQUEST) {
+ return RCODE_COMPLETE;
+ } else {
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static void tgt_agent_rw(struct fw_card *card,
+ struct fw_request *request, int tcode, int destination, int source,
+ int generation, unsigned long long offset, void *data, size_t length,
+ void *callback_data)
+{
+ struct sbp_target_agent *agent = callback_data;
+ int rcode = RCODE_ADDRESS_ERROR;
+
+ /* turn offset into the offset from the start of the block */
+ offset -= agent->handler.offset;
+
+ /* check the source matches the login */
+ if (source != agent->login->sess->node_id) {
+ pr_notice("ignoring request from foreign node (%x != %x)\n",
+ source, agent->login->sess->node_id);
+ fw_send_response(card, request, RCODE_TYPE_ERROR);
+ return;
+ }
+
+ if (offset == 0x00 && length == 4) {
+ /* AGENT_STATE */
+ rcode = tgt_agent_rw_agent_state(card, tcode,
+ generation, data, agent);
+ } else if (offset == 0x04 && length == 4) {
+ /* AGENT_RESET */
+ rcode = tgt_agent_rw_agent_reset(card, tcode,
+ generation, data, agent);
+ } else if (offset == 0x08 && length == 8) {
+ /* ORB_POINTER */
+ rcode = tgt_agent_rw_orb_pointer(card, tcode,
+ generation, data, agent);
+ } else if (offset == 0x10 && length == 4) {
+ /* DOORBELL */
+ rcode = tgt_agent_rw_doorbell(card, tcode,
+ generation, data, agent);
+ } else if (offset == 0x14 && length == 4) {
+ /* UNSOLICITED_STATUS_ENABLE */
+ rcode = tgt_agent_rw_unsolicited_status_enable(card, tcode,
+ generation, data, agent);
+ }
+
+ fw_send_response(card, request, rcode);
+}
+
+static void tgt_agent_process_work(struct work_struct *work)
+{
+ struct sbp_target_request *req =
+ container_of(work, struct sbp_target_request, work);
+
+ switch (ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc))) {
+ case 0:/* Format specified by this standard */
+ sbp_handle_command(req);
+ return;
+ case 1: /* Reserved for future standardization */
+ case 2: /* Vendor-dependent */
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ case 3: /* Dummy ORB */
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_DUMMY_ORB_COMPLETE));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ default:
+ BUG();
+ }
+}
+
+static void tgt_agent_fetch_work(struct work_struct *work)
+{
+ struct sbp_target_agent *agent =
+ container_of(work, struct sbp_target_agent, work);
+ struct sbp_session *sess = agent->login->sess;
+ struct sbp_target_request *req;
+ int ret;
+
+ smp_rmb();
+ if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE)
+ return;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ goto out;
+
+ smp_rmb();
+ if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE) {
+ sbp_free_request(req);
+ return;
+ }
+
+ /* read in the ORB */
+ ret = fw_run_transaction(sess->card, TCODE_READ_BLOCK_REQUEST,
+ sess->node_id, sess->generation, sess->speed,
+ agent->orb_pointer, &req->orb, sizeof(req->orb));
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("tgt_orb fetch failed: %x\n", ret);
+ sbp_free_request(req);
+ goto out;
+ }
+
+ smp_rmb();
+ if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE) {
+ sbp_free_request(req);
+ return;
+ }
+
+ req->agent = agent;
+ req->orb_pointer = agent->orb_pointer;
+
+ pr_debug("tgt_orb ptr:0x%llx next_orb:0x%llx data_descriptor:0x%llx misc:0x%x\n",
+ req->orb_pointer,
+ sbp2_pointer_to_addr(&req->orb.next_orb),
+ sbp2_pointer_to_addr(&req->orb.data_descriptor),
+ be32_to_cpu(req->orb.misc));
+
+ if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_SRC(STATUS_SRC_ORB_FINISHED));
+ } else {
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_SRC(STATUS_SRC_ORB_CONTINUING));
+ }
+
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_pointer >> 32));
+ req->status.orb_low = cpu_to_be32(agent->orb_pointer & 0xfffffffc);
+ INIT_WORK(&req->work, tgt_agent_process_work);
+
+ ret = queue_work(fw_workqueue, &req->work);
+ if (!ret) {
+ pr_err("tgt_orb queue_work failed\n");
+ sbp_free_request(req);
+ goto out;
+ }
+
+ /* check if we should carry on processing */
+ if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+ /* null next_orb */
+ goto out;
+ }
+
+ smp_rmb();
+ if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE)
+ return;
+
+ agent->orb_pointer = sbp2_pointer_to_addr(&req->orb.next_orb);
+
+ if (!queue_work(fw_workqueue, &agent->work)) {
+ pr_err("tgt_orb fetch queue_work failed\n");
+ goto out;
+ }
+
+ return;
+
+out:
+ /* finished */
+ atomic_cmpxchg(&agent->state,
+ AGENT_STATE_ACTIVE, AGENT_STATE_SUSPENDED);
+}
+
+struct sbp_target_agent *sbp_target_agent_register(
+ struct sbp_login_descriptor *login)
+{
+ struct sbp_target_agent *agent;
+ int ret;
+
+ agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+ if (!agent)
+ return ERR_PTR(-ENOMEM);
+
+ agent->handler.length = 0x20;
+ agent->handler.address_callback = tgt_agent_rw;
+ agent->handler.callback_data = agent;
+
+ agent->login = login;
+ atomic_set(&agent->state, AGENT_STATE_RESET);
+ INIT_WORK(&agent->work, tgt_agent_fetch_work);
+ agent->orb_pointer = (u64)-1;
+
+ ret = fw_core_add_address_handler(&agent->handler,
+ &sbp_register_region);
+ if (ret < 0) {
+ kfree(agent);
+ return ERR_PTR(ret);
+ }
+
+ return agent;
+}
+
+void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+ if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
+ flush_work_sync(&agent->work);
+
+ fw_core_remove_address_handler(&agent->handler);
+ kfree(agent);
+}
+
diff --git a/drivers/target/sbp/sbp_target_agent.h b/drivers/target/sbp/sbp_target_agent.h
new file mode 100644
index 0000000..870b901
--- /dev/null
+++ b/drivers/target/sbp/sbp_target_agent.h
@@ -0,0 +1,30 @@
+
+struct sbp_target_agent {
+ struct fw_address_handler handler;
+ struct sbp_login_descriptor *login;
+ atomic_t state;
+ struct work_struct work;
+ u64 orb_pointer;
+};
+
+struct sbp_target_request {
+ struct sbp_target_agent *agent;
+ u64 orb_pointer;
+ struct sbp_command_block_orb orb;
+ struct sbp_status_block status;
+ struct work_struct work;
+
+ struct se_cmd se_cmd;
+ struct sbp_page_table_entry *pg_tbl;
+ void *cmd_buf;
+ int unpacked_lun;
+ u32 data_len;
+ enum dma_data_direction data_dir;
+ void *data_buf;
+
+ unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+struct sbp_target_agent *sbp_target_agent_register(
+ struct sbp_login_descriptor *login);
+void sbp_target_agent_unregister(struct sbp_target_agent *agent);
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 11/13] firewire-sbp-target: Add sbp_scsi_cmnd.{c,h}
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (9 preceding siblings ...)
2012-02-11 19:44 ` [PATCH 10/13] firewire-sbp-target: Add sbp_target_agent.{c,h} Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-11 19:44 ` [PATCH 12/13] firewire-sbp-target: Add sbp_util.{c,h} Chris Boot
` (3 subsequent siblings)
14 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
Miscellaneous functions for dealing with SCSI commands, status, sense
data and data read/write. This is where the real grunt work of pushing
data in and out of the FireWire bus happens.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_scsi_cmnd.c | 357 ++++++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_scsi_cmnd.h | 6 +
2 files changed, 363 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_scsi_cmnd.c
create mode 100644 drivers/target/sbp/sbp_scsi_cmnd.h
diff --git a/drivers/target/sbp/sbp_scsi_cmnd.c b/drivers/target/sbp/sbp_scsi_cmnd.c
new file mode 100644
index 0000000..ca94a28
--- /dev/null
+++ b/drivers/target/sbp/sbp_scsi_cmnd.c
@@ -0,0 +1,357 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+
+#include "sbp_base.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+#include "sbp_util.h"
+
+/*
+ * Wraps fw_run_transaction taking into account page size and max payload, and
+ * retries the transaction if it fails
+ */
+static int sbp_run_transaction(struct sbp_target_request *req, int tcode,
+ unsigned long long offset, void *payload, size_t length)
+{
+ struct sbp_login_descriptor *login = req->agent->login;
+ struct sbp_session *sess = login->sess;
+ int ret, speed, max_payload, pg_size, seg_off = 0, seg_len;
+
+ speed = CMDBLK_ORB_SPEED(be32_to_cpu(req->orb.misc));
+ max_payload = 4 << CMDBLK_ORB_MAX_PAYLOAD(be32_to_cpu(req->orb.misc));
+ pg_size = CMDBLK_ORB_PG_SIZE(be32_to_cpu(req->orb.misc));
+
+ if (pg_size) {
+ pr_err("sbp_run_transaction: page size ignored\n");
+ pg_size = 0x100 << pg_size;
+ }
+
+ while (seg_off < length) {
+ seg_len = length - seg_off;
+ if (seg_len > max_payload)
+ seg_len = max_payload;
+
+ /* FIXME: take page_size into account */
+
+ /* FIXME: retry failed data transfers */
+ ret = fw_run_transaction(sess->card, tcode,
+ sess->node_id, sess->generation, speed,
+ offset + seg_off, payload + seg_off, seg_len);
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("sbp_run_transaction: txn failed: %x\n", ret);
+ return -EIO;
+ }
+
+ seg_off += seg_len;
+ }
+
+ return 0;
+}
+
+static int sbp_fetch_command(struct sbp_target_request *req)
+{
+ int ret, cmd_len, copy_len;
+
+ cmd_len = scsi_command_size(req->orb.command_block);
+
+ req->cmd_buf = kmalloc(cmd_len, GFP_KERNEL);
+ if (!req->cmd_buf)
+ return -ENOMEM;
+
+ memcpy(req->cmd_buf, req->orb.command_block,
+ min_t(int, cmd_len, sizeof(req->orb.command_block)));
+
+ if (cmd_len > sizeof(req->orb.command_block)) {
+ pr_debug("sbp_fetch_command: filling in long command\n");
+ copy_len = cmd_len - sizeof(req->orb.command_block);
+
+ ret = sbp_run_transaction(req, TCODE_READ_BLOCK_REQUEST,
+ req->orb_pointer + sizeof(req->orb),
+ req->cmd_buf + sizeof(req->orb.command_block), cmd_len);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sbp_fetch_page_table(struct sbp_target_request *req)
+{
+ int pg_tbl_sz, ret;
+ struct sbp_page_table_entry *pg_tbl;
+
+ if (!CMDBLK_ORB_PG_TBL_PRESENT(be32_to_cpu(req->orb.misc)))
+ return 0;
+
+ pg_tbl_sz = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc)) *
+ sizeof(struct sbp_page_table_entry);
+
+ pg_tbl = kmalloc(pg_tbl_sz, GFP_KERNEL);
+ if (!pg_tbl)
+ return -ENOMEM;
+
+ ret = sbp_run_transaction(req, TCODE_READ_BLOCK_REQUEST,
+ sbp2_pointer_to_addr(&req->orb.data_descriptor),
+ pg_tbl, pg_tbl_sz);
+ if (ret) {
+ kfree(pg_tbl);
+ return ret;
+ }
+
+ req->pg_tbl = pg_tbl;
+ return 0;
+}
+
+static void sbp_calc_data_length_direction(struct sbp_target_request *req)
+{
+ int data_size, direction, idx;
+
+ data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+ direction = CMDBLK_ORB_DIRECTION(be32_to_cpu(req->orb.misc));
+
+ if (!data_size) {
+ req->data_len = 0;
+ req->data_dir = DMA_NONE;
+ return;
+ }
+
+ req->data_dir = direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (req->pg_tbl) {
+ req->data_len = 0;
+ for (idx = 0; idx < data_size; idx++) {
+ req->data_len += be16_to_cpu(
+ req->pg_tbl[idx].segment_length);
+ }
+ } else {
+ req->data_len = data_size;
+ }
+}
+
+void sbp_handle_command(struct sbp_target_request *req)
+{
+ struct sbp_login_descriptor *login = req->agent->login;
+ struct sbp_session *sess = login->sess;
+ int ret;
+
+ ret = sbp_fetch_command(req);
+ if (ret) {
+ pr_debug("sbp_handle_command: fetch command failed: %d\n", ret);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ }
+
+ ret = sbp_fetch_page_table(req);
+ if (ret) {
+ pr_debug("sbp_handle_command: fetch page table failed: %d\n",
+ ret);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ }
+
+ req->unpacked_lun = req->agent->login->lun->unpacked_lun;
+ sbp_calc_data_length_direction(req);
+
+ pr_debug("sbp_handle_command unpacked_lun:%d data_len:%d "
+ "data_dir:%d\n", req->unpacked_lun, req->data_len,
+ req->data_dir);
+
+ target_submit_cmd(&req->se_cmd, sess->se_sess, req->cmd_buf,
+ req->sense_buf, req->unpacked_lun, 0, MSG_SIMPLE_TAG,
+ req->data_dir, TARGET_SCF_UNKNOWN_SIZE);
+}
+
+/*
+ * DMA_TO_DEVICE = read from initiator (SCSI WRITE)
+ * DMA_FROM_DEVICE = write to initiator (SCSI READ)
+ */
+int sbp_rw_data(struct sbp_target_request *req)
+{
+ int ret;
+
+ WARN_ON(!req->data_len);
+
+ if (req->pg_tbl) {
+ int idx, offset = 0, data_size;
+
+ data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+
+ for (idx = 0; idx < data_size; idx++) {
+ int pte_len = be16_to_cpu(
+ req->pg_tbl[idx].segment_length);
+ u64 pte_offset = (u64)be16_to_cpu(
+ req->pg_tbl[idx].segment_base_hi) << 32 |
+ be32_to_cpu(req->pg_tbl[idx].segment_base_lo);
+
+ ret = sbp_run_transaction(req,
+ (req->data_dir == DMA_TO_DEVICE) ?
+ TCODE_READ_BLOCK_REQUEST :
+ TCODE_WRITE_BLOCK_REQUEST,
+ pte_offset, req->data_buf + offset, pte_len);
+ if (ret)
+ break;
+
+ offset += pte_len;
+ }
+ } else {
+ ret = sbp_run_transaction(req,
+ (req->data_dir == DMA_TO_DEVICE) ?
+ TCODE_READ_BLOCK_REQUEST : TCODE_WRITE_BLOCK_REQUEST,
+ sbp2_pointer_to_addr(&req->orb.data_descriptor),
+ req->data_buf, req->data_len);
+ }
+
+ return ret;
+}
+
+int sbp_send_status(struct sbp_target_request *req)
+{
+ int ret, length;
+ struct sbp_login_descriptor *login = req->agent->login;
+
+ /* calculate how much data to send */
+ length = (((be32_to_cpu(req->status.status) >> 24) & 0x07) + 1) * 4;
+
+ ret = sbp_run_transaction(req, TCODE_WRITE_BLOCK_REQUEST,
+ login->status_fifo_addr, &req->status, length);
+ if (ret) {
+ pr_debug("sbp_send_status: write failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void sbp_sense_mangle(struct sbp_target_request *req)
+{
+ struct se_cmd *se_cmd = &req->se_cmd;
+ u8 *sense = req->sense_buf;
+ u8 *status = req->status.data;
+
+ WARN_ON(se_cmd->scsi_sense_length < 18);
+
+ switch (sense[0] & 0x7f) {
+ case 0x70:
+ status[0] = 0 << 6; /* sfmt */
+ break;
+ case 0x71:
+ status[0] = 1 << 6; /* sfmt */
+ break;
+ default:
+ /*
+ * TODO: SBP-3 specifies what we should do with descriptor
+ * format sense data
+ */
+ pr_err("sbp_send_sense: unknown sense format: 0x%x\n",
+ sense[0]);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQUEST_ABORTED));
+ return;
+ }
+
+ status[0] |= se_cmd->scsi_status & 0x3f;/* status */
+ status[1] =
+ (sense[0] & 0x80) | /* valid */
+ ((sense[2] & 0xe0) >> 1) | /* mark, eom, ili */
+ (sense[2] & 0x0f); /* sense_key */
+ status[2] = se_cmd->scsi_asc; /* sense_code */
+ status[3] = se_cmd->scsi_ascq; /* sense_qualifier */
+
+ /* information */
+ status[4] = sense[3];
+ status[5] = sense[4];
+ status[6] = sense[5];
+ status[7] = sense[6];
+
+ /* CDB-dependent */
+ status[8] = sense[8];
+ status[9] = sense[9];
+ status[10] = sense[10];
+ status[11] = sense[11];
+
+ /* fru */
+ status[12] = sense[14];
+
+ /* sense_key-dependent */
+ status[13] = sense[15];
+ status[14] = sense[16];
+ status[15] = sense[17];
+
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(5) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+int sbp_send_sense(struct sbp_target_request *req)
+{
+ struct se_cmd *se_cmd = &req->se_cmd;
+
+ if (se_cmd->scsi_sense_length) {
+ sbp_sense_mangle(req);
+ } else {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+ }
+
+ return sbp_send_status(req);
+}
+
+void sbp_free_request(struct sbp_target_request *req)
+{
+ kfree(req->pg_tbl);
+ kfree(req->cmd_buf);
+ kfree(req->data_buf);
+ kfree(req);
+}
diff --git a/drivers/target/sbp/sbp_scsi_cmnd.h b/drivers/target/sbp/sbp_scsi_cmnd.h
new file mode 100644
index 0000000..5e82b25
--- /dev/null
+++ b/drivers/target/sbp/sbp_scsi_cmnd.h
@@ -0,0 +1,6 @@
+
+void sbp_handle_command(struct sbp_target_request *req);
+int sbp_rw_data(struct sbp_target_request *req);
+int sbp_send_status(struct sbp_target_request *req);
+int sbp_send_sense(struct sbp_target_request *req);
+void sbp_free_request(struct sbp_target_request *req);
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 12/13] firewire-sbp-target: Add sbp_util.{c,h}
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (10 preceding siblings ...)
2012-02-11 19:44 ` [PATCH 11/13] firewire-sbp-target: Add sbp_scsi_cmnd.{c,h} Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-11 19:44 ` [PATCH 13/13] firewire-sbp-target: Add to target Kconfig and Makefile Chris Boot
` (2 subsequent siblings)
14 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
Common helper functions and global declarations.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_util.c | 36 ++++++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_util.h | 15 +++++++++++++++
2 files changed, 51 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_util.c
create mode 100644 drivers/target/sbp/sbp_util.h
diff --git a/drivers/target/sbp/sbp_util.c b/drivers/target/sbp/sbp_util.c
new file mode 100644
index 0000000..33073b1
--- /dev/null
+++ b/drivers/target/sbp/sbp_util.c
@@ -0,0 +1,36 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_util.h"
+
+const struct fw_address_region sbp_register_region = {
+ .start = CSR_REGISTER_BASE + 0x10000,
+ .end = 0x1000000000000ULL,
+};
+
diff --git a/drivers/target/sbp/sbp_util.h b/drivers/target/sbp/sbp_util.h
new file mode 100644
index 0000000..cd3e00d
--- /dev/null
+++ b/drivers/target/sbp/sbp_util.h
@@ -0,0 +1,15 @@
+
+extern const struct fw_address_region sbp_register_region;
+
+static inline u64 sbp2_pointer_to_addr(const struct sbp2_pointer *ptr)
+{
+ return (u64)(be32_to_cpu(ptr->high) & 0x0000ffff) << 32 |
+ (be32_to_cpu(ptr->low) & 0xfffffffc);
+}
+
+static inline void addr_to_sbp2_pointer(u64 addr, struct sbp2_pointer *ptr)
+{
+ ptr->high = cpu_to_be32(addr >> 32);
+ ptr->low = cpu_to_be32(addr);
+}
+
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 13/13] firewire-sbp-target: Add to target Kconfig and Makefile
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (11 preceding siblings ...)
2012-02-11 19:44 ` [PATCH 12/13] firewire-sbp-target: Add sbp_util.{c,h} Chris Boot
@ 2012-02-11 19:44 ` Chris Boot
2012-02-12 14:12 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Stefan Richter
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
14 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-11 19:44 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This commit also adds an entry to the MAINTAINERS file.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
MAINTAINERS | 9 +++++++++
drivers/target/Kconfig | 1 +
drivers/target/Makefile | 1 +
3 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index c0f348d..252aa72 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2695,6 +2695,15 @@ T: git git://git.alsa-project.org/alsa-kernel.git
S: Maintained
F: sound/firewire/
+FIREWIRE SBP-2 TARGET
+M: Chris Boot <bootc@bootc.net>
+L: linux-scsi@vger.kernel.org
+L: target-devel@vger.kernel.org
+L: linux1394-devel@lists.sourceforge.net
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core-2.6.git master
+S: Maintained
+F: drivers/target/sbp/
+
FIREWIRE SUBSYSTEM
M: Stefan Richter <stefanr@s5r6.in-berlin.de>
L: linux1394-devel@lists.sourceforge.net
diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig
index fc5fa9f..2cfa467 100644
--- a/drivers/target/Kconfig
+++ b/drivers/target/Kconfig
@@ -41,5 +41,6 @@ source "drivers/target/tcm_fc/Kconfig"
source "drivers/target/iscsi/Kconfig"
source "drivers/target/tcm_vhost/Kconfig"
source "drivers/target/usb-gadget/Kconfig"
+source "drivers/target/sbp/Kconfig"
endif
diff --git a/drivers/target/Makefile b/drivers/target/Makefile
index 6b5f526..1ae8862 100644
--- a/drivers/target/Makefile
+++ b/drivers/target/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_TCM_FC) += tcm_fc/
obj-$(CONFIG_ISCSI_TARGET) += iscsi/
obj-$(CONFIG_TCM_VHOST) += tcm_vhost/
obj-$(CONFIG_TARGET_USB_GADGET) += usb-gadget/
+obj-$(CONFIG_FIREWIRE_SBP_TARGET) += sbp/
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (12 preceding siblings ...)
2012-02-11 19:44 ` [PATCH 13/13] firewire-sbp-target: Add to target Kconfig and Makefile Chris Boot
@ 2012-02-12 14:12 ` Stefan Richter
2012-02-12 15:13 ` Chris Boot
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
14 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-02-12 14:12 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 11 Chris Boot wrote:
> Well after lots of work I have a working and generally (at least I think)
> sensible starting point for the FireWire target. It appears to work fine in all
> the configurations I've tested it against, including Linux and Mac OS X
> initiators.
I only very quickly scrolled through your patches yet. Looks all very
readable and well laid out. Some random thoughts:
Some of the smaller source files could certainly be merged with other
ones without loss of readability.
Some comments about what the code is doing can be removed since the
function names are very well readable on their own. Example:
+ /* read the peer's GUID */
+ ret = read_peer_guid(&guid, req);
The APIs which you include from "../../firewire/core.h" should eventually
be moved to <linux/firewire.h>. I think it does not matter whether this
is done before or after mainline merge. When we do so we should check
whether the affected APIs can be improved for usage in drivers.
Many of the printks should surely be demoted to debug messages with
runtime on-and-off switch.
There are list traversals and list manipulations that make an impression
as if they wanted to be mutex- or lock-protected, but I haven't looked yet
in which contexts these accesses happen.
sbp_proto.c:
+/*
+ * Handlers for Serial Attached SCSI (SBP)
+ */
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ Serial Bus Protocol
Though this is also readily apparent from the top comment in the file.
The four EXPORT_SYMBOLs in sbp_proto.c can be removed, it seems.
sbp_login.c:
+ pr_notice("initiator already logged-in\n");
+
+ /*
+ * SBP-2 R4 says we should return access denied, but
+ * that can confuse initiators. Instead we need to
+ * treat this like a reconnect, but send the login
+ * response block like a fresh login.
+ */
Are there initiators which don't bother with reconnect but send relogin
straight away?
Kconfig:
"default n" is redundant.
"*older* Apple computers": They are still manufactured with this feature.
--
Stefan Richter
-=====-===-- --=- -==--
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target
2012-02-12 14:12 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Stefan Richter
@ 2012-02-12 15:13 ` Chris Boot
2012-02-12 16:16 ` Stefan Richter
0 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-12 15:13 UTC (permalink / raw)
To: Stefan Richter
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On 12/02/2012 14:12, Stefan Richter wrote:
> On Feb 11 Chris Boot wrote:
>> Well after lots of work I have a working and generally (at least I think)
>> sensible starting point for the FireWire target. It appears to work fine in all
>> the configurations I've tested it against, including Linux and Mac OS X
>> initiators.
>
> I only very quickly scrolled through your patches yet. Looks all very
> readable and well laid out. Some random thoughts:
Thanks for taking a look!
> Some of the smaller source files could certainly be merged with other
> ones without loss of readability.
Yes, definitely.
> Some comments about what the code is doing can be removed since the
> function names are very well readable on their own. Example:
> + /* read the peer's GUID */
> + ret = read_peer_guid(&guid, req);
Guilty as charged. I'll go through and clean this up.
> The APIs which you include from "../../firewire/core.h" should eventually
> be moved to<linux/firewire.h>. I think it does not matter whether this
> is done before or after mainline merge. When we do so we should check
> whether the affected APIs can be improved for usage in drivers.
I'm not even sure I use that much from there at all. Possibly only
fw_card_{get,put,release}(), so that could quite easily be moved into
<linux/firewire.h>.
> Many of the printks should surely be demoted to debug messages with
> runtime on-and-off switch.
I've moved a lot of them to pr_debug() which doesn't emit anything
unless you ask it to. Are there others you think should be pr_debug()
that aren't?
> There are list traversals and list manipulations that make an impression
> as if they wanted to be mutex- or lock-protected, but I haven't looked yet
> in which contexts these accesses happen.
Yes this is what I'm most concerned about but it's an area I know very
little about. Some hand-holding would be appreciated.
> sbp_proto.c:
> +/*
> + * Handlers for Serial Attached SCSI (SBP)
> + */
> ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ Serial Bus Protocol
> Though this is also readily apparent from the top comment in the file.
Hah. Can you tell where I copied and pasted from? :-) Fixed.
> The four EXPORT_SYMBOLs in sbp_proto.c can be removed, it seems.
Another copy & paste leftover. Also fixed.
> sbp_login.c:
> + pr_notice("initiator already logged-in\n");
> +
> + /*
> + * SBP-2 R4 says we should return access denied, but
> + * that can confuse initiators. Instead we need to
> + * treat this like a reconnect, but send the login
> + * response block like a fresh login.
> + */
> Are there initiators which don't bother with reconnect but send relogin
> straight away?
I found my PowerBook did. It looks like when OpenFirmware hands over to
Darwin, the latter just does a login. I changed this before I had the
code to expire sessions once the reconnect timeout expires though so
it's probably unnecessary now but it would slow down the boot by several
seconds.
> Kconfig:
> "default n" is redundant.
> "*older* Apple computers": They are still manufactured with this feature.
I thought all the newer ones only did this over Thunderbolt and not
FireWire? I'm more than happy to change this though.
Cheers,
Chris
--
Chris Boot
bootc@bootc.net
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target
2012-02-12 15:13 ` Chris Boot
@ 2012-02-12 16:16 ` Stefan Richter
0 siblings, 0 replies; 104+ messages in thread
From: Stefan Richter @ 2012-02-12 16:16 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 12 Chris Boot wrote:
> On 12/02/2012 14:12, Stefan Richter wrote:
> > The APIs which you include from "../../firewire/core.h" should eventually
> > be moved to<linux/firewire.h>. I think it does not matter whether this
> > is done before or after mainline merge. When we do so we should check
> > whether the affected APIs can be improved for usage in drivers.
>
> I'm not even sure I use that much from there at all. Possibly only
> fw_card_{get,put,release}(), so that could quite easily be moved into
> <linux/firewire.h>.
Yes, I suppose this is just the card reference counting which we found
is needed for some types of userspace drivers too. But for them it is
wrapped up in the core-cdev.c glue, thus still core-internal presently.
> > Many of the printks should surely be demoted to debug messages with
> > runtime on-and-off switch.
>
> I've moved a lot of them to pr_debug() which doesn't emit anything
> unless you ask it to. Are there others you think should be pr_debug()
> that aren't?
OK, could have been a wrong impression after superficial reading.
[...]
> > sbp_login.c:
> > + pr_notice("initiator already logged-in\n");
> > +
> > + /*
> > + * SBP-2 R4 says we should return access denied, but
> > + * that can confuse initiators. Instead we need to
> > + * treat this like a reconnect, but send the login
> > + * response block like a fresh login.
> > + */
> > Are there initiators which don't bother with reconnect but send relogin
> > straight away?
>
> I found my PowerBook did. It looks like when OpenFirmware hands over to
> Darwin, the latter just does a login. I changed this before I had the
> code to expire sessions once the reconnect timeout expires though so
> it's probably unnecessary now but it would slow down the boot by several
> seconds.
Right, this is a special case. I remember this as an issue with Linux on
Apple PCs as well, together with a particular harddisk firmware with
somewhat unusual timing characteristics, which rejects firewire-sbp2's
login because it still considers the firmware's login valid. The former
ieee1394 + sbp2 stack had more luck, but probably just because of slightly
different timing on their part.
You could make this a special case for initiators with Apple's OUI in
the EUI-64. But I suppose there is no real downside to do this
unconditionally.
Anyway, the case of Apple firmware login -> OS login handover could
certainly be mentioned in the quoted comment, as the one quite common case
where this deviation from the spec is required or at least beneficial.
> > Kconfig:
> > "default n" is redundant.
> > "*older* Apple computers": They are still manufactured with this feature.
>
> I thought all the newer ones only did this over Thunderbolt and not
> FireWire? I'm more than happy to change this though.
Oh, I don't actually know about these ones.
--
Stefan Richter
-=====-===-- --=- -==--
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH v2 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target
2012-02-11 19:43 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (13 preceding siblings ...)
2012-02-12 14:12 ` [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target Stefan Richter
@ 2012-02-15 14:47 ` Chris Boot
2012-02-15 14:47 ` [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request Chris Boot
` (11 more replies)
14 siblings, 12 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-15 14:47 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr
The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection
as a SCSI transport. This module uses the SCSI Target framework to
expose LUNs to other machines attached to a FireWire bus, in effect
acting as a FireWire hard disk similar to FireWire Target Disk mode on
many Apple computers.
Changes since v1:
* Fixed some copy & paste issues
* Updated Kconfig (wording, depends, 'default n')
* Removed some unnecessary EXPORT_SYMBOL()s
* Merged sbp_util.{c,h} into sbp_configfs.c and sbp_base.h
* Merged sbp_proto.{c,h} into sbp_fabric.{c,h}
* Cleaned up comments and several printks
* Fixed a few minor bugs
* Create & use our own workqueue instead of using fw_workqueue
* Dropped the unused TFO->new_cmd_map and sbp_new_cmd()
* Overhauled and simplified tgt_agent_fetch_work()
* Removed some redundant members of struct sbp_target_request
* Removed struct sbp_lun and code to maintain redundant LUN list
* Added spinlock to struct sbp_session and use locking throughout
* Moved fw_card_{get,put,release}() into linux/firewire.h
It didn't feel like a lot of changes but actually that's quite a lot of work,
looking back at it...
Cheers,
Chris
--
Chris Boot
bootc@bootc.net.
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
@ 2012-02-15 14:47 ` Chris Boot
2012-02-15 19:09 ` Stefan Richter
2012-02-15 14:47 ` [PATCH v2 02/11] firewire: Move fw_card kref functions into linux/firewire.h Chris Boot
` (10 subsequent siblings)
11 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-15 14:47 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
Sometimes it's useful to know the FireWire speed of the request that has
just come in to a fw_address_handler callback. As struct fw_request is
opaque we can't peek inside to get the speed out of the struct fw_packet
that's just inside. For example, the SBP-2 spec says:
"The speed at which the block write request to the MANAGEMENT_AGENT
register is received shall determine the speed used by the target for
all subsequent requests to read the initiator’s configuration ROM, fetch
ORB’s from initiator memory or store status at the initiator’s
status_FIFO. Command block ORB’s separately specify the speed for
requests addressed to the data buffer or page table."
[ ANSI T10/1155D Revision 4 page 53/54 ]
Signed-off-by: Chris Boot <bootc@bootc.net>
Acked-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Cc: Clemens Ladisch <clemens@ladisch.de>
---
drivers/firewire/core-transaction.c | 16 ++++++++++++++++
include/linux/firewire.h | 1 +
2 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index 855ab3f..614f592 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -820,6 +820,22 @@ void fw_send_response(struct fw_card *card,
}
EXPORT_SYMBOL(fw_send_response);
+/**
+ * fw_get_request_speed() - Discover bus speed used for this request
+ * @request: The struct fw_request from which to obtain the speed.
+ *
+ * In certain circumstances it's important to be able to obtain the speed at
+ * which a request was made to an address handler, for example when
+ * implementing an SBP-2 or SBP-3 target. This function inspects the response
+ * object to obtain the speed, which is copied from the request packet in
+ * allocate_request().
+ */
+int fw_get_request_speed(struct fw_request *request)
+{
+ return request->response.speed;
+}
+EXPORT_SYMBOL(fw_get_request_speed);
+
static void handle_exclusive_region_request(struct fw_card *card,
struct fw_packet *p,
struct fw_request *request,
diff --git a/include/linux/firewire.h b/include/linux/firewire.h
index 84ccf8e..f010307 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -340,6 +340,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler,
void fw_core_remove_address_handler(struct fw_address_handler *handler);
void fw_send_response(struct fw_card *card,
struct fw_request *request, int rcode);
+int fw_get_request_speed(struct fw_request *request);
void fw_send_request(struct fw_card *card, struct fw_transaction *t,
int tcode, int destination_id, int generation, int speed,
unsigned long long offset, void *payload, size_t length,
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request
2012-02-15 14:47 ` [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request Chris Boot
@ 2012-02-15 19:09 ` Stefan Richter
2012-02-15 19:10 ` Chris Boot
0 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-02-15 19:09 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 15 Chris Boot wrote:
> --- a/drivers/firewire/core-transaction.c
> +++ b/drivers/firewire/core-transaction.c
> @@ -820,6 +820,22 @@ void fw_send_response(struct fw_card *card,
> }
> EXPORT_SYMBOL(fw_send_response);
>
> +/**
> + * fw_get_request_speed() - Discover bus speed used for this request
> + * @request: The struct fw_request from which to obtain the speed.
> + *
> + * In certain circumstances it's important to be able to obtain the speed at
> + * which a request was made to an address handler, for example when
> + * implementing an SBP-2 or SBP-3 target. This function inspects the response
> + * object to obtain the speed, which is copied from the request packet in
> + * allocate_request().
> + */
> +int fw_get_request_speed(struct fw_request *request)
> +{
> + return request->response.speed;
> +}
> +EXPORT_SYMBOL(fw_get_request_speed);
Uh oh, what have I done by asking for a comment? :-)
Can you tell what's wrong with this API documentation?
--
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request
2012-02-15 19:09 ` Stefan Richter
@ 2012-02-15 19:10 ` Chris Boot
2012-02-15 22:01 ` Stefan Richter
0 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-15 19:10 UTC (permalink / raw)
To: Stefan Richter
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On 15/02/2012 19:09, Stefan Richter wrote:
> On Feb 15 Chris Boot wrote:
>> --- a/drivers/firewire/core-transaction.c
>> +++ b/drivers/firewire/core-transaction.c
>> @@ -820,6 +820,22 @@ void fw_send_response(struct fw_card *card,
>> }
>> EXPORT_SYMBOL(fw_send_response);
>>
>> +/**
>> + * fw_get_request_speed() - Discover bus speed used for this request
>> + * @request: The struct fw_request from which to obtain the speed.
>> + *
>> + * In certain circumstances it's important to be able to obtain the speed at
>> + * which a request was made to an address handler, for example when
>> + * implementing an SBP-2 or SBP-3 target. This function inspects the response
>> + * object to obtain the speed, which is copied from the request packet in
>> + * allocate_request().
>> + */
>> +int fw_get_request_speed(struct fw_request *request)
>> +{
>> + return request->response.speed;
>> +}
>> +EXPORT_SYMBOL(fw_get_request_speed);
>
> Uh oh, what have I done by asking for a comment? :-)
>
> Can you tell what's wrong with this API documentation?
Better to have too much than too little? :-)
Linux 3.4, now with added Enterprise.
Shall I cut it down a bit?
Chris
--
Chris Boot
bootc@bootc.net
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request
2012-02-15 19:10 ` Chris Boot
@ 2012-02-15 22:01 ` Stefan Richter
2012-02-16 9:12 ` Chris Boot
0 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-02-15 22:01 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 15 Chris Boot wrote:
> On 15/02/2012 19:09, Stefan Richter wrote:
> > On Feb 15 Chris Boot wrote:
> >> --- a/drivers/firewire/core-transaction.c
> >> +++ b/drivers/firewire/core-transaction.c
> >> @@ -820,6 +820,22 @@ void fw_send_response(struct fw_card *card,
> >> }
> >> EXPORT_SYMBOL(fw_send_response);
> >>
> >> +/**
> >> + * fw_get_request_speed() - Discover bus speed used for this request
> >> + * @request: The struct fw_request from which to obtain the speed.
> >> + *
> >> + * In certain circumstances it's important to be able to obtain the speed at
> >> + * which a request was made to an address handler, for example when
> >> + * implementing an SBP-2 or SBP-3 target. This function inspects the response
> >> + * object to obtain the speed, which is copied from the request packet in
> >> + * allocate_request().
> >> + */
> >> +int fw_get_request_speed(struct fw_request *request)
> >> +{
> >> + return request->response.speed;
> >> +}
> >> +EXPORT_SYMBOL(fw_get_request_speed);
> >
> > Uh oh, what have I done by asking for a comment? :-)
> >
> > Can you tell what's wrong with this API documentation?
>
> Better to have too much than too little? :-)
>
> Linux 3.4, now with added Enterprise.
>
> Shall I cut it down a bit?
a) The implementation of the function should not be explained here;
after all it is meant to be opaque to API users. Besides, if somebody
changes the implementation he will for sure forget to change the comment.
b) It is fairly self-evident at which occasions an API user would want
to use this function. (Everytime when he needs to know that speed.)
c) The function call argument does not really need to be explained
either as soon as the purpose of the function has been made known.
So in my first response where I already acked your patch I should have
simply asked for
/**
* fw_get_request_speed() - returns speed at which the @request was received
*/
to be added to your patch. :-)
Patch review could be so easy for everyone involved if the reviewer knew
how to express himself...
--
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request
2012-02-15 22:01 ` Stefan Richter
@ 2012-02-16 9:12 ` Chris Boot
0 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-16 9:12 UTC (permalink / raw)
To: Stefan Richter
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On 15/02/2012 22:01, Stefan Richter wrote:
> On Feb 15 Chris Boot wrote:
>> On 15/02/2012 19:09, Stefan Richter wrote:
>>> On Feb 15 Chris Boot wrote:
>>>> --- a/drivers/firewire/core-transaction.c
>>>> +++ b/drivers/firewire/core-transaction.c
>>>> @@ -820,6 +820,22 @@ void fw_send_response(struct fw_card *card,
>>>> }
>>>> EXPORT_SYMBOL(fw_send_response);
>>>>
>>>> +/**
>>>> + * fw_get_request_speed() - Discover bus speed used for this request
>>>> + * @request: The struct fw_request from which to obtain the speed.
>>>> + *
>>>> + * In certain circumstances it's important to be able to obtain the speed at
>>>> + * which a request was made to an address handler, for example when
>>>> + * implementing an SBP-2 or SBP-3 target. This function inspects the response
>>>> + * object to obtain the speed, which is copied from the request packet in
>>>> + * allocate_request().
>>>> + */
>>>> +int fw_get_request_speed(struct fw_request *request)
>>>> +{
>>>> + return request->response.speed;
>>>> +}
>>>> +EXPORT_SYMBOL(fw_get_request_speed);
>>>
>>> Uh oh, what have I done by asking for a comment? :-)
>>>
>>> Can you tell what's wrong with this API documentation?
>>
>> Better to have too much than too little? :-)
>>
>> Linux 3.4, now with added Enterprise.
>>
>> Shall I cut it down a bit?
>
> a) The implementation of the function should not be explained here;
> after all it is meant to be opaque to API users. Besides, if somebody
> changes the implementation he will for sure forget to change the comment.
>
> b) It is fairly self-evident at which occasions an API user would want
> to use this function. (Everytime when he needs to know that speed.)
>
> c) The function call argument does not really need to be explained
> either as soon as the purpose of the function has been made known.
>
> So in my first response where I already acked your patch I should have
> simply asked for
>
> /**
> * fw_get_request_speed() - returns speed at which the @request was received
> */
>
> to be added to your patch. :-)
>
> Patch review could be so easy for everyone involved if the reviewer knew
> how to express himself...
I guess it comes from me just trying too hard... Will fix for v3.
Cheers,
Chris
--
Chris Boot
bootc@bootc.net
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH v2 02/11] firewire: Move fw_card kref functions into linux/firewire.h
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
2012-02-15 14:47 ` [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request Chris Boot
@ 2012-02-15 14:47 ` Chris Boot
2012-02-15 19:10 ` Stefan Richter
2012-02-15 14:47 ` [PATCH v2 03/11] firewire-sbp-target: Add Kconfig, Makefile and TODO Chris Boot
` (9 subsequent siblings)
11 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-15 14:47 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
When writing a firewire driver that doesn't deal with struct fw_device
objects (e.g. it only publishes FireWire units and doesn't subscribe to
them), you likely need to keep referenced to struct fw_card objects so
that you can send messages to other nodes. This patch moves
fw_card_put(), fw_card_get() and fw_card_release() into the public
include/linux/firewire.h header instead of drivers/firewire/core.h, and
adds EXPORT_SYMBOL_GPL(fw_card_release).
The firewire-sbp-target module requires these so it can keep a reference
to the fw_card object in order that it can fetch ORBs to execute and
read/write related data and status information.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/firewire/core-card.c | 1 +
drivers/firewire/core.h | 15 ---------------
include/linux/firewire.h | 14 ++++++++++++++
3 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index 85661b0..42b180b 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -654,6 +654,7 @@ void fw_card_release(struct kref *kref)
complete(&card->done);
}
+EXPORT_SYMBOL_GPL(fw_card_release);
void fw_core_remove_card(struct fw_card *card)
{
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index b45be57..b44657b 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -111,21 +111,6 @@ int fw_compute_block_crc(__be32 *block);
void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset);
void fw_schedule_bm_work(struct fw_card *card, unsigned long delay);
-static inline struct fw_card *fw_card_get(struct fw_card *card)
-{
- kref_get(&card->kref);
-
- return card;
-}
-
-void fw_card_release(struct kref *kref);
-
-static inline void fw_card_put(struct fw_card *card)
-{
- kref_put(&card->kref, fw_card_release);
-}
-
-
/* -cdev */
extern const struct file_operations fw_device_ops;
diff --git a/include/linux/firewire.h b/include/linux/firewire.h
index f010307..341e51c 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -138,6 +138,20 @@ struct fw_card {
__be32 maint_utility_register;
};
+static inline struct fw_card *fw_card_get(struct fw_card *card)
+{
+ kref_get(&card->kref);
+
+ return card;
+}
+
+void fw_card_release(struct kref *kref);
+
+static inline void fw_card_put(struct fw_card *card)
+{
+ kref_put(&card->kref, fw_card_release);
+}
+
struct fw_attribute_group {
struct attribute_group *groups[2];
struct attribute_group group;
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH v2 02/11] firewire: Move fw_card kref functions into linux/firewire.h
2012-02-15 14:47 ` [PATCH v2 02/11] firewire: Move fw_card kref functions into linux/firewire.h Chris Boot
@ 2012-02-15 19:10 ` Stefan Richter
2012-02-16 9:18 ` Chris Boot
0 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-02-15 19:10 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 15 Chris Boot wrote:
> When writing a firewire driver that doesn't deal with struct fw_device
> objects (e.g. it only publishes FireWire units and doesn't subscribe to
> them), you likely need to keep referenced to struct fw_card objects so
> that you can send messages to other nodes. This patch moves
> fw_card_put(), fw_card_get() and fw_card_release() into the public
> include/linux/firewire.h header instead of drivers/firewire/core.h, and
> adds EXPORT_SYMBOL_GPL(fw_card_release).
>
> The firewire-sbp-target module requires these so it can keep a reference
> to the fw_card object in order that it can fetch ORBs to execute and
> read/write related data and status information.
>
> Signed-off-by: Chris Boot <bootc@bootc.net>
> Cc: Clemens Ladisch <clemens@ladisch.de>
Acked-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
> ---
> drivers/firewire/core-card.c | 1 +
> drivers/firewire/core.h | 15 ---------------
> include/linux/firewire.h | 14 ++++++++++++++
> 3 files changed, 15 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
> index 85661b0..42b180b 100644
> --- a/drivers/firewire/core-card.c
> +++ b/drivers/firewire/core-card.c
> @@ -654,6 +654,7 @@ void fw_card_release(struct kref *kref)
>
> complete(&card->done);
> }
> +EXPORT_SYMBOL_GPL(fw_card_release);
>
> void fw_core_remove_card(struct fw_card *card)
> {
> diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
> index b45be57..b44657b 100644
> --- a/drivers/firewire/core.h
> +++ b/drivers/firewire/core.h
> @@ -111,21 +111,6 @@ int fw_compute_block_crc(__be32 *block);
> void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset);
> void fw_schedule_bm_work(struct fw_card *card, unsigned long delay);
>
> -static inline struct fw_card *fw_card_get(struct fw_card *card)
> -{
> - kref_get(&card->kref);
> -
> - return card;
> -}
> -
> -void fw_card_release(struct kref *kref);
> -
> -static inline void fw_card_put(struct fw_card *card)
> -{
> - kref_put(&card->kref, fw_card_release);
> -}
> -
> -
> /* -cdev */
>
> extern const struct file_operations fw_device_ops;
> diff --git a/include/linux/firewire.h b/include/linux/firewire.h
> index f010307..341e51c 100644
> --- a/include/linux/firewire.h
> +++ b/include/linux/firewire.h
> @@ -138,6 +138,20 @@ struct fw_card {
> __be32 maint_utility_register;
> };
>
> +static inline struct fw_card *fw_card_get(struct fw_card *card)
> +{
> + kref_get(&card->kref);
> +
> + return card;
> +}
> +
> +void fw_card_release(struct kref *kref);
> +
> +static inline void fw_card_put(struct fw_card *card)
> +{
> + kref_put(&card->kref, fw_card_release);
> +}
> +
> struct fw_attribute_group {
> struct attribute_group *groups[2];
> struct attribute_group group;
--
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 02/11] firewire: Move fw_card kref functions into linux/firewire.h
2012-02-15 19:10 ` Stefan Richter
@ 2012-02-16 9:18 ` Chris Boot
0 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-16 9:18 UTC (permalink / raw)
To: Stefan Richter
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On 15/02/2012 19:10, Stefan Richter wrote:
> On Feb 15 Chris Boot wrote:
>> When writing a firewire driver that doesn't deal with struct fw_device
>> objects (e.g. it only publishes FireWire units and doesn't subscribe to
>> them), you likely need to keep referenced to struct fw_card objects so
>> that you can send messages to other nodes. This patch moves
>> fw_card_put(), fw_card_get() and fw_card_release() into the public
>> include/linux/firewire.h header instead of drivers/firewire/core.h, and
>> adds EXPORT_SYMBOL_GPL(fw_card_release).
>>
>> The firewire-sbp-target module requires these so it can keep a reference
>> to the fw_card object in order that it can fetch ORBs to execute and
>> read/write related data and status information.
>>
>> Signed-off-by: Chris Boot<bootc@bootc.net>
>> Cc: Clemens Ladisch<clemens@ladisch.de>
>
> Acked-by: Stefan Richter<stefanr@s5r6.in-berlin.de>
Thanks!
[snip]
Cheers,
Chris
--
Chris Boot
bootc@bootc.net
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH v2 03/11] firewire-sbp-target: Add Kconfig, Makefile and TODO
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
2012-02-15 14:47 ` [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request Chris Boot
2012-02-15 14:47 ` [PATCH v2 02/11] firewire: Move fw_card kref functions into linux/firewire.h Chris Boot
@ 2012-02-15 14:47 ` Chris Boot
2012-02-15 14:47 ` [PATCH v2 04/11] firewire-sbp-target: Add sbp_base.h header Chris Boot
` (8 subsequent siblings)
11 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-15 14:47 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection
as a SCSI transport. This module uses the SCSI Target framework to
expose LUNs to other machines attached to a FireWire bus, in effect
acting as a FireWire hard disk similar to FireWire Target Disk mode on
many Apple computers.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/Kconfig | 13 +++++++++++++
drivers/target/sbp/Makefile | 11 +++++++++++
drivers/target/sbp/TODO | 7 +++++++
3 files changed, 31 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/Kconfig
create mode 100644 drivers/target/sbp/Makefile
create mode 100644 drivers/target/sbp/TODO
diff --git a/drivers/target/sbp/Kconfig b/drivers/target/sbp/Kconfig
new file mode 100644
index 0000000..c9f85e9
--- /dev/null
+++ b/drivers/target/sbp/Kconfig
@@ -0,0 +1,13 @@
+
+config FIREWIRE_SBP_TARGET
+ tristate "FireWire SBP-2 fabric module"
+ depends on FIREWIRE && EXPERIMENTAL
+ help
+ Say Y or M here to enable SCSI target functionality over FireWire.
+ This enables you to expose SCSI devices to other nodes on the FireWire
+ bus, for example hard disks. Similar to FireWire Target Disk mode on
+ many Apple computers.
+
+ To compile this driver as a module, say M here: The module will be
+ called firewire-sbp-target.
+
diff --git a/drivers/target/sbp/Makefile b/drivers/target/sbp/Makefile
new file mode 100644
index 0000000..c8f73f4
--- /dev/null
+++ b/drivers/target/sbp/Makefile
@@ -0,0 +1,11 @@
+
+firewire-sbp-target-y += \
+ sbp_configfs.o \
+ sbp_fabric.o \
+ sbp_login.o \
+ sbp_management_agent.o \
+ sbp_scsi_cmnd.o \
+ sbp_target_agent.o
+
+obj-$(CONFIG_FIREWIRE_SBP_TARGET) += firewire-sbp-target.o
+
diff --git a/drivers/target/sbp/TODO b/drivers/target/sbp/TODO
new file mode 100644
index 0000000..eaec1c9
--- /dev/null
+++ b/drivers/target/sbp/TODO
@@ -0,0 +1,7 @@
+* Force-terminate sessions when disabling targets
+* Ability to have several SCSI commands in-flight (TCQ?)
+* Retry failed FireWire transactions a few times with exponential backoff
+* Take into account the page_size field for transfers and/or page tables
+* Handle descriptor format sense data
+* Implement QUERY LOGINS management ORB
+* Implement TASK MANAGEMENT functionality
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH v2 04/11] firewire-sbp-target: Add sbp_base.h header
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
` (2 preceding siblings ...)
2012-02-15 14:47 ` [PATCH v2 03/11] firewire-sbp-target: Add Kconfig, Makefile and TODO Chris Boot
@ 2012-02-15 14:47 ` Chris Boot
2012-02-15 19:15 ` Stefan Richter
2012-02-15 14:47 ` [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c Chris Boot
` (7 subsequent siblings)
11 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-15 14:47 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This header contains defines and structures that are common to many of
the modules of the target code. This includes SBP-2 protocol structures
and constants as well as a few structs for setting up the target, LUN
login information and session setup.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_base.h | 204 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 204 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_base.h
diff --git a/drivers/target/sbp/sbp_base.h b/drivers/target/sbp/sbp_base.h
new file mode 100644
index 0000000..d39b743
--- /dev/null
+++ b/drivers/target/sbp/sbp_base.h
@@ -0,0 +1,204 @@
+
+#define SBP_VERSION "v0.1"
+#define SBP_NAMELEN 32
+
+#define SBP_ORB_FETCH_SIZE 8
+
+#define MANAGEMENT_AGENT_STATE_IDLE 0
+#define MANAGEMENT_AGENT_STATE_BUSY 1
+
+#define ORB_NOTIFY(v) (((v) >> 31) & 0x01)
+#define ORB_REQUEST_FORMAT(v) (((v) >> 29) & 0x03)
+
+#define MANAGEMENT_ORB_FUNCTION(v) (((v) >> 16) & 0x0f)
+
+#define MANAGEMENT_ORB_FUNCTION_LOGIN 0x0
+#define MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS 0x1
+#define MANAGEMENT_ORB_FUNCTION_RECONNECT 0x3
+#define MANAGEMENT_ORB_FUNCTION_SET_PASSWORD 0x4
+#define MANAGEMENT_ORB_FUNCTION_LOGOUT 0x7
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK 0xb
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET 0xc
+#define MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET 0xe
+#define MANAGEMENT_ORB_FUNCTION_TARGET_RESET 0xf
+
+#define LOGIN_ORB_EXCLUSIVE(v) (((v) >> 28) & 0x01)
+#define LOGIN_ORB_RESERVED(v) (((v) >> 24) & 0x0f)
+#define LOGIN_ORB_RECONNECT(v) (((v) >> 20) & 0x0f)
+#define LOGIN_ORB_LUN(v) (((v) >> 0) & 0xffff)
+#define LOGIN_ORB_PASSWORD_LENGTH(v) (((v) >> 16) & 0xffff)
+#define LOGIN_ORB_RESPONSE_LENGTH(v) (((v) >> 0) & 0xffff)
+
+#define RECONNECT_ORB_LOGIN_ID(v) (((v) >> 0) & 0xffff)
+#define LOGOUT_ORB_LOGIN_ID(v) (((v) >> 0) & 0xffff)
+
+#define CMDBLK_ORB_DIRECTION(v) (((v) >> 27) & 0x01)
+#define CMDBLK_ORB_SPEED(v) (((v) >> 24) & 0x07)
+#define CMDBLK_ORB_MAX_PAYLOAD(v) (((v) >> 20) & 0x0f)
+#define CMDBLK_ORB_PG_TBL_PRESENT(v) (((v) >> 19) & 0x01)
+#define CMDBLK_ORB_PG_SIZE(v) (((v) >> 16) & 0x07)
+#define CMDBLK_ORB_DATA_SIZE(v) (((v) >> 0) & 0xffff)
+
+#define STATUS_BLOCK_SRC(v) (((v) & 0x03) << 30)
+#define STATUS_BLOCK_RESP(v) (((v) & 0x03) << 28)
+#define STATUS_BLOCK_DEAD(v) (((v) ? 1 : 0) << 27)
+#define STATUS_BLOCK_LEN(v) (((v) & 0x07) << 24)
+#define STATUS_BLOCK_SBP_STATUS(v) (((v) & 0xff) << 16)
+#define STATUS_BLOCK_ORB_OFFSET_HIGH(v) (((v) & 0xffff) << 0)
+
+#define STATUS_SRC_ORB_CONTINUING 0
+#define STATUS_SRC_ORB_FINISHED 1
+#define STATUS_SRC_UNSOLICITED 2
+
+#define STATUS_RESP_REQUEST_COMPLETE 0
+#define STATUS_RESP_TRANSPORT_FAILURE 1
+#define STATUS_RESP_ILLEGAL_REQUEST 2
+#define STATUS_RESP_VENDOR_DEPENDENT 3
+
+#define SBP_STATUS_OK 0
+#define SBP_STATUS_REQ_TYPE_NOTSUPP 1
+#define SBP_STATUS_SPEED_NOTSUPP 2
+#define SBP_STATUS_PAGE_SIZE_NOTSUPP 3
+#define SBP_STATUS_ACCESS_DENIED 4
+#define SBP_STATUS_LUN_NOTSUPP 5
+#define SBP_STATUS_PAYLOAD_TOO_SMALL 6
+/* 7 is reserved */
+#define SBP_STATUS_RESOURCES_UNAVAIL 8
+#define SBP_STATUS_FUNCTION_REJECTED 9
+#define SBP_STATUS_LOGIN_ID_UNKNOWN 10
+#define SBP_STATUS_DUMMY_ORB_COMPLETE 11
+#define SBP_STATUS_REQUEST_ABORTED 12
+#define SBP_STATUS_UNSPECIFIED_ERROR 0xff
+
+#define AGENT_STATE_RESET 0
+#define AGENT_STATE_ACTIVE 1
+#define AGENT_STATE_SUSPENDED 2
+#define AGENT_STATE_DEAD 3
+
+struct sbp2_pointer {
+ __be32 high;
+ __be32 low;
+};
+
+struct sbp_command_block_orb {
+ struct sbp2_pointer next_orb;
+ struct sbp2_pointer data_descriptor;
+ __be32 misc;
+ u8 command_block[12];
+};
+
+struct sbp_page_table_entry {
+ __be16 segment_length;
+ __be16 segment_base_hi;
+ __be32 segment_base_lo;
+};
+
+struct sbp_management_orb {
+ struct sbp2_pointer ptr1;
+ struct sbp2_pointer ptr2;
+ __be32 misc;
+ __be32 length;
+ struct sbp2_pointer status_fifo;
+};
+
+struct sbp_status_block {
+ __be32 status;
+ __be32 orb_low;
+ u8 data[24];
+};
+
+struct sbp_login_response_block {
+ __be32 misc;
+ struct sbp2_pointer command_block_agent;
+ __be32 reconnect_hold;
+};
+
+struct sbp_login_descriptor {
+ struct sbp_session *sess;
+ struct list_head link;
+
+ struct se_lun *lun;
+
+ u64 status_fifo_addr;
+ int exclusive;
+ u16 login_id;
+ atomic_t unsolicited_status_enable;
+
+ struct sbp_target_agent *tgt_agt;
+};
+
+struct sbp_session {
+ struct se_session *se_sess;
+ struct list_head login_list;
+ spinlock_t login_list_lock;
+ struct delayed_work maint_work;
+
+ u64 guid; /* login_owner_EUI_64 */
+ int node_id; /* login_owner_ID */
+
+ struct fw_card *card;
+ int generation;
+ int speed;
+
+ int reconnect_hold;
+ u64 reconnect_expires;
+};
+
+struct sbp_nacl {
+ /* Initiator EUI-64 */
+ u64 guid;
+ /* ASCII formatted GUID for SBP Initiator port */
+ char iport_name[SBP_NAMELEN];
+ /* Returned by sbp_make_nodeacl() */
+ struct se_node_acl se_node_acl;
+};
+
+struct sbp_tpg {
+ /* Target portal group tag for TCM */
+ u16 tport_tpgt;
+ /* Pointer back to sbp_tport */
+ struct sbp_tport *tport;
+ /* Returned by sbp_make_tpg() */
+ struct se_portal_group se_tpg;
+};
+
+struct sbp_tport {
+ /* Target Unit Identifier (EUI-64) */
+ u64 guid;
+ /* Target port name */
+ char tport_name[SBP_NAMELEN];
+ /* Returned by sbp_make_tport() */
+ struct se_wwn tport_wwn;
+
+ struct sbp_tpg *tpg;
+
+ /* FireWire unit directory */
+ struct fw_descriptor unit_directory;
+
+ /* SBP Management Agent */
+ struct sbp_management_agent *mgt_agt;
+
+ /* Parameters */
+ int enable;
+ s32 directory_id;
+ int mgt_orb_timeout;
+ int max_reconnect_timeout;
+ int max_logins_per_lun;
+};
+
+extern struct target_fabric_configfs *sbp_fabric_configfs;
+extern const struct fw_address_region sbp_register_region;
+extern struct workqueue_struct *sbp_workqueue;
+
+static inline u64 sbp2_pointer_to_addr(const struct sbp2_pointer *ptr)
+{
+ return (u64)(be32_to_cpu(ptr->high) & 0x0000ffff) << 32 |
+ (be32_to_cpu(ptr->low) & 0xfffffffc);
+}
+
+static inline void addr_to_sbp2_pointer(u64 addr, struct sbp2_pointer *ptr)
+{
+ ptr->high = cpu_to_be32(addr >> 32);
+ ptr->low = cpu_to_be32(addr);
+}
+
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH v2 04/11] firewire-sbp-target: Add sbp_base.h header
2012-02-15 14:47 ` [PATCH v2 04/11] firewire-sbp-target: Add sbp_base.h header Chris Boot
@ 2012-02-15 19:15 ` Stefan Richter
2012-02-16 9:55 ` Chris Boot
0 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-02-15 19:15 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 15 Chris Boot wrote:
> --- /dev/null
> +++ b/drivers/target/sbp/sbp_base.h
> @@ -0,0 +1,204 @@
[...]
> +struct sbp2_pointer {
> + __be32 high;
> + __be32 low;
> +};
[...]
> +struct sbp_tport {
> + /* Target Unit Identifier (EUI-64) */
> + u64 guid;
> + /* Target port name */
> + char tport_name[SBP_NAMELEN];
> + /* Returned by sbp_make_tport() */
> + struct se_wwn tport_wwn;
> +
> + struct sbp_tpg *tpg;
> +
> + /* FireWire unit directory */
> + struct fw_descriptor unit_directory;
> +
> + /* SBP Management Agent */
> + struct sbp_management_agent *mgt_agt;
> +
> + /* Parameters */
> + int enable;
> + s32 directory_id;
> + int mgt_orb_timeout;
> + int max_reconnect_timeout;
> + int max_logins_per_lun;
> +};
> +
> +extern struct target_fabric_configfs *sbp_fabric_configfs;
> +extern const struct fw_address_region sbp_register_region;
> +extern struct workqueue_struct *sbp_workqueue;
[...]
Generally, any source file (.c or .h) is meant to be self-contained WRT
type definitions etc., meaning that they should have all required
#include's in themselves rather than rely on indirect includes.
So here you should include the headers which define __be32, u64, struct
se_wwn, struct workqueue and so on.
--
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 04/11] firewire-sbp-target: Add sbp_base.h header
2012-02-15 19:15 ` Stefan Richter
@ 2012-02-16 9:55 ` Chris Boot
0 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-16 9:55 UTC (permalink / raw)
To: Stefan Richter
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On 15/02/2012 19:15, Stefan Richter wrote:
> On Feb 15 Chris Boot wrote:
>> --- /dev/null
>> +++ b/drivers/target/sbp/sbp_base.h
>> @@ -0,0 +1,204 @@
> [...]
>> +struct sbp2_pointer {
>> + __be32 high;
>> + __be32 low;
>> +};
> [...]
>> +struct sbp_tport {
>> + /* Target Unit Identifier (EUI-64) */
>> + u64 guid;
>> + /* Target port name */
>> + char tport_name[SBP_NAMELEN];
>> + /* Returned by sbp_make_tport() */
>> + struct se_wwn tport_wwn;
>> +
>> + struct sbp_tpg *tpg;
>> +
>> + /* FireWire unit directory */
>> + struct fw_descriptor unit_directory;
>> +
>> + /* SBP Management Agent */
>> + struct sbp_management_agent *mgt_agt;
>> +
>> + /* Parameters */
>> + int enable;
>> + s32 directory_id;
>> + int mgt_orb_timeout;
>> + int max_reconnect_timeout;
>> + int max_logins_per_lun;
>> +};
>> +
>> +extern struct target_fabric_configfs *sbp_fabric_configfs;
>> +extern const struct fw_address_region sbp_register_region;
>> +extern struct workqueue_struct *sbp_workqueue;
> [...]
>
> Generally, any source file (.c or .h) is meant to be self-contained WRT
> type definitions etc., meaning that they should have all required
> #include's in themselves rather than rely on indirect includes.
>
> So here you should include the headers which define __be32, u64, struct
> se_wwn, struct workqueue and so on.
Thanks. I'll work on fixing this.
Cheers,
Chris
--
Chris Boot
bootc@bootc.net
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
` (3 preceding siblings ...)
2012-02-15 14:47 ` [PATCH v2 04/11] firewire-sbp-target: Add sbp_base.h header Chris Boot
@ 2012-02-15 14:47 ` Chris Boot
2012-02-15 19:21 ` Stefan Richter
2012-02-15 14:47 ` [PATCH v2 06/11] firewire-sbp-target: Add sbp_fabric.{c,h} Chris Boot
` (6 subsequent siblings)
11 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-15 14:47 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This is used to glue the target framework's configfs code to the target
code, and what is used to create targets and link them to LUNs to
export. The code to create the FireWire unit directory to advertise
targets on the FireWire bus is also in here.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_configfs.c | 751 +++++++++++++++++++++++++++++++++++++
1 files changed, 751 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_configfs.c
diff --git a/drivers/target/sbp/sbp_configfs.c b/drivers/target/sbp/sbp_configfs.c
new file mode 100644
index 0000000..f4a6b36
--- /dev/null
+++ b/drivers/target/sbp/sbp_configfs.c
@@ -0,0 +1,751 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_management_agent.h"
+
+/* Local pointer to allocated TCM configfs fabric module */
+struct target_fabric_configfs *sbp_fabric_configfs;
+
+struct workqueue_struct *sbp_workqueue;
+
+/* FireWire address region for management and command block address handlers */
+const struct fw_address_region sbp_register_region = {
+ .start = CSR_REGISTER_BASE + 0x10000,
+ .end = 0x1000000000000ULL,
+};
+
+static const u32 sbp_unit_directory_template[] = {
+ 0x1200609e, /* unit_specifier_id: NCITS/T10 */
+ 0x13010483, /* unit_sw_version: 1155D Rev 4 */
+ 0x3800609e, /* command_set_specifier_id: NCITS/T10 */
+ 0x390104d8, /* command_set: SPC-2 */
+ 0x3b000000, /* command_set_revision: 0 */
+ 0x3c000001, /* firmware_revision: 1 */
+};
+
+static int sbp_count_se_tpg_luns(struct se_portal_group *tpg)
+{
+ int i, count = 0;
+
+ spin_lock(&tpg->tpg_lun_lock);
+ for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+ struct se_lun *se_lun = &tpg->tpg_lun_list[i];
+
+ if (se_lun->lun_status == TRANSPORT_LUN_STATUS_FREE)
+ continue;
+
+ count++;
+ }
+ spin_unlock(&tpg->tpg_lun_lock);
+
+ return count;
+}
+
+static int sbp_update_unit_directory(struct sbp_tport *tport)
+{
+ int num_luns, num_entries, idx = 0, mgt_agt_addr, ret, i;
+ u32 *data;
+
+ if (tport->unit_directory.data) {
+ fw_core_remove_descriptor(&tport->unit_directory);
+ kfree(tport->unit_directory.data);
+ tport->unit_directory.data = NULL;
+ }
+
+ if (!tport->enable || !tport->tpg)
+ return 0;
+
+ num_luns = sbp_count_se_tpg_luns(&tport->tpg->se_tpg);
+
+ /*
+ * Number of entries in the final unit directory:
+ * - all of those in the template
+ * - management_agent
+ * - unit_characteristics
+ * - reconnect_timeout
+ * - unit unique ID
+ * - one for each LUN
+ *
+ * MUST NOT include leaf or sub-directory entries
+ */
+ num_entries = ARRAY_SIZE(sbp_unit_directory_template) + 4 + num_luns;
+
+ if (tport->directory_id != -1)
+ num_entries++;
+
+ /* allocate num_entries + 4 for the header and unique ID leaf */
+ data = kcalloc((num_entries + 4), sizeof(u32), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* directory_length */
+ data[idx++] = num_entries << 16;
+
+ /* directory_id */
+ if (tport->directory_id != -1)
+ data[idx++] = (CSR_DIRECTORY_ID << 24) | tport->directory_id;
+
+ /* unit directory template */
+ memcpy(&data[idx], sbp_unit_directory_template,
+ sizeof(sbp_unit_directory_template));
+ idx += ARRAY_SIZE(sbp_unit_directory_template);
+
+ /* management_agent */
+ mgt_agt_addr = (tport->mgt_agt->handler.offset - CSR_REGISTER_BASE) / 4;
+ data[idx++] = 0x54000000 | (mgt_agt_addr & 0x00ffffff);
+
+ /* unit_characteristics */
+ data[idx++] = 0x3a000000 |
+ (((tport->mgt_orb_timeout * 2) << 8) & 0xff00) |
+ SBP_ORB_FETCH_SIZE;
+
+ /* reconnect_timeout */
+ data[idx++] = 0x3d000000 | (tport->max_reconnect_timeout & 0xffff);
+
+ /* unit unique ID (leaf is just after LUNs) */
+ data[idx++] = 0x8d000000 | (num_luns + 1);
+
+ spin_lock(&tport->tpg->se_tpg.tpg_lun_lock);
+ for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+ struct se_lun *se_lun = &tport->tpg->se_tpg.tpg_lun_list[i];
+ struct se_device *dev;
+ int type;
+
+ if (se_lun->lun_status == TRANSPORT_LUN_STATUS_FREE)
+ continue;
+
+ spin_unlock(&tport->tpg->se_tpg.tpg_lun_lock);
+
+ dev = se_lun->lun_se_dev;
+ type = dev->transport->get_device_type(dev);
+
+ /* logical_unit_number */
+ data[idx++] = 0x14000000 |
+ ((type << 16) & 0x1f0000) |
+ (se_lun->unpacked_lun & 0xffff);
+
+ spin_lock(&tport->tpg->se_tpg.tpg_lun_lock);
+ }
+ spin_unlock(&tport->tpg->se_tpg.tpg_lun_lock);
+
+ /* unit unique ID leaf */
+ data[idx++] = 2 << 16;
+ data[idx++] = tport->guid >> 32;
+ data[idx++] = tport->guid;
+
+ tport->unit_directory.length = idx;
+ tport->unit_directory.key = (CSR_DIRECTORY | CSR_UNIT) << 24;
+ tport->unit_directory.data = data;
+
+ ret = fw_core_add_descriptor(&tport->unit_directory);
+ if (ret < 0) {
+ kfree(tport->unit_directory.data);
+ tport->unit_directory.data = NULL;
+ }
+
+ return ret;
+}
+
+static ssize_t sbp_parse_wwn(const char *name, u64 *wwn, int strict)
+{
+ const char *cp;
+ char c, nibble;
+ int pos = 0, err;
+
+ *wwn = 0;
+ for (cp = name; cp < &name[SBP_NAMELEN - 1]; cp++) {
+ c = *cp;
+ if (c == '\n' && cp[1] == '\0')
+ continue;
+ if (c == '\0') {
+ err = 2;
+ if (pos != 16)
+ goto fail;
+ return cp - name;
+ }
+ err = 3;
+ if (isdigit(c))
+ nibble = c - '0';
+ else if (isxdigit(c) && (islower(c) || !strict))
+ nibble = tolower(c) - 'a' + 10;
+ else
+ goto fail;
+ *wwn = (*wwn << 4) | nibble;
+ pos++;
+ }
+ err = 4;
+fail:
+ printk(KERN_INFO "err %u len %zu pos %u\n",
+ err, cp - name, pos);
+ return -1;
+}
+
+static ssize_t sbp_format_wwn(char *buf, size_t len, u64 wwn)
+{
+ return snprintf(buf, len, "%016llx", wwn);
+}
+
+static struct se_node_acl *sbp_make_nodeacl(
+ struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
+{
+ struct se_node_acl *se_nacl, *se_nacl_new;
+ struct sbp_nacl *nacl;
+ u64 guid = 0;
+ u32 nexus_depth = 1;
+
+ if (sbp_parse_wwn(name, &guid, 1) < 0)
+ return ERR_PTR(-EINVAL);
+
+ se_nacl_new = sbp_alloc_fabric_acl(se_tpg);
+ if (!se_nacl_new)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+ * when converting a NodeACL from demo mode -> explict
+ */
+ se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+ name, nexus_depth);
+ if (IS_ERR(se_nacl)) {
+ sbp_release_fabric_acl(se_tpg, se_nacl_new);
+ return se_nacl;
+ }
+
+ nacl = container_of(se_nacl, struct sbp_nacl, se_node_acl);
+ nacl->guid = guid;
+ sbp_format_wwn(nacl->iport_name, SBP_NAMELEN, guid);
+
+ return se_nacl;
+}
+
+static void sbp_drop_nodeacl(struct se_node_acl *se_acl)
+{
+ struct sbp_nacl *nacl =
+ container_of(se_acl, struct sbp_nacl, se_node_acl);
+
+ core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+ kfree(nacl);
+}
+
+static int sbp_post_link_lun(
+ struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+
+ return sbp_update_unit_directory(tpg->tport);
+}
+
+static void sbp_pre_unlink_lun(
+ struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ int ret;
+
+ if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0)
+ tport->enable = 0;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0)
+ pr_err("unlink LUN: failed to update unit directory\n");
+}
+
+static struct se_portal_group *sbp_make_tpg(
+ struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct sbp_tport *tport =
+ container_of(wwn, struct sbp_tport, tport_wwn);
+
+ struct sbp_tpg *tpg;
+ unsigned long tpgt;
+ int ret;
+
+ if (strstr(name, "tpgt_") != name)
+ return ERR_PTR(-EINVAL);
+ if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
+ return ERR_PTR(-EINVAL);
+
+ if (tport->tpg) {
+ pr_err("Only one TPG per Unit is possible.\n");
+ return ERR_PTR(-EBUSY);
+ }
+
+ tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
+ if (!tpg) {
+ pr_err("Unable to allocate struct sbp_tpg\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ tpg->tport = tport;
+ tpg->tport_tpgt = tpgt;
+ tport->tpg = tpg;
+
+ /* default attribute values */
+ tport->enable = 0;
+ tport->directory_id = -1;
+ tport->mgt_orb_timeout = 15;
+ tport->max_reconnect_timeout = 5;
+ tport->max_logins_per_lun = 1;
+
+ tport->mgt_agt = sbp_management_agent_register(tport);
+ if (IS_ERR(tport->mgt_agt)) {
+ ret = PTR_ERR(tport->mgt_agt);
+ kfree(tpg);
+ return ERR_PTR(ret);
+ }
+
+ ret = core_tpg_register(&sbp_fabric_configfs->tf_ops, wwn,
+ &tpg->se_tpg, (void *)tpg,
+ TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0) {
+ sbp_management_agent_unregister(tport->mgt_agt);
+ kfree(tpg);
+ return ERR_PTR(ret);
+ }
+
+ return &tpg->se_tpg;
+}
+
+static void sbp_drop_tpg(struct se_portal_group *se_tpg)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+
+ core_tpg_deregister(se_tpg);
+ sbp_management_agent_unregister(tport->mgt_agt);
+ tport->tpg = NULL;
+ kfree(tpg);
+}
+
+static struct se_wwn *sbp_make_tport(
+ struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct sbp_tport *tport;
+ u64 guid = 0;
+
+ if (sbp_parse_wwn(name, &guid, 1) < 0)
+ return ERR_PTR(-EINVAL);
+
+ tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+ if (!tport) {
+ pr_err("Unable to allocate struct sbp_tport\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ tport->guid = guid;
+ sbp_format_wwn(tport->tport_name, SBP_NAMELEN, guid);
+
+ return &tport->tport_wwn;
+}
+
+static void sbp_drop_tport(struct se_wwn *wwn)
+{
+ struct sbp_tport *tport =
+ container_of(wwn, struct sbp_tport, tport_wwn);
+
+ kfree(tport);
+}
+
+static ssize_t sbp_wwn_show_attr_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "FireWire SBP fabric module %s\n", SBP_VERSION);
+}
+
+TF_WWN_ATTR_RO(sbp, version);
+
+static struct configfs_attribute *sbp_wwn_attrs[] = {
+ &sbp_wwn_version.attr,
+ NULL,
+};
+
+static ssize_t sbp_tpg_show_directory_id(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+
+ if (tport->directory_id == -1)
+ return sprintf(page, "implicit\n");
+ else
+ return sprintf(page, "%06x\n", tport->directory_id);
+}
+
+static ssize_t sbp_tpg_store_directory_id(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+
+ if (tport->enable) {
+ pr_err("Cannot change the directory_id on an active target.\n");
+ return -EBUSY;
+ }
+
+ if (strstr(page, "implicit") == page) {
+ tport->directory_id = -1;
+ } else {
+ if (kstrtoul(page, 16, &val) < 0)
+ return -EINVAL;
+ if (val > 0xffffff)
+ return -EINVAL;
+
+ tport->directory_id = val;
+ }
+
+ return count;
+}
+
+static ssize_t sbp_tpg_show_enable(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->enable);
+}
+
+static ssize_t sbp_tpg_store_enable(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val != 0) && (val != 1))
+ return -EINVAL;
+
+ if (tport->enable == val)
+ return count;
+
+ if (val) {
+ if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0) {
+ pr_err("Cannot enable a target with no LUNs!\n");
+ return -EINVAL;
+ }
+ } else {
+ /* XXX: force-shutdown sessions instead? */
+ spin_lock(&se_tpg->session_lock);
+ if (!list_empty(&se_tpg->tpg_sess_list)) {
+ spin_unlock(&se_tpg->session_lock);
+ return -EBUSY;
+ }
+ spin_unlock(&se_tpg->session_lock);
+ }
+
+ tport->enable = val;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0) {
+ pr_err("Could not update Config ROM\n");
+ return ret;
+ }
+
+ return count;
+}
+
+TF_TPG_BASE_ATTR(sbp, directory_id, S_IRUGO | S_IWUSR);
+TF_TPG_BASE_ATTR(sbp, enable, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_base_attrs[] = {
+ &sbp_tpg_directory_id.attr,
+ &sbp_tpg_enable.attr,
+ NULL,
+};
+
+static ssize_t sbp_tpg_attrib_show_mgt_orb_timeout(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->mgt_orb_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_mgt_orb_timeout(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val < 1) || (val > 127))
+ return -EINVAL;
+
+ if (tport->mgt_orb_timeout == val)
+ return count;
+
+ tport->mgt_orb_timeout = val;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_reconnect_timeout(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->max_reconnect_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_reconnect_timeout(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val < 1) || (val > 32767))
+ return -EINVAL;
+
+ if (tport->max_reconnect_timeout == val)
+ return count;
+
+ tport->max_reconnect_timeout = val;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_logins_per_lun(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->max_logins_per_lun);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_logins_per_lun(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val < 1) || (val > 127))
+ return -EINVAL;
+
+ /* XXX: also check against current count? */
+
+ tport->max_logins_per_lun = val;
+
+ return count;
+}
+
+TF_TPG_ATTRIB_ATTR(sbp, mgt_orb_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_reconnect_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_logins_per_lun, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_attrib_attrs[] = {
+ &sbp_tpg_attrib_mgt_orb_timeout.attr,
+ &sbp_tpg_attrib_max_reconnect_timeout.attr,
+ &sbp_tpg_attrib_max_logins_per_lun.attr,
+ NULL,
+};
+
+static struct target_core_fabric_ops sbp_ops = {
+ .get_fabric_name = sbp_get_fabric_name,
+ .get_fabric_proto_ident = sbp_get_fabric_proto_ident,
+ .tpg_get_wwn = sbp_get_fabric_wwn,
+ .tpg_get_tag = sbp_get_tag,
+ .tpg_get_default_depth = sbp_get_default_depth,
+ .tpg_get_pr_transport_id = sbp_get_pr_transport_id,
+ .tpg_get_pr_transport_id_len = sbp_get_pr_transport_id_len,
+ .tpg_parse_pr_out_transport_id = sbp_parse_pr_out_transport_id,
+ .tpg_check_demo_mode = sbp_check_true,
+ .tpg_check_demo_mode_cache = sbp_check_true,
+ .tpg_check_demo_mode_write_protect = sbp_check_false,
+ .tpg_check_prod_mode_write_protect = sbp_check_false,
+ .tpg_alloc_fabric_acl = sbp_alloc_fabric_acl,
+ .tpg_release_fabric_acl = sbp_release_fabric_acl,
+ .tpg_get_inst_index = sbp_tpg_get_inst_index,
+ .release_cmd = sbp_release_cmd,
+ .shutdown_session = sbp_shutdown_session,
+ .close_session = sbp_close_session,
+ .stop_session = sbp_stop_session,
+ .fall_back_to_erl0 = sbp_reset_nexus,
+ .sess_logged_in = sbp_sess_logged_in,
+ .sess_get_index = sbp_sess_get_index,
+ .write_pending = sbp_write_pending,
+ .write_pending_status = sbp_write_pending_status,
+ .set_default_node_attributes = sbp_set_default_node_attrs,
+ .get_task_tag = sbp_get_task_tag,
+ .get_cmd_state = sbp_get_cmd_state,
+ .queue_data_in = sbp_queue_data_in,
+ .queue_status = sbp_queue_status,
+ .queue_tm_rsp = sbp_queue_tm_rsp,
+ .get_fabric_sense_len = sbp_get_fabric_sense_len,
+ .set_fabric_sense_len = sbp_set_fabric_sense_len,
+ .is_state_remove = sbp_is_state_remove,
+ .check_stop_free = sbp_check_stop_free,
+
+ .fabric_make_wwn = sbp_make_tport,
+ .fabric_drop_wwn = sbp_drop_tport,
+ .fabric_make_tpg = sbp_make_tpg,
+ .fabric_drop_tpg = sbp_drop_tpg,
+ .fabric_post_link = sbp_post_link_lun,
+ .fabric_pre_unlink = sbp_pre_unlink_lun,
+ .fabric_make_np = NULL,
+ .fabric_drop_np = NULL,
+ .fabric_make_nodeacl = sbp_make_nodeacl,
+ .fabric_drop_nodeacl = sbp_drop_nodeacl,
+};
+
+static int sbp_register_configfs(void)
+{
+ struct target_fabric_configfs *fabric;
+ int ret;
+
+ fabric = target_fabric_configfs_init(THIS_MODULE, "sbp");
+ if (!fabric) {
+ pr_err("target_fabric_configfs_init() failed\n");
+ return -ENOMEM;
+ }
+
+ fabric->tf_ops = sbp_ops;
+
+ /*
+ * Setup default attribute lists for various fabric->tf_cit_tmpl
+ */
+ TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = sbp_wwn_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = sbp_tpg_base_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = sbp_tpg_attrib_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+
+ ret = target_fabric_configfs_register(fabric);
+ if (ret < 0) {
+ pr_err("target_fabric_configfs_register() failed for SBP\n");
+ return ret;
+ }
+
+ sbp_workqueue = alloc_workqueue("firewire-sbp-target", WQ_UNBOUND, 0);
+ if (!sbp_workqueue) {
+ target_fabric_configfs_deregister(fabric);
+ return -ENOMEM;
+ }
+
+ sbp_fabric_configfs = fabric;
+
+ return 0;
+};
+
+static void sbp_deregister_configfs(void)
+{
+ if (!sbp_fabric_configfs)
+ return;
+
+ target_fabric_configfs_deregister(sbp_fabric_configfs);
+ destroy_workqueue(sbp_workqueue);
+ sbp_fabric_configfs = NULL;
+};
+
+static int __init sbp_init(void)
+{
+ int ret;
+
+ ret = sbp_register_configfs();
+ if (ret < 0)
+ return ret;
+
+ return 0;
+};
+
+static void sbp_exit(void)
+{
+ sbp_deregister_configfs();
+};
+
+MODULE_DESCRIPTION("FireWire SBP fabric driver");
+MODULE_LICENSE("GPL");
+module_init(sbp_init);
+module_exit(sbp_exit);
+
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c
2012-02-15 14:47 ` [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c Chris Boot
@ 2012-02-15 19:21 ` Stefan Richter
2012-02-16 9:57 ` Chris Boot
0 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-02-15 19:21 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 15 Chris Boot wrote:
> --- /dev/null
> +++ b/drivers/target/sbp/sbp_configfs.c
> @@ -0,0 +1,751 @@
> +/*
> + * SBP2 target driver (SCSI over IEEE1394 in target mode)
> + *
> + * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software Foundation,
> + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + */
> +
> +#define KMSG_COMPONENT "sbp_target"
> +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
> +
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/version.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/kthread.h>
> +#include <linux/types.h>
> +#include <linux/string.h>
> +#include <linux/configfs.h>
> +#include <linux/ctype.h>
> +#include <linux/firewire.h>
> +
> +#include <asm/unaligned.h>
> +
> +#include <target/target_core_base.h>
> +#include <target/target_core_backend.h>
> +#include <target/target_core_fabric.h>
> +#include <target/target_core_configfs.h>
> +#include <target/target_core_fabric_configfs.h>
> +#include <target/configfs_macros.h>
> +
> +#include "sbp_base.h"
> +#include "sbp_fabric.h"
> +#include "sbp_management_agent.h"
> +
> +/* Local pointer to allocated TCM configfs fabric module */
> +struct target_fabric_configfs *sbp_fabric_configfs;
> +
> +struct workqueue_struct *sbp_workqueue;
#include <linux/workqueue.h> goes before this.
[...]
> + sbp_workqueue = alloc_workqueue("firewire-sbp-target", WQ_UNBOUND, 0);
> + if (!sbp_workqueue) {
> + target_fabric_configfs_deregister(fabric);
> + return -ENOMEM;
> + }
What are your specific requirements that you cannot use one of the
system-wide workqueues?
--
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c
2012-02-15 19:21 ` Stefan Richter
@ 2012-02-16 9:57 ` Chris Boot
2012-02-16 13:48 ` Stefan Richter
0 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-16 9:57 UTC (permalink / raw)
To: Stefan Richter
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On 15/02/2012 19:21, Stefan Richter wrote:
> On Feb 15 Chris Boot wrote:
>> --- /dev/null
>> +++ b/drivers/target/sbp/sbp_configfs.c
>> @@ -0,0 +1,751 @@
>> +/*
>> + * SBP2 target driver (SCSI over IEEE1394 in target mode)
>> + *
>> + * Copyright (C) 2011 Chris Boot<bootc@bootc.net>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software Foundation,
>> + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
>> + */
>> +
>> +#define KMSG_COMPONENT "sbp_target"
>> +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
>> +
>> +#include<linux/module.h>
>> +#include<linux/moduleparam.h>
>> +#include<linux/version.h>
>> +#include<linux/init.h>
>> +#include<linux/slab.h>
>> +#include<linux/kthread.h>
>> +#include<linux/types.h>
>> +#include<linux/string.h>
>> +#include<linux/configfs.h>
>> +#include<linux/ctype.h>
>> +#include<linux/firewire.h>
>> +
>> +#include<asm/unaligned.h>
>> +
>> +#include<target/target_core_base.h>
>> +#include<target/target_core_backend.h>
>> +#include<target/target_core_fabric.h>
>> +#include<target/target_core_configfs.h>
>> +#include<target/target_core_fabric_configfs.h>
>> +#include<target/configfs_macros.h>
>> +
>> +#include "sbp_base.h"
>> +#include "sbp_fabric.h"
>> +#include "sbp_management_agent.h"
>> +
>> +/* Local pointer to allocated TCM configfs fabric module */
>> +struct target_fabric_configfs *sbp_fabric_configfs;
>> +
>> +struct workqueue_struct *sbp_workqueue;
>
> #include<linux/workqueue.h> goes before this.
Yep.
> [...]
>> + sbp_workqueue = alloc_workqueue("firewire-sbp-target", WQ_UNBOUND, 0);
>> + if (!sbp_workqueue) {
>> + target_fabric_configfs_deregister(fabric);
>> + return -ENOMEM;
>> + }
>
> What are your specific requirements that you cannot use one of the
> system-wide workqueues?
Nothing specific, I just thought it was sensible to use your own
workqueue if you put enough work into it. I'll switch to the system queues.
--
Chris Boot
bootc@bootc.net
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c
2012-02-16 9:57 ` Chris Boot
@ 2012-02-16 13:48 ` Stefan Richter
0 siblings, 0 replies; 104+ messages in thread
From: Stefan Richter @ 2012-02-16 13:48 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 16 Chris Boot wrote:
> On 15/02/2012 19:21, Stefan Richter wrote:
> > On Feb 15 Chris Boot wrote:
> >> + sbp_workqueue = alloc_workqueue("firewire-sbp-target", WQ_UNBOUND, 0);
> >> + if (!sbp_workqueue) {
> >> + target_fabric_configfs_deregister(fabric);
> >> + return -ENOMEM;
> >> + }
> >
> > What are your specific requirements that you cannot use one of the
> > system-wide workqueues?
>
> Nothing specific, I just thought it was sensible to use your own
> workqueue if you put enough work into it. I'll switch to the system queues.
OK, good. These days you can throw almost any kind of work into the
system workqueues without negative effect on their other users, given you
keep the queue properties in mind which are listed in linux/workqueue.h.
There is also some info in Documentation/workqueue.txt. If that still
leaves any doubt, you could Cc: Tejun Hejo on questions on the workqueue
infrastructure and he will likely give a helpful hint.
BTW, the drivers/firewire/ subsystem uses an own workqueue instead of the
system-wide ones because of combined requirements of non-reentrance and
memory-reclaim safety, explained in the changelog of commit 6ea9e7bbfc38.
--
Stefan Richter
-=====-===-- --=- =----
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH v2 06/11] firewire-sbp-target: Add sbp_fabric.{c,h}
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
` (4 preceding siblings ...)
2012-02-15 14:47 ` [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c Chris Boot
@ 2012-02-15 14:47 ` Chris Boot
2012-02-15 14:47 ` [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h} Chris Boot
` (5 subsequent siblings)
11 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-15 14:47 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This serves as further glue between the target framework and SBP-2, in
this case dealing with SCSI command submission and data in/out.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_fabric.c | 353 +++++++++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_fabric.h | 42 +++++
2 files changed, 395 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_fabric.c
create mode 100644 drivers/target/sbp/sbp_fabric.h
diff --git a/drivers/target/sbp/sbp_fabric.c b/drivers/target/sbp/sbp_fabric.c
new file mode 100644
index 0000000..354ef93
--- /dev/null
+++ b/drivers/target/sbp/sbp_fabric.c
@@ -0,0 +1,353 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+int sbp_check_true(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+int sbp_check_false(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+char *sbp_get_fabric_name(void)
+{
+ return "sbp";
+}
+
+char *sbp_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+
+ return &tport->tport_name[0];
+}
+
+u16 sbp_get_tag(struct se_portal_group *se_tpg)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ return tpg->tport_tpgt;
+}
+
+u32 sbp_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+ struct sbp_nacl *nacl;
+
+ nacl = kzalloc(sizeof(struct sbp_nacl), GFP_KERNEL);
+ if (!nacl) {
+ pr_err("Unable to alocate struct sbp_nacl\n");
+ return NULL;
+ }
+
+ return &nacl->se_node_acl;
+}
+
+void sbp_release_fabric_acl(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl)
+{
+ struct sbp_nacl *nacl =
+ container_of(se_nacl, struct sbp_nacl, se_node_acl);
+ kfree(nacl);
+}
+
+u32 sbp_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+void sbp_release_cmd(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ sbp_free_request(req);
+}
+
+int sbp_shutdown_session(struct se_session *se_sess)
+{
+ return 0;
+}
+
+void sbp_close_session(struct se_session *se_sess)
+{
+ return;
+}
+
+void sbp_stop_session(struct se_session *se_sess, int sess_sleep,
+ int conn_sleep)
+{
+ return;
+}
+
+void sbp_reset_nexus(struct se_session *se_sess)
+{
+ return;
+}
+
+int sbp_sess_logged_in(struct se_session *se_sess)
+{
+ return 0;
+}
+
+u32 sbp_sess_get_index(struct se_session *se_sess)
+{
+ return 0;
+}
+
+int sbp_write_pending(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+ int ret;
+
+ req->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
+ if (!req->data_buf)
+ return -ENOMEM;
+
+ ret = sbp_rw_data(req);
+ if (ret) {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ pr_warn("sbp_write_pending: data write error\n");
+ return ret;
+ }
+
+ sg_copy_from_buffer(se_cmd->t_data_sg,
+ se_cmd->t_data_nents,
+ req->data_buf,
+ se_cmd->data_length);
+
+ transport_generic_process_write(se_cmd);
+
+ return 0;
+}
+
+int sbp_write_pending_status(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+void sbp_set_default_node_attrs(struct se_node_acl *nacl)
+{
+ return;
+}
+
+u32 sbp_get_task_tag(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ /* only used for printk until we do TMRs */
+ return (u32)req->orb_pointer;
+}
+
+int sbp_get_cmd_state(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+int sbp_queue_data_in(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+ int ret;
+
+ req->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
+ if (!req->data_buf)
+ return -ENOMEM;
+
+ sg_copy_to_buffer(se_cmd->t_data_sg,
+ se_cmd->t_data_nents,
+ req->data_buf,
+ se_cmd->data_length);
+
+ ret = sbp_rw_data(req);
+ if (ret) {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ return ret;
+ }
+
+ return sbp_send_sense(req);
+}
+
+/*
+ * Called after command (no data transfer) or after the write (to device)
+ * operation is completed
+ */
+int sbp_queue_status(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ return sbp_send_sense(req);
+}
+
+int sbp_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+u16 sbp_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length)
+{
+ return 0;
+}
+
+u16 sbp_get_fabric_sense_len(void)
+{
+ return 0;
+}
+
+int sbp_is_state_remove(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+int sbp_check_stop_free(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ transport_generic_free_cmd(&req->se_cmd, 0);
+ return 1;
+}
+
+/*
+ * Handlers for Serial Bus Protocol 2/3 (SBP-2 / SBP-3)
+ */
+u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+ /*
+ * Return a IEEE 1394 SCSI Protocol identifier for loopback operations
+ * This is defined in section 7.5.1 Table 362 in spc4r17
+ */
+ return SCSI_PROTOCOL_SBP;
+}
+
+u32 sbp_get_pr_transport_id(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code,
+ unsigned char *buf)
+{
+ int ret;
+
+ /*
+ * Set PROTOCOL IDENTIFIER to 3h for SBP
+ */
+ buf[0] = SCSI_PROTOCOL_SBP;
+ /*
+ * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+ * over IEEE 1394
+ */
+ ret = hex2bin(&buf[8], se_nacl->initiatorname, 8);
+ if (ret < 0)
+ pr_debug("sbp transport_id: invalid hex string\n");
+
+ /*
+ * The IEEE 1394 Transport ID is a hardcoded 24-byte length
+ */
+ return 24;
+}
+
+u32 sbp_get_pr_transport_id_len(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code)
+{
+ *format_code = 0;
+ /*
+ * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+ * over IEEE 1394
+ *
+ * The SBP Transport ID is a hardcoded 24-byte length
+ */
+ return 24;
+}
+
+/*
+ * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above
+ * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations.
+ */
+char *sbp_parse_pr_out_transport_id(
+ struct se_portal_group *se_tpg,
+ const char *buf,
+ u32 *out_tid_len,
+ char **port_nexus_ptr)
+{
+ /*
+ * Assume the FORMAT CODE 00b from spc4r17, 7.5.4.4 TransportID
+ * for initiator ports using SCSI over SBP Serial SCSI Protocol
+ *
+ * The TransportID for a IEEE 1394 Initiator Port is of fixed size of
+ * 24 bytes, and IEEE 1394 does not contain a I_T nexus identifier,
+ * so we return the **port_nexus_ptr set to NULL.
+ */
+ *port_nexus_ptr = NULL;
+ *out_tid_len = 24;
+
+ return (char *)&buf[8];
+}
+
diff --git a/drivers/target/sbp/sbp_fabric.h b/drivers/target/sbp/sbp_fabric.h
new file mode 100644
index 0000000..eb757a0
--- /dev/null
+++ b/drivers/target/sbp/sbp_fabric.h
@@ -0,0 +1,42 @@
+
+int sbp_check_true(struct se_portal_group *);
+int sbp_check_false(struct se_portal_group *);
+char *sbp_get_fabric_name(void);
+char *sbp_get_fabric_wwn(struct se_portal_group *);
+u16 sbp_get_tag(struct se_portal_group *);
+u32 sbp_get_default_depth(struct se_portal_group *);
+struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *);
+void sbp_release_fabric_acl(struct se_portal_group *,
+ struct se_node_acl *);
+u32 sbp_tpg_get_inst_index(struct se_portal_group *);
+void sbp_release_cmd(struct se_cmd *se_cmd);
+int sbp_shutdown_session(struct se_session *);
+void sbp_close_session(struct se_session *);
+void sbp_stop_session(struct se_session *, int, int);
+void sbp_reset_nexus(struct se_session *);
+int sbp_sess_logged_in(struct se_session *);
+u32 sbp_sess_get_index(struct se_session *);
+int sbp_write_pending(struct se_cmd *);
+int sbp_write_pending_status(struct se_cmd *);
+void sbp_set_default_node_attrs(struct se_node_acl *);
+u32 sbp_get_task_tag(struct se_cmd *);
+int sbp_get_cmd_state(struct se_cmd *);
+int sbp_queue_data_in(struct se_cmd *);
+int sbp_queue_status(struct se_cmd *);
+int sbp_queue_tm_rsp(struct se_cmd *);
+u16 sbp_set_fabric_sense_len(struct se_cmd *, u32);
+u16 sbp_get_fabric_sense_len(void);
+int sbp_is_state_remove(struct se_cmd *);
+int sbp_check_stop_free(struct se_cmd *se_cmd);
+
+u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg);
+u32 sbp_get_pr_transport_id(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl, struct t10_pr_registration *pr_reg,
+ int *format_code, unsigned char *buf);
+u32 sbp_get_pr_transport_id_len(
+ struct se_portal_group *se_tpg, struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg, int *format_code);
+char *sbp_parse_pr_out_transport_id(
+ struct se_portal_group *se_tpg, const char *buf,
+ u32 *out_tid_len, char **port_nexus_ptr);
+
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h}
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
` (5 preceding siblings ...)
2012-02-15 14:47 ` [PATCH v2 06/11] firewire-sbp-target: Add sbp_fabric.{c,h} Chris Boot
@ 2012-02-15 14:47 ` Chris Boot
2012-02-15 19:48 ` Stefan Richter
2012-02-15 14:47 ` [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h} Chris Boot
` (4 subsequent siblings)
11 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-15 14:47 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This code implements the SBP-2 Management Agent. This is the first of
two firewire address handlers that are used to communicate with the
target. The Management Agent is used to handle login, reconnect and
logout to a SCSI LUN as well as task management functions.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_management_agent.c | 260 +++++++++++++++++++++++++++++
drivers/target/sbp/sbp_management_agent.h | 23 +++
2 files changed, 283 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_management_agent.c
create mode 100644 drivers/target/sbp/sbp_management_agent.h
diff --git a/drivers/target/sbp/sbp_management_agent.c b/drivers/target/sbp/sbp_management_agent.c
new file mode 100644
index 0000000..04e4df8
--- /dev/null
+++ b/drivers/target/sbp/sbp_management_agent.c
@@ -0,0 +1,260 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/kref.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+
+static void sbp_mgt_agent_process(struct work_struct *work)
+{
+ struct sbp_management_agent *agent =
+ container_of(work, struct sbp_management_agent, work);
+ struct sbp_management_request *req = agent->request;
+ int ret;
+ int status_data_len = 0;
+
+ /* fetch the ORB from the initiator */
+ ret = fw_run_transaction(req->card, TCODE_READ_BLOCK_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ agent->orb_offset, &req->orb, sizeof(req->orb));
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("mgt_orb fetch failed: %x\n", ret);
+ goto out;
+ }
+
+ pr_debug("mgt_orb ptr1:0x%llx ptr2:0x%llx misc:0x%x len:0x%x status_fifo:0x%llx\n",
+ sbp2_pointer_to_addr(&req->orb.ptr1),
+ sbp2_pointer_to_addr(&req->orb.ptr2),
+ be32_to_cpu(req->orb.misc), be32_to_cpu(req->orb.length),
+ sbp2_pointer_to_addr(&req->orb.status_fifo));
+
+ if (!ORB_NOTIFY(be32_to_cpu(req->orb.misc)) ||
+ ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc)) != 0) {
+ pr_err("mgt_orb bad request\n");
+ goto out;
+ }
+
+ switch (MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc))) {
+ case MANAGEMENT_ORB_FUNCTION_LOGIN:
+ sbp_management_request_login(agent, req, &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS:
+ sbp_management_request_query_logins(agent, req,
+ &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_RECONNECT:
+ sbp_management_request_reconnect(agent, req, &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_SET_PASSWORD:
+ pr_notice("SET PASSWORD not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_LOGOUT:
+ sbp_management_request_logout(agent, req, &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_ABORT_TASK:
+ pr_notice("ABORT TASK not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET:
+ pr_notice("ABORT TASK SET not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET:
+ pr_notice("LOGICAL UNIT RESET not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_TARGET_RESET:
+ pr_notice("TARGET RESET not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ default:
+ pr_notice("unknown management function 0x%x\n",
+ MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc)));
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+ }
+
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_SRC(1) | /* Response to ORB, next_ORB absent */
+ STATUS_BLOCK_LEN(DIV_ROUND_UP(status_data_len, 4) + 1) |
+ STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_offset >> 32));
+ req->status.orb_low = cpu_to_be32(agent->orb_offset);
+
+ /* write the status block back to the initiator */
+ ret = fw_run_transaction(req->card, TCODE_WRITE_BLOCK_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ sbp2_pointer_to_addr(&req->orb.status_fifo),
+ &req->status, 8 + status_data_len);
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("mgt_orb status write failed: %x\n", ret);
+ goto out;
+ }
+
+out:
+ fw_card_put(req->card);
+ kfree(req);
+ atomic_set(&agent->state, MANAGEMENT_AGENT_STATE_IDLE);
+}
+
+static void sbp_mgt_agent_rw(struct fw_card *card,
+ struct fw_request *request, int tcode, int destination, int source,
+ int generation, unsigned long long offset, void *data, size_t length,
+ void *callback_data)
+{
+ struct sbp_management_agent *agent = callback_data;
+ struct sbp2_pointer *ptr = data;
+
+ if (!agent->tport->enable) {
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ return;
+ }
+
+ if ((offset != agent->handler.offset) || (length != 8)) {
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ return;
+ }
+
+ if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+ struct sbp_management_request *req;
+ int ret;
+
+ smp_wmb();
+ if (atomic_cmpxchg(&agent->state,
+ MANAGEMENT_AGENT_STATE_IDLE,
+ MANAGEMENT_AGENT_STATE_BUSY) !=
+ MANAGEMENT_AGENT_STATE_IDLE) {
+ pr_notice("ignoring management request while busy\n");
+
+ fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+ return;
+ }
+
+ req = kzalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+ return;
+ }
+
+ req->card = fw_card_get(card);
+ req->generation = generation;
+ req->node_addr = source;
+ req->speed = fw_get_request_speed(request);
+
+ agent->orb_offset = sbp2_pointer_to_addr(ptr);
+ agent->request = req;
+
+ ret = queue_work(sbp_workqueue, &agent->work);
+ if (!ret) {
+ /* pretend we're busy */
+ kfree(req);
+ fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+ return;
+ }
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+ } else if (tcode == TCODE_READ_BLOCK_REQUEST) {
+ addr_to_sbp2_pointer(agent->orb_offset, ptr);
+ fw_send_response(card, request, RCODE_COMPLETE);
+ } else {
+ fw_send_response(card, request, RCODE_TYPE_ERROR);
+ }
+}
+
+struct sbp_management_agent *sbp_management_agent_register(
+ struct sbp_tport *tport)
+{
+ int ret;
+ struct sbp_management_agent *agent;
+
+ agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+ if (!agent)
+ return ERR_PTR(-ENOMEM);
+
+ agent->tport = tport;
+ agent->handler.length = 0x08;
+ agent->handler.address_callback = sbp_mgt_agent_rw;
+ agent->handler.callback_data = agent;
+ atomic_set(&agent->state, MANAGEMENT_AGENT_STATE_IDLE);
+ INIT_WORK(&agent->work, sbp_mgt_agent_process);
+ agent->orb_offset = 0;
+ agent->request = NULL;
+
+ ret = fw_core_add_address_handler(&agent->handler,
+ &sbp_register_region);
+ if (ret < 0) {
+ kfree(agent);
+ return ERR_PTR(ret);
+ }
+
+ return agent;
+}
+
+void sbp_management_agent_unregister(struct sbp_management_agent *agent)
+{
+ if (atomic_read(&agent->state) != MANAGEMENT_AGENT_STATE_IDLE)
+ flush_work_sync(&agent->work);
+
+ fw_core_remove_address_handler(&agent->handler);
+ kfree(agent);
+}
+
diff --git a/drivers/target/sbp/sbp_management_agent.h b/drivers/target/sbp/sbp_management_agent.h
new file mode 100644
index 0000000..615b90e
--- /dev/null
+++ b/drivers/target/sbp/sbp_management_agent.h
@@ -0,0 +1,23 @@
+
+struct sbp_management_agent {
+ struct sbp_tport *tport;
+ struct fw_address_handler handler;
+ atomic_t state;
+ struct work_struct work;
+ u64 orb_offset;
+ struct sbp_management_request *request;
+};
+
+struct sbp_management_request {
+ struct sbp_management_orb orb;
+ struct sbp_status_block status;
+ struct fw_card *card;
+ int generation;
+ int node_addr;
+ int speed;
+};
+
+struct sbp_management_agent *sbp_management_agent_register(
+ struct sbp_tport *tport);
+void sbp_management_agent_unregister(struct sbp_management_agent *agent);
+
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h}
2012-02-15 14:47 ` [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h} Chris Boot
@ 2012-02-15 19:48 ` Stefan Richter
2012-02-16 10:28 ` Chris Boot
0 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-02-15 19:48 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 15 Chris Boot wrote:
> --- /dev/null
> +++ b/drivers/target/sbp/sbp_management_agent.c
[...]
> +static void sbp_mgt_agent_rw(struct fw_card *card,
> + struct fw_request *request, int tcode, int destination, int source,
> + int generation, unsigned long long offset, void *data, size_t length,
> + void *callback_data)
> +{
> + struct sbp_management_agent *agent = callback_data;
> + struct sbp2_pointer *ptr = data;
> +
> + if (!agent->tport->enable) {
> + fw_send_response(card, request, RCODE_ADDRESS_ERROR);
> + return;
> + }
> +
> + if ((offset != agent->handler.offset) || (length != 8)) {
> + fw_send_response(card, request, RCODE_ADDRESS_ERROR);
> + return;
> + }
> +
> + if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
> + struct sbp_management_request *req;
> + int ret;
> +
> + smp_wmb();
> + if (atomic_cmpxchg(&agent->state,
> + MANAGEMENT_AGENT_STATE_IDLE,
> + MANAGEMENT_AGENT_STATE_BUSY) !=
> + MANAGEMENT_AGENT_STATE_IDLE) {
> + pr_notice("ignoring management request while busy\n");
> +
> + fw_send_response(card, request, RCODE_CONFLICT_ERROR);
> + return;
> + }
There is a rule of thumb which says: If you add a memory barrier anywhere
in your code, also add a comment saying which accesses this barrier is
meant to bring into order.
So after the write barrier is apparently the agent->state access. What
access is before the barrier?
And how does the read side look like?
These questions are mostly rhetoric. It is quite likely that this code is
better off with a plain and simple mutex serialization.
[...]
> +void sbp_management_agent_unregister(struct sbp_management_agent *agent)
> +{
> + if (atomic_read(&agent->state) != MANAGEMENT_AGENT_STATE_IDLE)
> + flush_work_sync(&agent->work);
> +
> + fw_core_remove_address_handler(&agent->handler);
> + kfree(agent);
> +}
I still have yet to test-apply all your patches, look at the sum of the
code and understand what the execution contexts and critical sections
are. So I really should not yet ask the next, uninformed question.
Looking at this function, I wonder: Can the agent->state change after you
read it, and what would happen then?
--
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h}
2012-02-15 19:48 ` Stefan Richter
@ 2012-02-16 10:28 ` Chris Boot
2012-02-16 14:12 ` Stefan Richter
0 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-16 10:28 UTC (permalink / raw)
To: Stefan Richter
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On 15/02/2012 19:48, Stefan Richter wrote:
> On Feb 15 Chris Boot wrote:
>> --- /dev/null
>> +++ b/drivers/target/sbp/sbp_management_agent.c
> [...]
>> +static void sbp_mgt_agent_rw(struct fw_card *card,
>> + struct fw_request *request, int tcode, int destination, int source,
>> + int generation, unsigned long long offset, void *data, size_t length,
>> + void *callback_data)
>> +{
>> + struct sbp_management_agent *agent = callback_data;
>> + struct sbp2_pointer *ptr = data;
>> +
>> + if (!agent->tport->enable) {
>> + fw_send_response(card, request, RCODE_ADDRESS_ERROR);
>> + return;
>> + }
>> +
>> + if ((offset != agent->handler.offset) || (length != 8)) {
>> + fw_send_response(card, request, RCODE_ADDRESS_ERROR);
>> + return;
>> + }
>> +
>> + if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
>> + struct sbp_management_request *req;
>> + int ret;
>> +
>> + smp_wmb();
>> + if (atomic_cmpxchg(&agent->state,
>> + MANAGEMENT_AGENT_STATE_IDLE,
>> + MANAGEMENT_AGENT_STATE_BUSY) !=
>> + MANAGEMENT_AGENT_STATE_IDLE) {
>> + pr_notice("ignoring management request while busy\n");
>> +
>> + fw_send_response(card, request, RCODE_CONFLICT_ERROR);
>> + return;
>> + }
>
> There is a rule of thumb which says: If you add a memory barrier anywhere
> in your code, also add a comment saying which accesses this barrier is
> meant to bring into order.
>
> So after the write barrier is apparently the agent->state access. What
> access is before the barrier?
>
> And how does the read side look like?
>
> These questions are mostly rhetoric. It is quite likely that this code is
> better off with a plain and simple mutex serialization.
Well as I mentioned in my previous messages atomics and locking are
pretty new ideas to me. Having only really done threading in PERL
before, the world of doing it in the kernel is rather different! :-) The
memory barriers are there after I looked in Documentation/atomic_ops.txt
and clearly misunderstood the entire thing.
Reading about mutexes though I see I can't use them from interrupt
context, but doesn't the FireWire address handler execute in interrupt
context? I have to check the state of the management agent in the
address handler to properly reject requests from the initiator when the
agent is busy. I guess a spinlock is called for in this situation,
possibly using spin_trylock() in the address handler to avoid blocking?
> [...]
>> +void sbp_management_agent_unregister(struct sbp_management_agent *agent)
>> +{
>> + if (atomic_read(&agent->state) != MANAGEMENT_AGENT_STATE_IDLE)
>> + flush_work_sync(&agent->work);
>> +
>> + fw_core_remove_address_handler(&agent->handler);
>> + kfree(agent);
>> +}
>
> I still have yet to test-apply all your patches, look at the sum of the
> code and understand what the execution contexts and critical sections
> are. So I really should not yet ask the next, uninformed question.
>
> Looking at this function, I wonder: Can the agent->state change after you
> read it, and what would happen then?
The agent state could change between the read and before the address
handler is unregistered, this would require an initiator to keep sending
management requests long after the FW unit descriptor is removed though.
I guess to solve this in a bulletproof manner I need a kref in struct
sbp_management_agent and a spinlock(?), and a new state to say the agent
is going away. That way if I happen to receive requests while the agent
is being removed it can reply with address errors before the address
handler is fully removed. I'd also need something similar in the target
agent.
--
Chris Boot
bootc@bootc.net
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h}
2012-02-16 10:28 ` Chris Boot
@ 2012-02-16 14:12 ` Stefan Richter
0 siblings, 0 replies; 104+ messages in thread
From: Stefan Richter @ 2012-02-16 14:12 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 16 Chris Boot wrote:
> Reading about mutexes though I see I can't use them from interrupt
> context, but doesn't the FireWire address handler execute in interrupt
> context? I have to check the state of the management agent in the
> address handler to properly reject requests from the initiator when the
> agent is busy. I guess a spinlock is called for in this situation,
> possibly using spin_trylock() in the address handler to avoid blocking?
Yes; the request-receive callback (address handler) is executed in tasklet
context. All drivers which use this currently use the spin_lock_irqsave
API variant. I am considering to change the entire subsystem to use
spin_lock_bh only. IMO you could use spin_lock_bh in your target code, or
spin_lock for locks which are _only_ ever taken in tasklet context.
Plain spin_lock cannot be used, neither in the address handler nor in any
process context which grabs the same lock as the address handler.
The use cases for trylock variants of the locking APIs are somewhat
special. If in doubt, ignore that trylock APIs exist.
In strict terms, spin_trylock avoids busy-waiting for a contended lock.
mutex_trylock avoids blocking in the sense of sleeping wait for a contended
mutex. However, to expand on the obvious, the trylock variants are only
useful if whatever action which was meant to be performed can be aborted
without problem, e.g. if upper layers would be able to retry at some later
occasion.
(I am still interested in closer looks at the execution contexts and object
lifetime rules in your code, but spare time vs. mental capacity seem
regularly at odds...)
--
Stefan Richter
-=====-===-- --=- =----
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h}
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
` (6 preceding siblings ...)
2012-02-15 14:47 ` [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h} Chris Boot
@ 2012-02-15 14:47 ` Chris Boot
2012-02-15 21:00 ` Stefan Richter
2012-02-15 14:47 ` [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h} Chris Boot
` (3 subsequent siblings)
11 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-15 14:47 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This file contains the implementation of the login, reconnect and logout
management ORBs in SBP-2.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_login.c | 665 ++++++++++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_login.h | 14 +
2 files changed, 679 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_login.c
create mode 100644 drivers/target/sbp/sbp_login.h
diff --git a/drivers/target/sbp/sbp_login.c b/drivers/target/sbp/sbp_login.c
new file mode 100644
index 0000000..74b5eaf
--- /dev/null
+++ b/drivers/target/sbp/sbp_login.c
@@ -0,0 +1,665 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kref.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+
+#define SESSION_MAINTENANCE_INTERVAL HZ
+
+static atomic_t login_id = ATOMIC_INIT(0);
+
+static void session_maintenance_work(struct work_struct *work);
+
+static int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
+{
+ int ret;
+ __be32 high, low;
+
+ ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
+ &high, sizeof(high));
+ if (ret != RCODE_COMPLETE)
+ return ret;
+
+ ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
+ &low, sizeof(low));
+ if (ret != RCODE_COMPLETE)
+ return ret;
+
+ *guid = (u64)be32_to_cpu(high) << 32 | be32_to_cpu(low);
+
+ return RCODE_COMPLETE;
+}
+
+static struct sbp_session *sbp_session_find_by_guid(
+ struct sbp_tpg *tpg, u64 guid)
+{
+ struct se_session *se_sess;
+
+ spin_lock(&tpg->se_tpg.session_lock);
+ list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+ struct sbp_session *sess = se_sess->fabric_sess_ptr;
+ if (sess->guid == guid) {
+ spin_unlock(&tpg->se_tpg.session_lock);
+ return sess;
+ }
+ }
+ spin_unlock(&tpg->se_tpg.session_lock);
+
+ return NULL;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_lun(
+ struct sbp_session *session, struct se_lun *lun)
+{
+ struct sbp_login_descriptor *login;
+
+ spin_lock(&session->login_list_lock);
+ list_for_each_entry(login, &session->login_list, link) {
+ if (login->lun == lun) {
+ spin_unlock(&session->login_list_lock);
+ return login;
+ }
+ }
+ spin_unlock(&session->login_list_lock);
+
+ return NULL;
+}
+
+static int sbp_login_count_all_by_lun(
+ struct sbp_tpg *tpg,
+ struct se_lun *lun,
+ int exclusive)
+{
+ struct se_session *se_sess;
+ int count = 0;
+
+ spin_lock(&tpg->se_tpg.session_lock);
+ list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+ struct sbp_session *sess = se_sess->fabric_sess_ptr;
+ struct sbp_login_descriptor *login;
+
+ spin_lock(&sess->login_list_lock);
+ list_for_each_entry(login, &sess->login_list, link) {
+ if (login->lun != lun)
+ continue;
+
+ if (!exclusive) {
+ count++;
+ continue;
+ }
+
+ if (login->exclusive)
+ count++;
+ }
+ spin_unlock(&sess->login_list_lock);
+ }
+ spin_unlock(&tpg->se_tpg.session_lock);
+
+ return count;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_id(
+ struct sbp_tpg *tpg, int login_id)
+{
+ struct se_session *se_sess;
+
+ spin_lock(&tpg->se_tpg.session_lock);
+ list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+ struct sbp_session *sess = se_sess->fabric_sess_ptr;
+ struct sbp_login_descriptor *login;
+
+ spin_lock(&sess->login_list_lock);
+ list_for_each_entry(login, &sess->login_list, link) {
+ if (login->login_id == login_id) {
+ spin_unlock(&sess->login_list_lock);
+ spin_unlock(&tpg->se_tpg.session_lock);
+ return login;
+ }
+ }
+ spin_unlock(&sess->login_list_lock);
+ }
+ spin_unlock(&tpg->se_tpg.session_lock);
+
+ return NULL;
+}
+
+static struct se_lun *sbp_get_lun_from_tpg(struct sbp_tpg *tpg, int lun)
+{
+ struct se_portal_group *se_tpg = &tpg->se_tpg;
+ struct se_lun *se_lun;
+
+ if (lun >= TRANSPORT_MAX_LUNS_PER_TPG)
+ return ERR_PTR(-ENODEV);
+
+ spin_lock(&se_tpg->tpg_lun_lock);
+ se_lun = &se_tpg->tpg_lun_list[lun];
+
+ if (se_lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE)
+ se_lun = ERR_PTR(-EINVAL);
+
+ spin_unlock(&se_tpg->tpg_lun_lock);
+
+ return se_lun;
+}
+
+static struct sbp_session *sbp_session_create(
+ struct sbp_tpg *tpg,
+ u64 guid)
+{
+ struct sbp_session *sess;
+ int ret;
+ char guid_str[17];
+ struct se_node_acl *se_nacl;
+
+ sess = kmalloc(sizeof(*sess), GFP_KERNEL);
+ if (!sess) {
+ pr_err("failed to allocate session descriptor\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sess->se_sess = transport_init_session();
+ if (IS_ERR(sess->se_sess)) {
+ pr_err("failed to init se_session\n");
+
+ ret = PTR_ERR(sess->se_sess);
+ kfree(sess);
+ return ERR_PTR(ret);
+ }
+
+ snprintf(guid_str, sizeof(guid_str), "%016llx", guid);
+
+ se_nacl = core_tpg_check_initiator_node_acl(&tpg->se_tpg, guid_str);
+ if (!se_nacl) {
+ pr_warn("Node ACL not found for %s\n", guid_str);
+
+ transport_free_session(sess->se_sess);
+ kfree(sess);
+
+ return ERR_PTR(-EPERM);
+ }
+
+ sess->se_sess->se_node_acl = se_nacl;
+
+ INIT_LIST_HEAD(&sess->login_list);
+ spin_lock_init(&sess->login_list_lock);
+ INIT_DELAYED_WORK(&sess->maint_work, session_maintenance_work);
+
+ sess->guid = guid;
+
+ transport_register_session(&tpg->se_tpg, se_nacl, sess->se_sess, sess);
+
+ return sess;
+}
+
+static void sbp_session_release(struct sbp_session *sess, bool cancel_work)
+{
+ spin_lock(&sess->login_list_lock);
+ if (!list_empty(&sess->login_list)) {
+ spin_unlock(&sess->login_list_lock);
+ return;
+ }
+ spin_unlock(&sess->login_list_lock);
+
+ transport_deregister_session_configfs(sess->se_sess);
+ transport_deregister_session(sess->se_sess);
+
+ if (sess->card)
+ fw_card_put(sess->card);
+
+ if (cancel_work)
+ cancel_delayed_work_sync(&sess->maint_work);
+
+ kfree(sess);
+}
+
+static void sbp_login_release(struct sbp_login_descriptor *login,
+ bool cancel_work)
+{
+ struct sbp_session *sess = login->sess;
+
+ /* FIXME: abort/wait on tasks */
+
+ spin_lock(&sess->login_list_lock);
+ list_del(&login->link);
+ spin_unlock(&sess->login_list_lock);
+
+ sbp_target_agent_unregister(login->tgt_agt);
+ sbp_session_release(sess, cancel_work);
+ kfree(login);
+}
+
+void sbp_management_request_login(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ struct sbp_tport *tport = agent->tport;
+ struct sbp_tpg *tpg = tport->tpg;
+ struct se_lun *se_lun;
+ int ret;
+ u64 guid;
+ struct sbp_session *sess;
+ struct sbp_login_descriptor *login;
+ struct sbp_login_response_block *response;
+ int login_response_len;
+
+ se_lun = sbp_get_lun_from_tpg(tpg,
+ LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+ if (IS_ERR(se_lun)) {
+ pr_notice("login to unknown LUN: %d\n",
+ LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LUN_NOTSUPP));
+ return;
+ }
+
+ ret = read_peer_guid(&guid, req);
+ if (ret != RCODE_COMPLETE) {
+ pr_warn("failed to read peer GUID: %d\n", ret);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ return;
+ }
+
+ pr_notice("mgt_agent LOGIN to LUN %d from %016llx\n",
+ se_lun->unpacked_lun, guid);
+
+ sess = sbp_session_find_by_guid(tpg, guid);
+ if (sess) {
+ login = sbp_login_find_by_lun(sess, se_lun);
+ if (login) {
+ pr_notice("initiator already logged-in\n");
+
+ /*
+ * SBP-2 R4 says we should return access denied, but
+ * that can confuse initiators. Instead we need to
+ * treat this like a reconnect, but send the login
+ * response block like a fresh login.
+ *
+ * This is required particularly in the case of Apple
+ * devices booting off the FireWire target, where
+ * the firmware has an active login to the target. When
+ * the OS takes control of the session it issues its own
+ * LOGIN rather than a RECONNECT. To avoid the machine
+ * waiting until the reconnect_hold expires, we can skip
+ * the ACCESS_DENIED errors to speed things up.
+ */
+
+ goto already_logged_in;
+ }
+ }
+
+ /*
+ * check exclusive bit in login request
+ * reject with access_denied if any logins present
+ */
+ if (LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc)) &&
+ sbp_login_count_all_by_lun(tpg, se_lun, 0)) {
+ pr_warn("refusing exclusive login with other active logins\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ /*
+ * check exclusive bit in any existing login descriptor
+ * reject with access_denied if any exclusive logins present
+ */
+ if (sbp_login_count_all_by_lun(tpg, se_lun, 1)) {
+ pr_warn("refusing login while another exclusive login present\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ /*
+ * check we haven't exceeded the number of allowed logins
+ * reject with resources_unavailable if we have
+ */
+ if (sbp_login_count_all_by_lun(tpg, se_lun, 0) >=
+ tport->max_logins_per_lun) {
+ pr_warn("max number of logins reached\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ if (!sess) {
+ sess = sbp_session_create(tpg, guid);
+ if (IS_ERR(sess)) {
+ switch (PTR_ERR(sess)) {
+ case -EPERM:
+ ret = SBP_STATUS_ACCESS_DENIED;
+ break;
+ default:
+ ret = SBP_STATUS_RESOURCES_UNAVAIL;
+ break;
+ }
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(
+ STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(ret));
+ return;
+ }
+
+ sess->node_id = req->node_addr;
+ sess->card = fw_card_get(req->card);
+ sess->generation = req->generation;
+ sess->speed = req->speed;
+
+ queue_delayed_work(sbp_workqueue, &sess->maint_work,
+ SESSION_MAINTENANCE_INTERVAL);
+ }
+
+ /* only take the latest reconnect_hold into account */
+ sess->reconnect_hold = min(
+ 1 << LOGIN_ORB_RECONNECT(be32_to_cpu(req->orb.misc)),
+ tport->max_reconnect_timeout) - 1;
+
+ login = kmalloc(sizeof(*login), GFP_KERNEL);
+ if (!login) {
+ pr_err("failed to allocate login descriptor\n");
+
+ sbp_session_release(sess, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ login->sess = sess;
+ login->lun = se_lun;
+ login->status_fifo_addr = sbp2_pointer_to_addr(&req->orb.status_fifo);
+ login->exclusive = LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc));
+ login->login_id = atomic_inc_return(&login_id);
+ atomic_set(&login->unsolicited_status_enable, 0);
+
+ login->tgt_agt = sbp_target_agent_register(login);
+ if (IS_ERR(login->tgt_agt)) {
+ ret = PTR_ERR(login->tgt_agt);
+ pr_err("failed to map command block handler: %d\n", ret);
+
+ sbp_session_release(sess, true);
+ kfree(login);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ spin_lock(&sess->login_list_lock);
+ list_add_tail(&login->link, &sess->login_list);
+ spin_unlock(&sess->login_list_lock);
+
+already_logged_in:
+ response = kzalloc(sizeof(*response), GFP_KERNEL);
+ if (!response) {
+ pr_err("failed to allocate login response block\n");
+
+ sbp_login_release(login, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ login_response_len = clamp_val(
+ LOGIN_ORB_RESPONSE_LENGTH(be32_to_cpu(req->orb.length)),
+ 12, sizeof(*response));
+ response->misc = cpu_to_be32(
+ ((login_response_len & 0xffff) << 16) |
+ (login->login_id & 0xffff));
+ response->reconnect_hold = cpu_to_be32(sess->reconnect_hold & 0xffff);
+ addr_to_sbp2_pointer(login->tgt_agt->handler.offset,
+ &response->command_block_agent);
+
+ ret = fw_run_transaction(sess->card, TCODE_WRITE_BLOCK_REQUEST,
+ sess->node_id, sess->generation, sess->speed,
+ sbp2_pointer_to_addr(&req->orb.ptr2), response,
+ login_response_len);
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("failed to write login response block: %x\n", ret);
+
+ kfree(response);
+ sbp_login_release(login, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ return;
+ }
+
+ kfree(response);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+void sbp_management_request_query_logins(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ pr_notice("QUERY LOGINS not implemented\n");
+ /* FIXME: implement */
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+}
+
+void sbp_management_request_reconnect(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ struct sbp_tport *tport = agent->tport;
+ struct sbp_tpg *tpg = tport->tpg;
+ int ret;
+ u64 guid;
+ struct sbp_login_descriptor *login;
+
+ ret = read_peer_guid(&guid, req);
+ if (ret != RCODE_COMPLETE) {
+ pr_warn("failed to read peer GUID: %d\n", ret);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ return;
+ }
+
+ pr_notice("mgt_agent RECONNECT from %016llx\n", guid);
+
+ login = sbp_login_find_by_id(tpg,
+ RECONNECT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc)));
+
+ if (!login) {
+ pr_err("mgt_agent RECONNECT unknown login ID\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ if (login->sess->guid != guid) {
+ pr_err("mgt_agent RECONNECT login GUID doesn't match\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ if (login->sess->card)
+ fw_card_put(login->sess->card);
+
+ /* update the node details */
+ login->sess->generation = req->generation;
+ login->sess->node_id = req->node_addr;
+ login->sess->card = fw_card_get(req->card);
+ login->sess->speed = req->speed;
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+void sbp_management_request_logout(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ struct sbp_tport *tport = agent->tport;
+ struct sbp_tpg *tpg = tport->tpg;
+ int login_id;
+ struct sbp_login_descriptor *login;
+
+ login_id = LOGOUT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc));
+
+ login = sbp_login_find_by_id(tpg, login_id);
+ if (!login) {
+ pr_warn("cannot find login: %d\n", login_id);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LOGIN_ID_UNKNOWN));
+ return;
+ }
+
+ pr_info("mgt_agent LOGOUT from LUN %d session %d\n",
+ login->lun->unpacked_lun, login->login_id);
+
+ if (req->node_addr != login->sess->node_id) {
+ pr_warn("logout from different node ID\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ sbp_login_release(login, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static void session_check_for_reset(struct sbp_session *sess)
+{
+ bool card_valid = false;
+
+ if (sess->card) {
+ spin_lock_irq(&sess->card->lock);
+ card_valid = (sess->card->local_node != NULL);
+ spin_unlock_irq(&sess->card->lock);
+
+ if (!card_valid) {
+ fw_card_put(sess->card);
+ sess->card = NULL;
+ }
+ }
+
+ if (!card_valid || (sess->generation != sess->card->generation)) {
+ pr_info("Waiting for reconnect from node: %016llx\n",
+ sess->guid);
+
+ sess->node_id = -1;
+ sess->reconnect_expires = get_jiffies_64() +
+ ((sess->reconnect_hold + 1) * HZ);
+ }
+}
+
+static void session_reconnect_expired(struct sbp_session *sess)
+{
+ struct sbp_login_descriptor *login, *temp;
+
+ pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
+
+ spin_lock(&sess->login_list_lock);
+ list_for_each_entry_safe(login, temp, &sess->login_list, link) {
+ spin_unlock(&sess->login_list_lock);
+ sbp_login_release(login, false);
+ spin_lock(&sess->login_list_lock);
+ }
+ spin_unlock(&sess->login_list_lock);
+
+ /* sbp_login_release() calls sbp_session_release() */
+}
+
+static void session_maintenance_work(struct work_struct *work)
+{
+ struct sbp_session *sess = container_of(work, struct sbp_session,
+ maint_work.work);
+
+ /* could be called while tearing down the session */
+ spin_lock(&sess->login_list_lock);
+ if (list_empty(&sess->login_list)) {
+ spin_unlock(&sess->login_list_lock);
+ return;
+ }
+ spin_unlock(&sess->login_list_lock);
+
+ if (sess->node_id != -1) {
+ /* check for bus reset and make node_id invalid */
+ session_check_for_reset(sess);
+
+ queue_delayed_work(sbp_workqueue, &sess->maint_work,
+ SESSION_MAINTENANCE_INTERVAL);
+ } else if (!time_after64(get_jiffies_64(), sess->reconnect_expires)) {
+ /* still waiting for reconnect */
+ queue_delayed_work(sbp_workqueue, &sess->maint_work,
+ SESSION_MAINTENANCE_INTERVAL);
+ } else {
+ /* reconnect timeout has expired */
+ session_reconnect_expired(sess);
+ }
+}
+
diff --git a/drivers/target/sbp/sbp_login.h b/drivers/target/sbp/sbp_login.h
new file mode 100644
index 0000000..4c86dcb
--- /dev/null
+++ b/drivers/target/sbp/sbp_login.h
@@ -0,0 +1,14 @@
+
+extern void sbp_management_request_login(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size);
+extern void sbp_management_request_query_logins(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size);
+extern void sbp_management_request_reconnect(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size);
+extern void sbp_management_request_logout(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size);
+
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h}
2012-02-15 14:47 ` [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h} Chris Boot
@ 2012-02-15 21:00 ` Stefan Richter
2012-02-16 11:21 ` Chris Boot
0 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-02-15 21:00 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 15 Chris Boot wrote:
> This file contains the implementation of the login, reconnect and logout
> management ORBs in SBP-2.
>
> Signed-off-by: Chris Boot <bootc@bootc.net>
> Cc: Andy Grover <agrover@redhat.com>
> Cc: Clemens Ladisch <clemens@ladisch.de>
> Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
> Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
> ---
> drivers/target/sbp/sbp_login.c | 665 ++++++++++++++++++++++++++++++++++++++++
> drivers/target/sbp/sbp_login.h | 14 +
> 2 files changed, 679 insertions(+), 0 deletions(-)
> create mode 100644 drivers/target/sbp/sbp_login.c
> create mode 100644 drivers/target/sbp/sbp_login.h
>
> diff --git a/drivers/target/sbp/sbp_login.c b/drivers/target/sbp/sbp_login.c
> new file mode 100644
> index 0000000..74b5eaf
> --- /dev/null
> +++ b/drivers/target/sbp/sbp_login.c
> @@ -0,0 +1,665 @@
> +/*
> + * SBP2 target driver (SCSI over IEEE1394 in target mode)
> + *
> + * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software Foundation,
> + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + */
> +
> +#define KMSG_COMPONENT "sbp_target"
> +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
> +
> +#include <linux/kref.h>
> +#include <linux/firewire.h>
> +#include <linux/firewire-constants.h>
> +#include <linux/slab.h>
> +
> +#include <target/target_core_base.h>
> +#include <target/target_core_fabric.h>
> +
> +#include "sbp_base.h"
> +#include "sbp_management_agent.h"
> +#include "sbp_login.h"
> +#include "sbp_target_agent.h"
> +
> +#define SESSION_MAINTENANCE_INTERVAL HZ
> +
> +static atomic_t login_id = ATOMIC_INIT(0);
> +
> +static void session_maintenance_work(struct work_struct *work);
> +
> +static int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
> +{
> + int ret;
> + __be32 high, low;
> +
> + ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
> + req->node_addr, req->generation, req->speed,
> + (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
> + &high, sizeof(high));
> + if (ret != RCODE_COMPLETE)
> + return ret;
> +
> + ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
> + req->node_addr, req->generation, req->speed,
> + (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
> + &low, sizeof(low));
> + if (ret != RCODE_COMPLETE)
> + return ret;
> +
> + *guid = (u64)be32_to_cpu(high) << 32 | be32_to_cpu(low);
> +
> + return RCODE_COMPLETE;
> +}
> +
> +static struct sbp_session *sbp_session_find_by_guid(
> + struct sbp_tpg *tpg, u64 guid)
> +{
> + struct se_session *se_sess;
> +
> + spin_lock(&tpg->se_tpg.session_lock);
> + list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
> + struct sbp_session *sess = se_sess->fabric_sess_ptr;
> + if (sess->guid == guid) {
> + spin_unlock(&tpg->se_tpg.session_lock);
> + return sess;
> + }
> + }
> + spin_unlock(&tpg->se_tpg.session_lock);
> +
> + return NULL;
> +}
Another form to write this would be
static struct sbp_session *sbp_session_find_by_guid(
struct sbp_tpg *tpg, u64 guid)
{
struct se_session *se_sess;
struct sbp_session *s, *session = NULL;
spin_lock(&tpg->se_tpg.session_lock);
list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
s = se_sess->fabric_sess_ptr;
if (s->guid == guid) {
session = s;
break;
}
}
spin_unlock(&tpg->se_tpg.session_lock);
return session;
}
But since your function is very small, the dual unlock-and-exit paths are
not a problem for readability.
As an aside, here is a variation of the theme, though weirdly looking if
one never came across it before:
static struct sbp_session *sbp_session_find_by_guid(
struct sbp_tpg *tpg, u64 guid)
{
struct se_session *s;
spin_lock(&tpg->se_tpg.session_lock);
list_for_each_entry(s, &tpg->se_tpg.tpg_sess_list, sess_list)
if (s->fabric_sess_ptr->guid == guid)
break;
spin_unlock(&tpg->se_tpg.session_lock);
if (&s->sess_list != &tpg->se_tpg.tpg_sess_list)
return s->fabric_sess_ptr;
else
return NULL;
}
[...]
> +static struct sbp_login_descriptor *sbp_login_find_by_id(
> + struct sbp_tpg *tpg, int login_id)
> +{
> + struct se_session *se_sess;
> +
> + spin_lock(&tpg->se_tpg.session_lock);
> + list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
> + struct sbp_session *sess = se_sess->fabric_sess_ptr;
> + struct sbp_login_descriptor *login;
> +
> + spin_lock(&sess->login_list_lock);
> + list_for_each_entry(login, &sess->login_list, link) {
> + if (login->login_id == login_id) {
> + spin_unlock(&sess->login_list_lock);
> + spin_unlock(&tpg->se_tpg.session_lock);
> + return login;
> + }
> + }
> + spin_unlock(&sess->login_list_lock);
> + }
> + spin_unlock(&tpg->se_tpg.session_lock);
> +
> + return NULL;
> +}
This function on the other hand might indeed benefit from a style
involving a single unlock-and-exit path.
[...]
> +static void sbp_session_release(struct sbp_session *sess, bool cancel_work)
> +{
> + spin_lock(&sess->login_list_lock);
> + if (!list_empty(&sess->login_list)) {
> + spin_unlock(&sess->login_list_lock);
> + return;
> + }
> + spin_unlock(&sess->login_list_lock);
> +
> + transport_deregister_session_configfs(sess->se_sess);
> + transport_deregister_session(sess->se_sess);
> +
> + if (sess->card)
> + fw_card_put(sess->card);
> +
> + if (cancel_work)
> + cancel_delayed_work_sync(&sess->maint_work);
> +
> + kfree(sess);
> +}
What prevents that an entry is added to sess->login_list right after
you tested for it being empty?
If there is something external which prevents this, then you don't
need to take the lock just for this test.
If there is no such external measure of serialization, then the
spinlock-protected section is too small.
By the way, the use of spin_lock()/spin_unlock() is quite atypical.
This API restricts you
- not to call a possibly sleeping function within the lock-
protected section,
- not to take the lock in tasklet context or IRQ context.
So this locking API is quite rarely used: Anywhere where a mutex
/could/ be used, but none of the locked sections ever need to sleep.
This is a rather narrow use case.
Maybe you know all this but I thought I mention it anyway.
> +static void session_check_for_reset(struct sbp_session *sess)
> +{
> + bool card_valid = false;
> +
> + if (sess->card) {
> + spin_lock_irq(&sess->card->lock);
> + card_valid = (sess->card->local_node != NULL);
> + spin_unlock_irq(&sess->card->lock);
> +
> + if (!card_valid) {
> + fw_card_put(sess->card);
> + sess->card = NULL;
> + }
> + }
> +
> + if (!card_valid || (sess->generation != sess->card->generation)) {
> + pr_info("Waiting for reconnect from node: %016llx\n",
> + sess->guid);
> +
> + sess->node_id = -1;
> + sess->reconnect_expires = get_jiffies_64() +
> + ((sess->reconnect_hold + 1) * HZ);
> + }
> +}
[Note to self: When more awake, carefully review this peeking into
fw_card internals, the generation accesses, and the card refcounting.]
> +static void session_reconnect_expired(struct sbp_session *sess)
> +{
> + struct sbp_login_descriptor *login, *temp;
> +
> + pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
> +
> + spin_lock(&sess->login_list_lock);
> + list_for_each_entry_safe(login, temp, &sess->login_list, link) {
> + spin_unlock(&sess->login_list_lock);
> + sbp_login_release(login, false);
> + spin_lock(&sess->login_list_lock);
> + }
> + spin_unlock(&sess->login_list_lock);
> +
> + /* sbp_login_release() calls sbp_session_release() */
> +}
This is wrong. Either something external protects the
session_reconnect_expired() executing context from concurrent
manipulations of sess->login_list. Then you don't need to
take the lock here in the first place.
Or there is no such external serialization measure. Then you
must not drop the list lock in the loop body.
In the latter case, an easy fix would be to move the expired
logins to a local temporary list while holding the lock, then
release each item from the temporary list without holding the
lock.
--
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h}
2012-02-15 21:00 ` Stefan Richter
@ 2012-02-16 11:21 ` Chris Boot
2012-03-03 17:37 ` Stefan Richter
0 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-16 11:21 UTC (permalink / raw)
To: Stefan Richter
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On 15/02/2012 21:00, Stefan Richter wrote:
> On Feb 15 Chris Boot wrote:
>> This file contains the implementation of the login, reconnect and logout
>> management ORBs in SBP-2.
>>
>> Signed-off-by: Chris Boot<bootc@bootc.net>
>> Cc: Andy Grover<agrover@redhat.com>
>> Cc: Clemens Ladisch<clemens@ladisch.de>
>> Cc: Nicholas A. Bellinger<nab@linux-iscsi.org>
>> Cc: Stefan Richter<stefanr@s5r6.in-berlin.de>
>> ---
>> drivers/target/sbp/sbp_login.c | 665 ++++++++++++++++++++++++++++++++++++++++
>> drivers/target/sbp/sbp_login.h | 14 +
>> 2 files changed, 679 insertions(+), 0 deletions(-)
>> create mode 100644 drivers/target/sbp/sbp_login.c
>> create mode 100644 drivers/target/sbp/sbp_login.h
>>
>> diff --git a/drivers/target/sbp/sbp_login.c b/drivers/target/sbp/sbp_login.c
>> new file mode 100644
>> index 0000000..74b5eaf
>> --- /dev/null
>> +++ b/drivers/target/sbp/sbp_login.c
>> @@ -0,0 +1,665 @@
>> +/*
>> + * SBP2 target driver (SCSI over IEEE1394 in target mode)
>> + *
>> + * Copyright (C) 2011 Chris Boot<bootc@bootc.net>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software Foundation,
>> + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
>> + */
>> +
>> +#define KMSG_COMPONENT "sbp_target"
>> +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
>> +
>> +#include<linux/kref.h>
>> +#include<linux/firewire.h>
>> +#include<linux/firewire-constants.h>
>> +#include<linux/slab.h>
>> +
>> +#include<target/target_core_base.h>
>> +#include<target/target_core_fabric.h>
>> +
>> +#include "sbp_base.h"
>> +#include "sbp_management_agent.h"
>> +#include "sbp_login.h"
>> +#include "sbp_target_agent.h"
>> +
>> +#define SESSION_MAINTENANCE_INTERVAL HZ
>> +
>> +static atomic_t login_id = ATOMIC_INIT(0);
>> +
>> +static void session_maintenance_work(struct work_struct *work);
>> +
>> +static int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
>> +{
>> + int ret;
>> + __be32 high, low;
>> +
>> + ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
>> + req->node_addr, req->generation, req->speed,
>> + (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
>> + &high, sizeof(high));
>> + if (ret != RCODE_COMPLETE)
>> + return ret;
>> +
>> + ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
>> + req->node_addr, req->generation, req->speed,
>> + (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
>> + &low, sizeof(low));
>> + if (ret != RCODE_COMPLETE)
>> + return ret;
>> +
>> + *guid = (u64)be32_to_cpu(high)<< 32 | be32_to_cpu(low);
>> +
>> + return RCODE_COMPLETE;
>> +}
>> +
>> +static struct sbp_session *sbp_session_find_by_guid(
>> + struct sbp_tpg *tpg, u64 guid)
>> +{
>> + struct se_session *se_sess;
>> +
>> + spin_lock(&tpg->se_tpg.session_lock);
>> + list_for_each_entry(se_sess,&tpg->se_tpg.tpg_sess_list, sess_list) {
>> + struct sbp_session *sess = se_sess->fabric_sess_ptr;
>> + if (sess->guid == guid) {
>> + spin_unlock(&tpg->se_tpg.session_lock);
>> + return sess;
>> + }
>> + }
>> + spin_unlock(&tpg->se_tpg.session_lock);
>> +
>> + return NULL;
>> +}
>
> Another form to write this would be
>
> static struct sbp_session *sbp_session_find_by_guid(
> struct sbp_tpg *tpg, u64 guid)
> {
> struct se_session *se_sess;
> struct sbp_session *s, *session = NULL;
>
> spin_lock(&tpg->se_tpg.session_lock);
> list_for_each_entry(se_sess,&tpg->se_tpg.tpg_sess_list, sess_list) {
> s = se_sess->fabric_sess_ptr;
> if (s->guid == guid) {
> session = s;
> break;
> }
> }
> spin_unlock(&tpg->se_tpg.session_lock);
>
> return session;
> }
>
> But since your function is very small, the dual unlock-and-exit paths are
> not a problem for readability.
>
> As an aside, here is a variation of the theme, though weirdly looking if
> one never came across it before:
>
> static struct sbp_session *sbp_session_find_by_guid(
> struct sbp_tpg *tpg, u64 guid)
> {
> struct se_session *s;
>
> spin_lock(&tpg->se_tpg.session_lock);
> list_for_each_entry(s,&tpg->se_tpg.tpg_sess_list, sess_list)
> if (s->fabric_sess_ptr->guid == guid)
> break;
> spin_unlock(&tpg->se_tpg.session_lock);
>
> if (&s->sess_list !=&tpg->se_tpg.tpg_sess_list)
> return s->fabric_sess_ptr;
> else
> return NULL;
> }
>
> [...]
>> +static struct sbp_login_descriptor *sbp_login_find_by_id(
>> + struct sbp_tpg *tpg, int login_id)
>> +{
>> + struct se_session *se_sess;
>> +
>> + spin_lock(&tpg->se_tpg.session_lock);
>> + list_for_each_entry(se_sess,&tpg->se_tpg.tpg_sess_list, sess_list) {
>> + struct sbp_session *sess = se_sess->fabric_sess_ptr;
>> + struct sbp_login_descriptor *login;
>> +
>> + spin_lock(&sess->login_list_lock);
>> + list_for_each_entry(login,&sess->login_list, link) {
>> + if (login->login_id == login_id) {
>> + spin_unlock(&sess->login_list_lock);
>> + spin_unlock(&tpg->se_tpg.session_lock);
>> + return login;
>> + }
>> + }
>> + spin_unlock(&sess->login_list_lock);
>> + }
>> + spin_unlock(&tpg->se_tpg.session_lock);
>> +
>> + return NULL;
>> +}
>
> This function on the other hand might indeed benefit from a style
> involving a single unlock-and-exit path.
>
> [...]
>> +static void sbp_session_release(struct sbp_session *sess, bool cancel_work)
>> +{
>> + spin_lock(&sess->login_list_lock);
>> + if (!list_empty(&sess->login_list)) {
>> + spin_unlock(&sess->login_list_lock);
>> + return;
>> + }
>> + spin_unlock(&sess->login_list_lock);
>> +
>> + transport_deregister_session_configfs(sess->se_sess);
>> + transport_deregister_session(sess->se_sess);
>> +
>> + if (sess->card)
>> + fw_card_put(sess->card);
>> +
>> + if (cancel_work)
>> + cancel_delayed_work_sync(&sess->maint_work);
>> +
>> + kfree(sess);
>> +}
>
> What prevents that an entry is added to sess->login_list right after
> you tested for it being empty?
>
> If there is something external which prevents this, then you don't
> need to take the lock just for this test.
>
> If there is no such external measure of serialization, then the
> spinlock-protected section is too small.
Only two things can manipulate the login list: the management agent,
which is serialised (e.g. only one management request per target port
can be ongoing at any point in time), and the session maintenance work
item. So you're right, the lock-protected section is too small.
> By the way, the use of spin_lock()/spin_unlock() is quite atypical.
> This API restricts you
> - not to call a possibly sleeping function within the lock-
> protected section,
> - not to take the lock in tasklet context or IRQ context.
>
> So this locking API is quite rarely used: Anywhere where a mutex
> /could/ be used, but none of the locked sections ever need to sleep.
> This is a rather narrow use case.
>
> Maybe you know all this but I thought I mention it anyway.
The session is only really inspected/maniuplated in the context of work
items, so it's possible I could use mutexes - although in the target
agent address handler I do check the node_id against the one in the
session (and I should really check the generation too). Would memory
barriers be enough for those or would locking be required?
>> +static void session_check_for_reset(struct sbp_session *sess)
>> +{
>> + bool card_valid = false;
>> +
>> + if (sess->card) {
>> + spin_lock_irq(&sess->card->lock);
>> + card_valid = (sess->card->local_node != NULL);
>> + spin_unlock_irq(&sess->card->lock);
>> +
>> + if (!card_valid) {
>> + fw_card_put(sess->card);
>> + sess->card = NULL;
>> + }
>> + }
>> +
>> + if (!card_valid || (sess->generation != sess->card->generation)) {
>> + pr_info("Waiting for reconnect from node: %016llx\n",
>> + sess->guid);
>> +
>> + sess->node_id = -1;
>> + sess->reconnect_expires = get_jiffies_64() +
>> + ((sess->reconnect_hold + 1) * HZ);
>> + }
>> +}
>
> [Note to self: When more awake, carefully review this peeking into
> fw_card internals, the generation accesses, and the card refcounting.]
>
>> +static void session_reconnect_expired(struct sbp_session *sess)
>> +{
>> + struct sbp_login_descriptor *login, *temp;
>> +
>> + pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
>> +
>> + spin_lock(&sess->login_list_lock);
>> + list_for_each_entry_safe(login, temp,&sess->login_list, link) {
>> + spin_unlock(&sess->login_list_lock);
>> + sbp_login_release(login, false);
>> + spin_lock(&sess->login_list_lock);
>> + }
>> + spin_unlock(&sess->login_list_lock);
>> +
>> + /* sbp_login_release() calls sbp_session_release() */
>> +}
>
> This is wrong. Either something external protects the
> session_reconnect_expired() executing context from concurrent
> manipulations of sess->login_list. Then you don't need to
> take the lock here in the first place.
>
> Or there is no such external serialization measure. Then you
> must not drop the list lock in the loop body.
>
> In the latter case, an easy fix would be to move the expired
> logins to a local temporary list while holding the lock, then
> release each item from the temporary list without holding the
> lock.
Yes deep down I knew it was wrong, and it'll be the latter case of
needing more locking than it has. I guess I need to protect access to
the entire session really. Possibly even rwlocks due to only the
management processes ever changing anything, but lots of reads during
command handling.
Cheers,
Chris
--
Chris Boot
bootc@bootc.net
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h}
2012-02-16 11:21 ` Chris Boot
@ 2012-03-03 17:37 ` Stefan Richter
2012-03-15 17:48 ` Paul E. McKenney
0 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-03-03 17:37 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 16 Chris Boot wrote:
> The session is only really inspected/maniuplated in the context of work
> items, so it's possible I could use mutexes - although in the target
> agent address handler I do check the node_id against the one in the
> session (and I should really check the generation too). Would memory
> barriers be enough for those or would locking be required?
A node ID itself is an incomplete datum. Only a card--generation--node_ID
tuple is complete.
If generation and node ID are written and read concurrently, you have a
couple of choices to do this consistently: You could atomically write and
read an integer which contains 8 bits of generation and 16 bits of node
ID. Or you could serialize write and read accesses by a lock or mutex.
Or you could force the write side to write node ID before generation
(with an smp_wmb between to enforce order), and the read side to read
generation before node ID (with an smp_rmb to enforce order).
The latter method leaves a brief window in which a reader could pick up a
current node ID together with an outdated generation. This is harmless
because the OHCI hardware and respective precautions in firewire-ohci
prevents transaction subactions with outdated generation to go out to the
wire.
It is generally recommended to implement such accesses in drivers with
locks (spinlocks or mutexes). Among the various means to deal with
concurrency issues, locks seem to be best understood by us simple folk who
hack on drivers. :-)
[...]
> I guess I need to protect access to
> the entire session really. Possibly even rwlocks due to only the
> management processes ever changing anything, but lots of reads during
> command handling.
The use case for rwlocks is not really the case where infrequent write
access meets frequent read access. Rather, the use case is when it is
important to reduce or prevent contention between readers.
Rwlocks come with their own downsides though. I guess the somewhat
costlier lock implementation could counter the benefit of allowing
concurrent readers. Or maybe latency spikes around a write access could be
an issue.
I believe I have read somewhere that one should rather use a simple
spinlock unless exhaustive tests prove that an rwlock really performs
better. Furthermore, in many if not all use cases of rwlocks, RCU is
available as another alternative. RCU comes with its own set of downsides
though, for example not being as well and widely understood by programmers
compared to locking, being less easy to debug (may have improved
recently), and posing some challenges to RT-PREEMPT kernels.
AFAIU the above considerations cannot be applied 100 % to able-to-sleep
reader-writer locks, i.e. the kernel's rwsem. Still, the use case of an
rwsem (in contrast to a mutex) is not particularly where a datum is rarely
written and often read, but where it is desirable to let multiple readers
not block each other.
[Somebody correct me where I'm wrong.]
PS, I cloned your git tree not long ago, but again various distractions
kept me from having a broader look at your code...
--
Stefan Richter
-=====-===-- --== ---==
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h}
2012-03-03 17:37 ` Stefan Richter
@ 2012-03-15 17:48 ` Paul E. McKenney
0 siblings, 0 replies; 104+ messages in thread
From: Paul E. McKenney @ 2012-03-15 17:48 UTC (permalink / raw)
To: Stefan Richter
Cc: Chris Boot, linux1394-devel, target-devel, linux-kernel, agrover,
clemens, nab
On Sat, Mar 03, 2012 at 06:37:22PM +0100, Stefan Richter wrote:
> On Feb 16 Chris Boot wrote:
[ . . . ]
> > I guess I need to protect access to
> > the entire session really. Possibly even rwlocks due to only the
> > management processes ever changing anything, but lots of reads during
> > command handling.
>
> The use case for rwlocks is not really the case where infrequent write
> access meets frequent read access. Rather, the use case is when it is
> important to reduce or prevent contention between readers.
>
> Rwlocks come with their own downsides though. I guess the somewhat
> costlier lock implementation could counter the benefit of allowing
> concurrent readers. Or maybe latency spikes around a write access could be
> an issue.
>
> I believe I have read somewhere that one should rather use a simple
> spinlock unless exhaustive tests prove that an rwlock really performs
> better. Furthermore, in many if not all use cases of rwlocks, RCU is
> available as another alternative. RCU comes with its own set of downsides
> though, for example not being as well and widely understood by programmers
> compared to locking, being less easy to debug (may have improved
> recently), and posing some challenges to RT-PREEMPT kernels.
The preemptible RCU implementations (TREE_PREEMPT_RCU and TINY_PREEMPT_RCU)
handle the -rt tree.
> AFAIU the above considerations cannot be applied 100 % to able-to-sleep
> reader-writer locks, i.e. the kernel's rwsem. Still, the use case of an
> rwsem (in contrast to a mutex) is not particularly where a datum is rarely
> written and often read, but where it is desirable to let multiple readers
> not block each other.
>
> [Somebody correct me where I'm wrong.]
And you can use SRCU if readers need to block.
That said, even I agree that RCU is not always the right tool for the job.
;-), Paul
> PS, I cloned your git tree not long ago, but again various distractions
> kept me from having a broader look at your code...
> --
> Stefan Richter
> -=====-===-- --== ---==
> http://arcgraph.de/sr/
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
` (7 preceding siblings ...)
2012-02-15 14:47 ` [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h} Chris Boot
@ 2012-02-15 14:47 ` Chris Boot
2012-02-15 21:27 ` Stefan Richter
2012-02-15 14:47 ` [PATCH v2 10/11] firewire-sbp-target: Add sbp_scsi_cmnd.{c,h} Chris Boot
` (2 subsequent siblings)
11 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-15 14:47 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This implements the SBP-2 Command Block Agent, or Target Agent. This is
what receives SCSI commands and forwards them to the target framework.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_target_agent.c | 356 +++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_target_agent.h | 27 +++
2 files changed, 383 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_target_agent.c
create mode 100644 drivers/target/sbp/sbp_target_agent.h
diff --git a/drivers/target/sbp/sbp_target_agent.c b/drivers/target/sbp/sbp_target_agent.c
new file mode 100644
index 0000000..66b0b78
--- /dev/null
+++ b/drivers/target/sbp/sbp_target_agent.c
@@ -0,0 +1,356 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+static int tgt_agent_rw_agent_state(struct fw_card *card,
+ int tcode, int generation, void *data,
+ struct sbp_target_agent *agent)
+{
+ __be32 state;
+
+ switch (tcode) {
+ case TCODE_READ_QUADLET_REQUEST:
+ pr_debug("tgt_agent AGENT_STATE READ\n");
+
+ state = cpu_to_be32(atomic_read(&agent->state));
+ memcpy(data, &state, sizeof(state));
+
+ return RCODE_COMPLETE;
+
+ case TCODE_WRITE_QUADLET_REQUEST:
+ /* ignored */
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_agent_reset(struct fw_card *card,
+ int tcode, int generation, void *data,
+ struct sbp_target_agent *agent)
+{
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ pr_debug("tgt_agent AGENT_RESET\n");
+ atomic_set(&agent->state, AGENT_STATE_RESET);
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_orb_pointer(struct fw_card *card,
+ int tcode, int generation, void *data,
+ struct sbp_target_agent *agent)
+{
+ struct sbp2_pointer *ptr = data;
+ int ret;
+
+ switch (tcode) {
+ case TCODE_WRITE_BLOCK_REQUEST:
+ smp_wmb();
+ atomic_cmpxchg(&agent->state,
+ AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
+ smp_wmb();
+ if (atomic_cmpxchg(&agent->state,
+ AGENT_STATE_SUSPENDED,
+ AGENT_STATE_ACTIVE)
+ != AGENT_STATE_SUSPENDED)
+ return RCODE_CONFLICT_ERROR;
+ smp_wmb();
+
+ agent->orb_pointer = sbp2_pointer_to_addr(ptr);
+
+ pr_debug("tgt_agent ORB_POINTER write: 0x%llx\n",
+ agent->orb_pointer);
+
+ ret = queue_work(sbp_workqueue, &agent->work);
+ if (!ret)
+ return RCODE_CONFLICT_ERROR;
+
+ return RCODE_COMPLETE;
+
+ case TCODE_READ_BLOCK_REQUEST:
+ pr_debug("tgt_agent ORB_POINTER READ\n");
+ addr_to_sbp2_pointer(agent->orb_pointer, ptr);
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_doorbell(struct fw_card *card,
+ int tcode, int generation, void *data,
+ struct sbp_target_agent *agent)
+{
+ int ret;
+
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ smp_wmb();
+ if (atomic_cmpxchg(&agent->state,
+ AGENT_STATE_SUSPENDED,
+ AGENT_STATE_ACTIVE)
+ != AGENT_STATE_SUSPENDED)
+ return RCODE_CONFLICT_ERROR;
+ smp_wmb();
+
+ pr_debug("tgt_agent DOORBELL\n");
+
+ ret = queue_work(sbp_workqueue, &agent->work);
+ if (!ret)
+ return RCODE_CONFLICT_ERROR;
+
+ return RCODE_COMPLETE;
+
+ case TCODE_READ_QUADLET_REQUEST:
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_unsolicited_status_enable(struct fw_card *card,
+ int tcode, int generation, void *data,
+ struct sbp_target_agent *agent)
+{
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ pr_debug("tgt_agent UNSOLICITED_STATUS_ENABLE\n");
+ atomic_set(&agent->login->unsolicited_status_enable, 1);
+ return RCODE_COMPLETE;
+
+ case TCODE_READ_QUADLET_REQUEST:
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static void tgt_agent_rw(struct fw_card *card,
+ struct fw_request *request, int tcode, int destination, int source,
+ int generation, unsigned long long offset, void *data, size_t length,
+ void *callback_data)
+{
+ struct sbp_target_agent *agent = callback_data;
+ int rcode = RCODE_ADDRESS_ERROR;
+
+ /* turn offset into the offset from the start of the block */
+ offset -= agent->handler.offset;
+
+ if (source != agent->login->sess->node_id) {
+ pr_notice("ignoring request from foreign node (%x != %x)\n",
+ source, agent->login->sess->node_id);
+ fw_send_response(card, request, RCODE_TYPE_ERROR);
+ return;
+ }
+
+ if (offset == 0x00 && length == 4) {
+ /* AGENT_STATE */
+ rcode = tgt_agent_rw_agent_state(card, tcode,
+ generation, data, agent);
+ } else if (offset == 0x04 && length == 4) {
+ /* AGENT_RESET */
+ rcode = tgt_agent_rw_agent_reset(card, tcode,
+ generation, data, agent);
+ } else if (offset == 0x08 && length == 8) {
+ /* ORB_POINTER */
+ rcode = tgt_agent_rw_orb_pointer(card, tcode,
+ generation, data, agent);
+ } else if (offset == 0x10 && length == 4) {
+ /* DOORBELL */
+ rcode = tgt_agent_rw_doorbell(card, tcode,
+ generation, data, agent);
+ } else if (offset == 0x14 && length == 4) {
+ /* UNSOLICITED_STATUS_ENABLE */
+ rcode = tgt_agent_rw_unsolicited_status_enable(card, tcode,
+ generation, data, agent);
+ }
+
+ fw_send_response(card, request, rcode);
+}
+
+static void tgt_agent_process_work(struct work_struct *work)
+{
+ struct sbp_target_request *req =
+ container_of(work, struct sbp_target_request, work);
+
+ switch (ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc))) {
+ case 0:/* Format specified by this standard */
+ sbp_handle_command(req);
+ return;
+ case 1: /* Reserved for future standardization */
+ case 2: /* Vendor-dependent */
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ case 3: /* Dummy ORB */
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_DUMMY_ORB_COMPLETE));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ default:
+ BUG();
+ }
+}
+
+static void tgt_agent_fetch_work(struct work_struct *work)
+{
+ struct sbp_target_agent *agent =
+ container_of(work, struct sbp_target_agent, work);
+ struct sbp_session *sess = agent->login->sess;
+ struct sbp_target_request *req;
+ int ret;
+
+ smp_rmb();
+ if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE)
+ return;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req) {
+ atomic_cmpxchg(&agent->state, AGENT_STATE_ACTIVE,
+ AGENT_STATE_DEAD);
+ return;
+ }
+
+ req->agent = agent;
+ req->orb_pointer = agent->orb_pointer;
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_ORB_OFFSET_HIGH(req->orb_pointer >> 32));
+ req->status.orb_low = cpu_to_be32(agent->orb_pointer & 0xfffffffc);
+ INIT_WORK(&req->work, tgt_agent_process_work);
+
+ /* read in the ORB */
+ ret = fw_run_transaction(sess->card, TCODE_READ_BLOCK_REQUEST,
+ sess->node_id, sess->generation, sess->speed,
+ req->orb_pointer, &req->orb, sizeof(req->orb));
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("tgt_orb fetch failed: %x\n", ret);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(1) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ atomic_cmpxchg(&agent->state, AGENT_STATE_ACTIVE,
+ AGENT_STATE_DEAD);
+ return;
+ }
+
+ pr_debug("tgt_orb ptr:0x%llx next_orb:0x%llx data_descriptor:0x%llx misc:0x%x\n",
+ req->orb_pointer,
+ sbp2_pointer_to_addr(&req->orb.next_orb),
+ sbp2_pointer_to_addr(&req->orb.data_descriptor),
+ be32_to_cpu(req->orb.misc));
+
+ if (req->orb_pointer >> 32)
+ pr_debug("ORB with high bits set\n");
+
+ if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+ /* NULL next-ORB */
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_SRC(STATUS_SRC_ORB_FINISHED));
+ } else {
+ /* non-NULL next-ORB */
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_SRC(STATUS_SRC_ORB_CONTINUING));
+ }
+
+ queue_work(sbp_workqueue, &req->work);
+
+ /* check if we should carry on processing */
+ if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+ /* null next_orb */
+ atomic_cmpxchg(&agent->state, AGENT_STATE_ACTIVE,
+ AGENT_STATE_SUSPENDED);
+ } else {
+ pr_debug("non-NULL next-ORB\n");
+ agent->orb_pointer = sbp2_pointer_to_addr(&req->orb.next_orb);
+ queue_work(sbp_workqueue, &agent->work);
+ }
+}
+
+struct sbp_target_agent *sbp_target_agent_register(
+ struct sbp_login_descriptor *login)
+{
+ struct sbp_target_agent *agent;
+ int ret;
+
+ agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+ if (!agent)
+ return ERR_PTR(-ENOMEM);
+
+ agent->handler.length = 0x20;
+ agent->handler.address_callback = tgt_agent_rw;
+ agent->handler.callback_data = agent;
+
+ agent->login = login;
+ atomic_set(&agent->state, AGENT_STATE_RESET);
+ INIT_WORK(&agent->work, tgt_agent_fetch_work);
+ agent->orb_pointer = (u64)-1;
+
+ ret = fw_core_add_address_handler(&agent->handler,
+ &sbp_register_region);
+ if (ret < 0) {
+ kfree(agent);
+ return ERR_PTR(ret);
+ }
+
+ return agent;
+}
+
+void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+ if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
+ flush_work_sync(&agent->work);
+
+ fw_core_remove_address_handler(&agent->handler);
+ kfree(agent);
+}
+
diff --git a/drivers/target/sbp/sbp_target_agent.h b/drivers/target/sbp/sbp_target_agent.h
new file mode 100644
index 0000000..cddcc2f
--- /dev/null
+++ b/drivers/target/sbp/sbp_target_agent.h
@@ -0,0 +1,27 @@
+
+struct sbp_target_agent {
+ struct fw_address_handler handler;
+ struct sbp_login_descriptor *login;
+ atomic_t state;
+ struct work_struct work;
+ u64 orb_pointer;
+};
+
+struct sbp_target_request {
+ struct sbp_target_agent *agent;
+ u64 orb_pointer;
+ struct sbp_command_block_orb orb;
+ struct sbp_status_block status;
+ struct work_struct work;
+
+ struct se_cmd se_cmd;
+ struct sbp_page_table_entry *pg_tbl;
+ void *cmd_buf;
+ void *data_buf;
+
+ unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+struct sbp_target_agent *sbp_target_agent_register(
+ struct sbp_login_descriptor *login);
+void sbp_target_agent_unregister(struct sbp_target_agent *agent);
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}
2012-02-15 14:47 ` [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h} Chris Boot
@ 2012-02-15 21:27 ` Stefan Richter
2012-02-16 11:25 ` Chris Boot
0 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-02-15 21:27 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 15 Chris Boot wrote:
> --- /dev/null
> +++ b/drivers/target/sbp/sbp_target_agent.c
[...]
> +static int tgt_agent_rw_orb_pointer(struct fw_card *card,
> + int tcode, int generation, void *data,
> + struct sbp_target_agent *agent)
> +{
> + struct sbp2_pointer *ptr = data;
> + int ret;
> +
> + switch (tcode) {
> + case TCODE_WRITE_BLOCK_REQUEST:
> + smp_wmb();
> + atomic_cmpxchg(&agent->state,
> + AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
> + smp_wmb();
> + if (atomic_cmpxchg(&agent->state,
> + AGENT_STATE_SUSPENDED,
> + AGENT_STATE_ACTIVE)
> + != AGENT_STATE_SUSPENDED)
> + return RCODE_CONFLICT_ERROR;
> + smp_wmb();
Why the double state change?
And as asked at the patch, which writes are the barriers meant to order,
and how does the corresponding read side look like? Or are these barriers
not actually needed after all?
[...]
> +void sbp_target_agent_unregister(struct sbp_target_agent *agent)
> +{
> + if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
> + flush_work_sync(&agent->work);
> +
> + fw_core_remove_address_handler(&agent->handler);
> + kfree(agent);
> +}
So, asking once more without having read the code in full yet: Are you
sure that agent->state is not going to change anymore after you tested it
here?
--
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}
2012-02-15 21:27 ` Stefan Richter
@ 2012-02-16 11:25 ` Chris Boot
2012-02-18 14:59 ` Stefan Richter
0 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-02-16 11:25 UTC (permalink / raw)
To: Stefan Richter
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On 15/02/2012 21:27, Stefan Richter wrote:
> On Feb 15 Chris Boot wrote:
>> --- /dev/null
>> +++ b/drivers/target/sbp/sbp_target_agent.c
> [...]
>> +static int tgt_agent_rw_orb_pointer(struct fw_card *card,
>> + int tcode, int generation, void *data,
>> + struct sbp_target_agent *agent)
>> +{
>> + struct sbp2_pointer *ptr = data;
>> + int ret;
>> +
>> + switch (tcode) {
>> + case TCODE_WRITE_BLOCK_REQUEST:
>> + smp_wmb();
>> + atomic_cmpxchg(&agent->state,
>> + AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
>> + smp_wmb();
>> + if (atomic_cmpxchg(&agent->state,
>> + AGENT_STATE_SUSPENDED,
>> + AGENT_STATE_ACTIVE)
>> + != AGENT_STATE_SUSPENDED)
>> + return RCODE_CONFLICT_ERROR;
>> + smp_wmb();
>
> Why the double state change?
Because the SBP spec differentiates between the RESET state, which
happens after the agent initialises or is sent an explicit reset
request, and when it's suspended between requests...
> And as asked at the patch, which writes are the barriers meant to order,
> and how does the corresponding read side look like? Or are these barriers
> not actually needed after all?
...of course this is another time when my use of atomics and memory
barriers is entirely wrong. I should most likely be using locking here.
> [...]
>> +void sbp_target_agent_unregister(struct sbp_target_agent *agent)
>> +{
>> + if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
>> + flush_work_sync(&agent->work);
>> +
>> + fw_core_remove_address_handler(&agent->handler);
>> + kfree(agent);
>> +}
>
> So, asking once more without having read the code in full yet: Are you
> sure that agent->state is not going to change anymore after you tested it
> here?
Nope. At least in this case I can unregister the address handler before
I check if I need to flush the work item.
Cheers,
Chris
--
Chris Boot
bootc@bootc.net
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}
2012-02-16 11:25 ` Chris Boot
@ 2012-02-18 14:59 ` Stefan Richter
2012-02-18 15:05 ` Chris Boot
0 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-02-18 14:59 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Feb 16 Chris Boot wrote:
> On 15/02/2012 21:27, Stefan Richter wrote:
> > On Feb 15 Chris Boot wrote:
> >> --- /dev/null
> >> +++ b/drivers/target/sbp/sbp_target_agent.c
> > [...]
> >> +static int tgt_agent_rw_orb_pointer(struct fw_card *card,
> >> + int tcode, int generation, void *data,
> >> + struct sbp_target_agent *agent)
> >> +{
> >> + struct sbp2_pointer *ptr = data;
> >> + int ret;
> >> +
> >> + switch (tcode) {
> >> + case TCODE_WRITE_BLOCK_REQUEST:
> >> + smp_wmb();
> >> + atomic_cmpxchg(&agent->state,
> >> + AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
> >> + smp_wmb();
> >> + if (atomic_cmpxchg(&agent->state,
> >> + AGENT_STATE_SUSPENDED,
> >> + AGENT_STATE_ACTIVE)
> >> + != AGENT_STATE_SUSPENDED)
> >> + return RCODE_CONFLICT_ERROR;
> >> + smp_wmb();
> >
> > Why the double state change?
>
> Because the SBP spec differentiates between the RESET state, which
> happens after the agent initialises or is sent an explicit reset
> request, and when it's suspended between requests...
OK, right, there are the state transitions Reset-->Active and
Suspended-->Active. Though you implement the former as a swift
Reset-->Suspended-->Active. Which does indeed work, provided that there
is no other concurrent context which could transition from Suspended to
Anything-but-Active.
> > And as asked at the patch, which writes are the barriers meant to order,
> > and how does the corresponding read side look like? Or are these barriers
> > not actually needed after all?
>
> ...of course this is another time when my use of atomics and memory
> barriers is entirely wrong. I should most likely be using locking here.
>
> > [...]
> >> +void sbp_target_agent_unregister(struct sbp_target_agent *agent)
> >> +{
> >> + if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
> >> + flush_work_sync(&agent->work);
> >> +
> >> + fw_core_remove_address_handler(&agent->handler);
> >> + kfree(agent);
> >> +}
> >
> > So, asking once more without having read the code in full yet: Are you
> > sure that agent->state is not going to change anymore after you tested it
> > here?
>
> Nope. At least in this case I can unregister the address handler before
> I check if I need to flush the work item.
Yep, first unregister the handler, then wait for the work to finish, then
free the data.
And as discussed off-list today, firewire-core should be improved to
guarantee you that the handler isn't still running anywhere when
fw_core_remove_address_handler() returns.
--
Stefan Richter
-=====-===-- --=- =--=-
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}
2012-02-18 14:59 ` Stefan Richter
@ 2012-02-18 15:05 ` Chris Boot
0 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-18 15:05 UTC (permalink / raw)
To: Stefan Richter
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On 18 Feb 2012, at 14:59, Stefan Richter <stefanr@s5r6.in-berlin.de> wrote:
> On Feb 16 Chris Boot wrote:
>> On 15/02/2012 21:27, Stefan Richter wrote:
>>> On Feb 15 Chris Boot wrote:
>>>> --- /dev/null
>>>> +++ b/drivers/target/sbp/sbp_target_agent.c
>>> [...]
>>>> +static int tgt_agent_rw_orb_pointer(struct fw_card *card,
>>>> + int tcode, int generation, void *data,
>>>> + struct sbp_target_agent *agent)
>>>> +{
>>>> + struct sbp2_pointer *ptr = data;
>>>> + int ret;
>>>> +
>>>> + switch (tcode) {
>>>> + case TCODE_WRITE_BLOCK_REQUEST:
>>>> + smp_wmb();
>>>> + atomic_cmpxchg(&agent->state,
>>>> + AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
>>>> + smp_wmb();
>>>> + if (atomic_cmpxchg(&agent->state,
>>>> + AGENT_STATE_SUSPENDED,
>>>> + AGENT_STATE_ACTIVE)
>>>> + != AGENT_STATE_SUSPENDED)
>>>> + return RCODE_CONFLICT_ERROR;
>>>> + smp_wmb();
>>>
>>> Why the double state change?
>>
>> Because the SBP spec differentiates between the RESET state, which
>> happens after the agent initialises or is sent an explicit reset
>> request, and when it's suspended between requests...
>
> OK, right, there are the state transitions Reset-->Active and
> Suspended-->Active. Though you implement the former as a swift
> Reset-->Suspended-->Active. Which does indeed work, provided that there
> is no other concurrent context which could transition from Suspended to
> Anything-but-Active.
I'll rewrite this bit to use a lock to protect the state struct member so it isn't so strange. That would also avoid the unusual double state change.
>>> And as asked at the patch, which writes are the barriers meant to order,
>>> and how does the corresponding read side look like? Or are these barriers
>>> not actually needed after all?
>>
>> ...of course this is another time when my use of atomics and memory
>> barriers is entirely wrong. I should most likely be using locking here.
>>
>>> [...]
>>>> +void sbp_target_agent_unregister(struct sbp_target_agent *agent)
>>>> +{
>>>> + if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
>>>> + flush_work_sync(&agent->work);
>>>> +
>>>> + fw_core_remove_address_handler(&agent->handler);
>>>> + kfree(agent);
>>>> +}
>>>
>>> So, asking once more without having read the code in full yet: Are you
>>> sure that agent->state is not going to change anymore after you tested it
>>> here?
>>
>> Nope. At least in this case I can unregister the address handler before
>> I check if I need to flush the work item.
>
> Yep, first unregister the handler, then wait for the work to finish, then
> free the data.
>
> And as discussed off-list today, firewire-core should be improved to
> guarantee you that the handler isn't still running anywhere when
> fw_core_remove_address_handler() returns.
Great, thanks for clearing that up.
Cheers,
Chris
--
Chris Boot
bootc@bootc.net
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH v2 10/11] firewire-sbp-target: Add sbp_scsi_cmnd.{c,h}
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
` (8 preceding siblings ...)
2012-02-15 14:47 ` [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h} Chris Boot
@ 2012-02-15 14:47 ` Chris Boot
2012-02-15 14:47 ` [PATCH v2 11/11] firewire-sbp-target: Add to target Kconfig and Makefile Chris Boot
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
11 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-15 14:47 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
Miscellaneous functions for dealing with SCSI commands, status, sense
data and data read/write. This is where the real grunt work of pushing
data in and out of the FireWire bus happens.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_scsi_cmnd.c | 360 ++++++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_scsi_cmnd.h | 6 +
2 files changed, 366 insertions(+), 0 deletions(-)
create mode 100644 drivers/target/sbp/sbp_scsi_cmnd.c
create mode 100644 drivers/target/sbp/sbp_scsi_cmnd.h
diff --git a/drivers/target/sbp/sbp_scsi_cmnd.c b/drivers/target/sbp/sbp_scsi_cmnd.c
new file mode 100644
index 0000000..e0b4b71
--- /dev/null
+++ b/drivers/target/sbp/sbp_scsi_cmnd.c
@@ -0,0 +1,360 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+
+#include "sbp_base.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+/*
+ * Wraps fw_run_transaction taking into account page size and max payload, and
+ * retries the transaction if it fails
+ */
+static int sbp_run_transaction(struct sbp_target_request *req, int tcode,
+ unsigned long long offset, void *payload, size_t length)
+{
+ struct sbp_login_descriptor *login = req->agent->login;
+ struct sbp_session *sess = login->sess;
+ int ret, speed, max_payload, pg_size, seg_off = 0, seg_len;
+
+ speed = CMDBLK_ORB_SPEED(be32_to_cpu(req->orb.misc));
+ max_payload = 4 << CMDBLK_ORB_MAX_PAYLOAD(be32_to_cpu(req->orb.misc));
+ pg_size = CMDBLK_ORB_PG_SIZE(be32_to_cpu(req->orb.misc));
+
+ if (pg_size) {
+ pr_err("sbp_run_transaction: page size ignored\n");
+ pg_size = 0x100 << pg_size;
+ }
+
+ while (seg_off < length) {
+ seg_len = length - seg_off;
+ if (seg_len > max_payload)
+ seg_len = max_payload;
+
+ /* FIXME: take page_size into account */
+
+ /* FIXME: retry failed data transfers */
+ ret = fw_run_transaction(sess->card, tcode,
+ sess->node_id, sess->generation, speed,
+ offset + seg_off, payload + seg_off, seg_len);
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("sbp_run_transaction: txn failed: %x\n", ret);
+ return -EIO;
+ }
+
+ seg_off += seg_len;
+ }
+
+ return 0;
+}
+
+static int sbp_fetch_command(struct sbp_target_request *req)
+{
+ int ret, cmd_len, copy_len;
+
+ cmd_len = scsi_command_size(req->orb.command_block);
+
+ req->cmd_buf = kmalloc(cmd_len, GFP_KERNEL);
+ if (!req->cmd_buf)
+ return -ENOMEM;
+
+ memcpy(req->cmd_buf, req->orb.command_block,
+ min_t(int, cmd_len, sizeof(req->orb.command_block)));
+
+ if (cmd_len > sizeof(req->orb.command_block)) {
+ pr_debug("sbp_fetch_command: filling in long command\n");
+ copy_len = cmd_len - sizeof(req->orb.command_block);
+
+ ret = sbp_run_transaction(req, TCODE_READ_BLOCK_REQUEST,
+ req->orb_pointer + sizeof(req->orb),
+ req->cmd_buf + sizeof(req->orb.command_block),
+ copy_len);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sbp_fetch_page_table(struct sbp_target_request *req)
+{
+ int pg_tbl_sz, ret;
+ struct sbp_page_table_entry *pg_tbl;
+
+ if (!CMDBLK_ORB_PG_TBL_PRESENT(be32_to_cpu(req->orb.misc)))
+ return 0;
+
+ pg_tbl_sz = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc)) *
+ sizeof(struct sbp_page_table_entry);
+
+ pg_tbl = kmalloc(pg_tbl_sz, GFP_KERNEL);
+ if (!pg_tbl)
+ return -ENOMEM;
+
+ ret = sbp_run_transaction(req, TCODE_READ_BLOCK_REQUEST,
+ sbp2_pointer_to_addr(&req->orb.data_descriptor),
+ pg_tbl, pg_tbl_sz);
+ if (ret) {
+ kfree(pg_tbl);
+ return ret;
+ }
+
+ req->pg_tbl = pg_tbl;
+ return 0;
+}
+
+static void sbp_calc_data_length_direction(struct sbp_target_request *req,
+ u32 *data_len, enum dma_data_direction *data_dir)
+{
+ int data_size, direction, idx;
+
+ data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+ direction = CMDBLK_ORB_DIRECTION(be32_to_cpu(req->orb.misc));
+
+ if (!data_size) {
+ *data_len = 0;
+ *data_dir = DMA_NONE;
+ return;
+ }
+
+ *data_dir = direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (req->pg_tbl) {
+ *data_len = 0;
+ for (idx = 0; idx < data_size; idx++) {
+ *data_len += be16_to_cpu(
+ req->pg_tbl[idx].segment_length);
+ }
+ } else {
+ *data_len = data_size;
+ }
+}
+
+void sbp_handle_command(struct sbp_target_request *req)
+{
+ struct sbp_login_descriptor *login = req->agent->login;
+ struct sbp_session *sess = login->sess;
+ int ret, unpacked_lun;
+ u32 data_length;
+ enum dma_data_direction data_dir;
+
+ ret = sbp_fetch_command(req);
+ if (ret) {
+ pr_debug("sbp_handle_command: fetch command failed: %d\n", ret);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ }
+
+ ret = sbp_fetch_page_table(req);
+ if (ret) {
+ pr_debug("sbp_handle_command: fetch page table failed: %d\n",
+ ret);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ }
+
+ unpacked_lun = req->agent->login->lun->unpacked_lun;
+ sbp_calc_data_length_direction(req, &data_length, &data_dir);
+
+ pr_debug("sbp_handle_command unpacked_lun:%d data_len:%d data_dir:%d\n",
+ unpacked_lun, data_length, data_dir);
+
+ target_submit_cmd(&req->se_cmd, sess->se_sess, req->cmd_buf,
+ req->sense_buf, unpacked_lun, data_length,
+ MSG_SIMPLE_TAG, data_dir, 0);
+}
+
+/*
+ * DMA_TO_DEVICE = read from initiator (SCSI WRITE)
+ * DMA_FROM_DEVICE = write to initiator (SCSI READ)
+ */
+int sbp_rw_data(struct sbp_target_request *req)
+{
+ int ret, tcode;
+
+ tcode = (req->se_cmd.data_direction == DMA_TO_DEVICE) ?
+ TCODE_READ_BLOCK_REQUEST :
+ TCODE_WRITE_BLOCK_REQUEST;
+
+ if (req->pg_tbl) {
+ int idx, offset = 0, data_size;
+
+ data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+
+ for (idx = 0; idx < data_size; idx++) {
+ struct sbp_page_table_entry *pte = &req->pg_tbl[idx];
+ int pte_len = be16_to_cpu(pte->segment_length);
+ u64 pte_offset =
+ (u64)be16_to_cpu(pte->segment_base_hi) << 32 |
+ be32_to_cpu(pte->segment_base_lo);
+
+ ret = sbp_run_transaction(req, tcode, pte_offset,
+ req->data_buf + offset, pte_len);
+ if (ret)
+ break;
+
+ offset += pte_len;
+ }
+ } else {
+ ret = sbp_run_transaction(req, tcode,
+ sbp2_pointer_to_addr(&req->orb.data_descriptor),
+ req->data_buf, req->se_cmd.data_length);
+ }
+
+ return ret;
+}
+
+int sbp_send_status(struct sbp_target_request *req)
+{
+ int ret, length;
+ struct sbp_login_descriptor *login = req->agent->login;
+
+ length = (((be32_to_cpu(req->status.status) >> 24) & 0x07) + 1) * 4;
+
+ ret = sbp_run_transaction(req, TCODE_WRITE_BLOCK_REQUEST,
+ login->status_fifo_addr, &req->status, length);
+ if (ret) {
+ pr_debug("sbp_send_status: write failed: %d\n", ret);
+ return ret;
+ }
+
+ pr_debug("sbp_send_status: status write complete for ORB: 0x%llx\n",
+ req->orb_pointer);
+
+ return 0;
+}
+
+static void sbp_sense_mangle(struct sbp_target_request *req)
+{
+ struct se_cmd *se_cmd = &req->se_cmd;
+ u8 *sense = req->sense_buf;
+ u8 *status = req->status.data;
+
+ WARN_ON(se_cmd->scsi_sense_length < 18);
+
+ switch (sense[0] & 0x7f) { /* sfmt */
+ case 0x70: /* current, fixed */
+ status[0] = 0 << 6;
+ break;
+ case 0x71: /* deferred, fixed */
+ status[0] = 1 << 6;
+ break;
+ case 0x72: /* current, descriptor */
+ case 0x73: /* deferred, descriptor */
+ default:
+ /*
+ * TODO: SBP-3 specifies what we should do with descriptor
+ * format sense data
+ */
+ pr_err("sbp_send_sense: unknown sense format: 0x%x\n",
+ sense[0]);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQUEST_ABORTED));
+ return;
+ }
+
+ status[0] |= se_cmd->scsi_status & 0x3f;/* status */
+ status[1] =
+ (sense[0] & 0x80) | /* valid */
+ ((sense[2] & 0xe0) >> 1) | /* mark, eom, ili */
+ (sense[2] & 0x0f); /* sense_key */
+ status[2] = se_cmd->scsi_asc; /* sense_code */
+ status[3] = se_cmd->scsi_ascq; /* sense_qualifier */
+
+ /* information */
+ status[4] = sense[3];
+ status[5] = sense[4];
+ status[6] = sense[5];
+ status[7] = sense[6];
+
+ /* CDB-dependent */
+ status[8] = sense[8];
+ status[9] = sense[9];
+ status[10] = sense[10];
+ status[11] = sense[11];
+
+ /* fru */
+ status[12] = sense[14];
+
+ /* sense_key-dependent */
+ status[13] = sense[15];
+ status[14] = sense[16];
+ status[15] = sense[17];
+
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(5) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+int sbp_send_sense(struct sbp_target_request *req)
+{
+ struct se_cmd *se_cmd = &req->se_cmd;
+
+ if (se_cmd->scsi_sense_length) {
+ sbp_sense_mangle(req);
+ } else {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+ }
+
+ return sbp_send_status(req);
+}
+
+void sbp_free_request(struct sbp_target_request *req)
+{
+ kfree(req->pg_tbl);
+ kfree(req->cmd_buf);
+ kfree(req->data_buf);
+ kfree(req);
+}
diff --git a/drivers/target/sbp/sbp_scsi_cmnd.h b/drivers/target/sbp/sbp_scsi_cmnd.h
new file mode 100644
index 0000000..5e82b25
--- /dev/null
+++ b/drivers/target/sbp/sbp_scsi_cmnd.h
@@ -0,0 +1,6 @@
+
+void sbp_handle_command(struct sbp_target_request *req);
+int sbp_rw_data(struct sbp_target_request *req);
+int sbp_send_status(struct sbp_target_request *req);
+int sbp_send_sense(struct sbp_target_request *req);
+void sbp_free_request(struct sbp_target_request *req);
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH v2 11/11] firewire-sbp-target: Add to target Kconfig and Makefile
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
` (9 preceding siblings ...)
2012-02-15 14:47 ` [PATCH v2 10/11] firewire-sbp-target: Add sbp_scsi_cmnd.{c,h} Chris Boot
@ 2012-02-15 14:47 ` Chris Boot
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
11 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-02-15 14:47 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This commit also adds an entry to the MAINTAINERS file.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
MAINTAINERS | 9 +++++++++
drivers/target/Kconfig | 1 +
drivers/target/Makefile | 1 +
3 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index c0f348d..252aa72 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2695,6 +2695,15 @@ T: git git://git.alsa-project.org/alsa-kernel.git
S: Maintained
F: sound/firewire/
+FIREWIRE SBP-2 TARGET
+M: Chris Boot <bootc@bootc.net>
+L: linux-scsi@vger.kernel.org
+L: target-devel@vger.kernel.org
+L: linux1394-devel@lists.sourceforge.net
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core-2.6.git master
+S: Maintained
+F: drivers/target/sbp/
+
FIREWIRE SUBSYSTEM
M: Stefan Richter <stefanr@s5r6.in-berlin.de>
L: linux1394-devel@lists.sourceforge.net
diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig
index fc5fa9f..2cfa467 100644
--- a/drivers/target/Kconfig
+++ b/drivers/target/Kconfig
@@ -41,5 +41,6 @@ source "drivers/target/tcm_fc/Kconfig"
source "drivers/target/iscsi/Kconfig"
source "drivers/target/tcm_vhost/Kconfig"
source "drivers/target/usb-gadget/Kconfig"
+source "drivers/target/sbp/Kconfig"
endif
diff --git a/drivers/target/Makefile b/drivers/target/Makefile
index 6b5f526..1ae8862 100644
--- a/drivers/target/Makefile
+++ b/drivers/target/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_TCM_FC) += tcm_fc/
obj-$(CONFIG_ISCSI_TARGET) += iscsi/
obj-$(CONFIG_TCM_VHOST) += tcm_vhost/
obj-$(CONFIG_TARGET_USB_GADGET) += usb-gadget/
+obj-$(CONFIG_FIREWIRE_SBP_TARGET) += sbp/
--
1.7.9
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target
2012-02-15 14:47 ` [PATCH v2 00/11] " Chris Boot
` (10 preceding siblings ...)
2012-02-15 14:47 ` [PATCH v2 11/11] firewire-sbp-target: Add to target Kconfig and Makefile Chris Boot
@ 2012-04-11 14:20 ` Chris Boot
2012-04-11 14:20 ` [PATCH 01/11] firewire: Add function to get speed from opaque struct fw_request Chris Boot
` (12 more replies)
11 siblings, 13 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-11 14:20 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr
The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection
as a SCSI transport. This module uses the SCSI Target framework to
expose LUNs to other machines attached to a FireWire bus, in effect
acting as a FireWire hard disk similar to FireWire Target Disk mode on
many Apple computers.
Sorry this latest revision has been a long time coming. I was trying to
chase down a crashing bug (but haven't been able to replicate it), changed
my mind about a large portion of the code twice, and life got in the way as
well!
Changes in v3:
* Updates for target framework API changes
* Attempt to make headers self-contained
* Remove bad use of atomics and memory barriers
* Rework use of locking
* Use system workqueues
* Wrap fw_run_transaction() to retry failed transactions
* Coding style fixes
* Fix a few bugs
* Overhaul sbp_rw_data()
* Overhaul target fetch agent
Changes in v2:
* Fixed some copy & paste issues
* Updated Kconfig (wording, depends, 'default n')
* Removed some unnecessary EXPORT_SYMBOL()s
* Merged sbp_util.{c,h} into sbp_configfs.c and sbp_base.h
* Merged sbp_proto.{c,h} into sbp_fabric.{c,h}
* Cleaned up comments and several printks
* Fixed a few minor bugs
* Create & use our own workqueue instead of using fw_workqueue
* Dropped the unused TFO->new_cmd_map and sbp_new_cmd()
* Overhauled and simplified tgt_agent_fetch_work()
* Removed some redundant members of struct sbp_target_request
* Removed struct sbp_lun and code to maintain redundant LUN list
* Added spinlock to struct sbp_session and use locking throughout
* Moved fw_card_{get,put,release}() into linux/firewire.h
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH 01/11] firewire: Add function to get speed from opaque struct fw_request
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
@ 2012-04-11 14:20 ` Chris Boot
2012-04-11 14:20 ` [PATCH 02/11] firewire: Move fw_card kref functions into linux/firewire.h Chris Boot
` (11 subsequent siblings)
12 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-11 14:20 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
Sometimes it's useful to know the FireWire speed of the request that has
just come in to a fw_address_handler callback. As struct fw_request is
opaque we can't peek inside to get the speed out of the struct fw_packet
that's just inside. For example, the SBP-2 spec says:
"The speed at which the block write request to the MANAGEMENT_AGENT
register is received shall determine the speed used by the target for
all subsequent requests to read the initiator’s configuration ROM, fetch
ORB’s from initiator memory or store status at the initiator’s
status_FIFO. Command block ORB’s separately specify the speed for
requests addressed to the data buffer or page table."
[ ANSI T10/1155D Revision 4 page 53/54 ]
Signed-off-by: Chris Boot <bootc@bootc.net>
Acked-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Cc: Clemens Ladisch <clemens@ladisch.de>
---
drivers/firewire/core-transaction.c | 9 +++++++++
include/linux/firewire.h | 1 +
2 files changed, 10 insertions(+)
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index dea2dcc..db8a965 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -820,6 +820,15 @@ void fw_send_response(struct fw_card *card,
}
EXPORT_SYMBOL(fw_send_response);
+/**
+ * fw_get_request_speed() - returns speed at which the @request was received
+ */
+int fw_get_request_speed(struct fw_request *request)
+{
+ return request->response.speed;
+}
+EXPORT_SYMBOL(fw_get_request_speed);
+
static void handle_exclusive_region_request(struct fw_card *card,
struct fw_packet *p,
struct fw_request *request,
diff --git a/include/linux/firewire.h b/include/linux/firewire.h
index cdc9b71..4683130 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -325,6 +325,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler,
void fw_core_remove_address_handler(struct fw_address_handler *handler);
void fw_send_response(struct fw_card *card,
struct fw_request *request, int rcode);
+int fw_get_request_speed(struct fw_request *request);
void fw_send_request(struct fw_card *card, struct fw_transaction *t,
int tcode, int destination_id, int generation, int speed,
unsigned long long offset, void *payload, size_t length,
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 02/11] firewire: Move fw_card kref functions into linux/firewire.h
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
2012-04-11 14:20 ` [PATCH 01/11] firewire: Add function to get speed from opaque struct fw_request Chris Boot
@ 2012-04-11 14:20 ` Chris Boot
2012-04-11 14:20 ` [PATCH 03/11] firewire-sbp-target: Add Kconfig, Makefile and TODO Chris Boot
` (10 subsequent siblings)
12 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-11 14:20 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
When writing a firewire driver that doesn't deal with struct fw_device
objects (e.g. it only publishes FireWire units and doesn't subscribe to
them), you likely need to keep referenced to struct fw_card objects so
that you can send messages to other nodes. This patch moves
fw_card_put(), fw_card_get() and fw_card_release() into the public
include/linux/firewire.h header instead of drivers/firewire/core.h, and
adds EXPORT_SYMBOL_GPL(fw_card_release).
The firewire-sbp-target module requires these so it can keep a reference
to the fw_card object in order that it can fetch ORBs to execute and
read/write related data and status information.
Signed-off-by: Chris Boot <bootc@bootc.net>
Acked-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Cc: Clemens Ladisch <clemens@ladisch.de>
---
drivers/firewire/core-card.c | 1 +
drivers/firewire/core.h | 15 ---------------
include/linux/firewire.h | 14 ++++++++++++++
3 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index cc595eb..f5552b3 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -676,6 +676,7 @@ void fw_card_release(struct kref *kref)
complete(&card->done);
}
+EXPORT_SYMBOL_GPL(fw_card_release);
void fw_core_remove_card(struct fw_card *card)
{
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index 9047f55..b5a2f61 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -120,21 +120,6 @@ int fw_compute_block_crc(__be32 *block);
void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset);
void fw_schedule_bm_work(struct fw_card *card, unsigned long delay);
-static inline struct fw_card *fw_card_get(struct fw_card *card)
-{
- kref_get(&card->kref);
-
- return card;
-}
-
-void fw_card_release(struct kref *kref);
-
-static inline void fw_card_put(struct fw_card *card)
-{
- kref_put(&card->kref, fw_card_release);
-}
-
-
/* -cdev */
extern const struct file_operations fw_device_ops;
diff --git a/include/linux/firewire.h b/include/linux/firewire.h
index 4683130..e83c24a 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -135,6 +135,20 @@ struct fw_card {
__be32 maint_utility_register;
};
+static inline struct fw_card *fw_card_get(struct fw_card *card)
+{
+ kref_get(&card->kref);
+
+ return card;
+}
+
+void fw_card_release(struct kref *kref);
+
+static inline void fw_card_put(struct fw_card *card)
+{
+ kref_put(&card->kref, fw_card_release);
+}
+
struct fw_attribute_group {
struct attribute_group *groups[2];
struct attribute_group group;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 03/11] firewire-sbp-target: Add Kconfig, Makefile and TODO
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
2012-04-11 14:20 ` [PATCH 01/11] firewire: Add function to get speed from opaque struct fw_request Chris Boot
2012-04-11 14:20 ` [PATCH 02/11] firewire: Move fw_card kref functions into linux/firewire.h Chris Boot
@ 2012-04-11 14:20 ` Chris Boot
2012-04-11 14:20 ` [PATCH 04/11] firewire-sbp-target: Add sbp_base.h header Chris Boot
` (9 subsequent siblings)
12 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-11 14:20 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection
as a SCSI transport. This module uses the SCSI Target framework to
expose LUNs to other machines attached to a FireWire bus, in effect
acting as a FireWire hard disk similar to FireWire Target Disk mode on
many Apple computers.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/Kconfig | 13 +++++++++++++
drivers/target/sbp/Makefile | 11 +++++++++++
drivers/target/sbp/TODO | 7 +++++++
3 files changed, 31 insertions(+)
create mode 100644 drivers/target/sbp/Kconfig
create mode 100644 drivers/target/sbp/Makefile
create mode 100644 drivers/target/sbp/TODO
diff --git a/drivers/target/sbp/Kconfig b/drivers/target/sbp/Kconfig
new file mode 100644
index 0000000..c9f85e9
--- /dev/null
+++ b/drivers/target/sbp/Kconfig
@@ -0,0 +1,13 @@
+
+config FIREWIRE_SBP_TARGET
+ tristate "FireWire SBP-2 fabric module"
+ depends on FIREWIRE && EXPERIMENTAL
+ help
+ Say Y or M here to enable SCSI target functionality over FireWire.
+ This enables you to expose SCSI devices to other nodes on the FireWire
+ bus, for example hard disks. Similar to FireWire Target Disk mode on
+ many Apple computers.
+
+ To compile this driver as a module, say M here: The module will be
+ called firewire-sbp-target.
+
diff --git a/drivers/target/sbp/Makefile b/drivers/target/sbp/Makefile
new file mode 100644
index 0000000..c8f73f4
--- /dev/null
+++ b/drivers/target/sbp/Makefile
@@ -0,0 +1,11 @@
+
+firewire-sbp-target-y += \
+ sbp_configfs.o \
+ sbp_fabric.o \
+ sbp_login.o \
+ sbp_management_agent.o \
+ sbp_scsi_cmnd.o \
+ sbp_target_agent.o
+
+obj-$(CONFIG_FIREWIRE_SBP_TARGET) += firewire-sbp-target.o
+
diff --git a/drivers/target/sbp/TODO b/drivers/target/sbp/TODO
new file mode 100644
index 0000000..eaec1c9
--- /dev/null
+++ b/drivers/target/sbp/TODO
@@ -0,0 +1,7 @@
+* Force-terminate sessions when disabling targets
+* Ability to have several SCSI commands in-flight (TCQ?)
+* Retry failed FireWire transactions a few times with exponential backoff
+* Take into account the page_size field for transfers and/or page tables
+* Handle descriptor format sense data
+* Implement QUERY LOGINS management ORB
+* Implement TASK MANAGEMENT functionality
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 04/11] firewire-sbp-target: Add sbp_base.h header
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (2 preceding siblings ...)
2012-04-11 14:20 ` [PATCH 03/11] firewire-sbp-target: Add Kconfig, Makefile and TODO Chris Boot
@ 2012-04-11 14:20 ` Chris Boot
2012-04-11 14:20 ` [PATCH 05/11] firewire-sbp-target: Add sbp_configfs.c Chris Boot
` (8 subsequent siblings)
12 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-11 14:20 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This header contains defines and structures that are common to many of
the modules of the target code. This includes SBP-2 protocol structures
and constants as well as a few structs for setting up the target, LUN
login information and session setup.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_base.h | 211 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 211 insertions(+)
create mode 100644 drivers/target/sbp/sbp_base.h
diff --git a/drivers/target/sbp/sbp_base.h b/drivers/target/sbp/sbp_base.h
new file mode 100644
index 0000000..fa6a39b
--- /dev/null
+++ b/drivers/target/sbp/sbp_base.h
@@ -0,0 +1,211 @@
+#ifndef _SBP_BASE_H
+#define _SBP_BASE_H
+
+#include <linux/firewire.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <target/target_core_base.h>
+
+#define SBP_VERSION "v0.1"
+#define SBP_NAMELEN 32
+
+#define SBP_ORB_FETCH_SIZE 8
+
+#define MANAGEMENT_AGENT_STATE_IDLE 0
+#define MANAGEMENT_AGENT_STATE_BUSY 1
+
+#define ORB_NOTIFY(v) (((v) >> 31) & 0x01)
+#define ORB_REQUEST_FORMAT(v) (((v) >> 29) & 0x03)
+
+#define MANAGEMENT_ORB_FUNCTION(v) (((v) >> 16) & 0x0f)
+
+#define MANAGEMENT_ORB_FUNCTION_LOGIN 0x0
+#define MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS 0x1
+#define MANAGEMENT_ORB_FUNCTION_RECONNECT 0x3
+#define MANAGEMENT_ORB_FUNCTION_SET_PASSWORD 0x4
+#define MANAGEMENT_ORB_FUNCTION_LOGOUT 0x7
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK 0xb
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET 0xc
+#define MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET 0xe
+#define MANAGEMENT_ORB_FUNCTION_TARGET_RESET 0xf
+
+#define LOGIN_ORB_EXCLUSIVE(v) (((v) >> 28) & 0x01)
+#define LOGIN_ORB_RESERVED(v) (((v) >> 24) & 0x0f)
+#define LOGIN_ORB_RECONNECT(v) (((v) >> 20) & 0x0f)
+#define LOGIN_ORB_LUN(v) (((v) >> 0) & 0xffff)
+#define LOGIN_ORB_PASSWORD_LENGTH(v) (((v) >> 16) & 0xffff)
+#define LOGIN_ORB_RESPONSE_LENGTH(v) (((v) >> 0) & 0xffff)
+
+#define RECONNECT_ORB_LOGIN_ID(v) (((v) >> 0) & 0xffff)
+#define LOGOUT_ORB_LOGIN_ID(v) (((v) >> 0) & 0xffff)
+
+#define CMDBLK_ORB_DIRECTION(v) (((v) >> 27) & 0x01)
+#define CMDBLK_ORB_SPEED(v) (((v) >> 24) & 0x07)
+#define CMDBLK_ORB_MAX_PAYLOAD(v) (((v) >> 20) & 0x0f)
+#define CMDBLK_ORB_PG_TBL_PRESENT(v) (((v) >> 19) & 0x01)
+#define CMDBLK_ORB_PG_SIZE(v) (((v) >> 16) & 0x07)
+#define CMDBLK_ORB_DATA_SIZE(v) (((v) >> 0) & 0xffff)
+
+#define STATUS_BLOCK_SRC(v) (((v) & 0x03) << 30)
+#define STATUS_BLOCK_RESP(v) (((v) & 0x03) << 28)
+#define STATUS_BLOCK_DEAD(v) (((v) ? 1 : 0) << 27)
+#define STATUS_BLOCK_LEN(v) (((v) & 0x07) << 24)
+#define STATUS_BLOCK_SBP_STATUS(v) (((v) & 0xff) << 16)
+#define STATUS_BLOCK_ORB_OFFSET_HIGH(v) (((v) & 0xffff) << 0)
+
+#define STATUS_SRC_ORB_CONTINUING 0
+#define STATUS_SRC_ORB_FINISHED 1
+#define STATUS_SRC_UNSOLICITED 2
+
+#define STATUS_RESP_REQUEST_COMPLETE 0
+#define STATUS_RESP_TRANSPORT_FAILURE 1
+#define STATUS_RESP_ILLEGAL_REQUEST 2
+#define STATUS_RESP_VENDOR_DEPENDENT 3
+
+#define SBP_STATUS_OK 0
+#define SBP_STATUS_REQ_TYPE_NOTSUPP 1
+#define SBP_STATUS_SPEED_NOTSUPP 2
+#define SBP_STATUS_PAGE_SIZE_NOTSUPP 3
+#define SBP_STATUS_ACCESS_DENIED 4
+#define SBP_STATUS_LUN_NOTSUPP 5
+#define SBP_STATUS_PAYLOAD_TOO_SMALL 6
+/* 7 is reserved */
+#define SBP_STATUS_RESOURCES_UNAVAIL 8
+#define SBP_STATUS_FUNCTION_REJECTED 9
+#define SBP_STATUS_LOGIN_ID_UNKNOWN 10
+#define SBP_STATUS_DUMMY_ORB_COMPLETE 11
+#define SBP_STATUS_REQUEST_ABORTED 12
+#define SBP_STATUS_UNSPECIFIED_ERROR 0xff
+
+#define AGENT_STATE_RESET 0
+#define AGENT_STATE_ACTIVE 1
+#define AGENT_STATE_SUSPENDED 2
+#define AGENT_STATE_DEAD 3
+
+struct sbp2_pointer {
+ __be32 high;
+ __be32 low;
+};
+
+struct sbp_command_block_orb {
+ struct sbp2_pointer next_orb;
+ struct sbp2_pointer data_descriptor;
+ __be32 misc;
+ u8 command_block[12];
+};
+
+struct sbp_page_table_entry {
+ __be16 segment_length;
+ __be16 segment_base_hi;
+ __be32 segment_base_lo;
+};
+
+struct sbp_management_orb {
+ struct sbp2_pointer ptr1;
+ struct sbp2_pointer ptr2;
+ __be32 misc;
+ __be32 length;
+ struct sbp2_pointer status_fifo;
+};
+
+struct sbp_status_block {
+ __be32 status;
+ __be32 orb_low;
+ u8 data[24];
+};
+
+struct sbp_login_response_block {
+ __be32 misc;
+ struct sbp2_pointer command_block_agent;
+ __be32 reconnect_hold;
+};
+
+struct sbp_login_descriptor {
+ struct sbp_session *sess;
+ struct list_head link;
+
+ struct se_lun *lun;
+
+ u64 status_fifo_addr;
+ int exclusive;
+ u16 login_id;
+
+ struct sbp_target_agent *tgt_agt;
+};
+
+struct sbp_session {
+ spinlock_t lock;
+ struct se_session *se_sess;
+ struct list_head login_list;
+ struct delayed_work maint_work;
+
+ u64 guid; /* login_owner_EUI_64 */
+ int node_id; /* login_owner_ID */
+
+ struct fw_card *card;
+ int generation;
+ int speed;
+
+ int reconnect_hold;
+ u64 reconnect_expires;
+};
+
+struct sbp_nacl {
+ /* Initiator EUI-64 */
+ u64 guid;
+ /* ASCII formatted GUID for SBP Initiator port */
+ char iport_name[SBP_NAMELEN];
+ /* Returned by sbp_make_nodeacl() */
+ struct se_node_acl se_node_acl;
+};
+
+struct sbp_tpg {
+ /* Target portal group tag for TCM */
+ u16 tport_tpgt;
+ /* Pointer back to sbp_tport */
+ struct sbp_tport *tport;
+ /* Returned by sbp_make_tpg() */
+ struct se_portal_group se_tpg;
+};
+
+struct sbp_tport {
+ /* Target Unit Identifier (EUI-64) */
+ u64 guid;
+ /* Target port name */
+ char tport_name[SBP_NAMELEN];
+ /* Returned by sbp_make_tport() */
+ struct se_wwn tport_wwn;
+
+ struct sbp_tpg *tpg;
+
+ /* FireWire unit directory */
+ struct fw_descriptor unit_directory;
+
+ /* SBP Management Agent */
+ struct sbp_management_agent *mgt_agt;
+
+ /* Parameters */
+ int enable;
+ s32 directory_id;
+ int mgt_orb_timeout;
+ int max_reconnect_timeout;
+ int max_logins_per_lun;
+};
+
+extern struct target_fabric_configfs *sbp_fabric_configfs;
+extern const struct fw_address_region sbp_register_region;
+
+static inline u64 sbp2_pointer_to_addr(const struct sbp2_pointer *ptr)
+{
+ return (u64)(be32_to_cpu(ptr->high) & 0x0000ffff) << 32 |
+ (be32_to_cpu(ptr->low) & 0xfffffffc);
+}
+
+static inline void addr_to_sbp2_pointer(u64 addr, struct sbp2_pointer *ptr)
+{
+ ptr->high = cpu_to_be32(addr >> 32);
+ ptr->low = cpu_to_be32(addr);
+}
+
+#endif
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 05/11] firewire-sbp-target: Add sbp_configfs.c
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (3 preceding siblings ...)
2012-04-11 14:20 ` [PATCH 04/11] firewire-sbp-target: Add sbp_base.h header Chris Boot
@ 2012-04-11 14:20 ` Chris Boot
2012-04-11 14:20 ` [PATCH 06/11] firewire-sbp-target: Add sbp_fabric.{c,h} Chris Boot
` (7 subsequent siblings)
12 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-11 14:20 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This is used to glue the target framework's configfs code to the target
code, and what is used to create targets and link them to LUNs to
export. The code to create the FireWire unit directory to advertise
targets on the FireWire bus is also in here.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_configfs.c | 737 +++++++++++++++++++++++++++++++++++++
1 file changed, 737 insertions(+)
create mode 100644 drivers/target/sbp/sbp_configfs.c
diff --git a/drivers/target/sbp/sbp_configfs.c b/drivers/target/sbp/sbp_configfs.c
new file mode 100644
index 0000000..5399616
--- /dev/null
+++ b/drivers/target/sbp/sbp_configfs.c
@@ -0,0 +1,737 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_management_agent.h"
+
+/* Local pointer to allocated TCM configfs fabric module */
+struct target_fabric_configfs *sbp_fabric_configfs;
+
+/* FireWire address region for management and command block address handlers */
+const struct fw_address_region sbp_register_region = {
+ .start = CSR_REGISTER_BASE + 0x10000,
+ .end = 0x1000000000000ULL,
+};
+
+static const u32 sbp_unit_directory_template[] = {
+ 0x1200609e, /* unit_specifier_id: NCITS/T10 */
+ 0x13010483, /* unit_sw_version: 1155D Rev 4 */
+ 0x3800609e, /* command_set_specifier_id: NCITS/T10 */
+ 0x390104d8, /* command_set: SPC-2 */
+ 0x3b000000, /* command_set_revision: 0 */
+ 0x3c000001, /* firmware_revision: 1 */
+};
+
+static int sbp_count_se_tpg_luns(struct se_portal_group *tpg)
+{
+ int i, count = 0;
+
+ spin_lock(&tpg->tpg_lun_lock);
+ for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+ struct se_lun *se_lun = tpg->tpg_lun_list[i];
+
+ if (se_lun->lun_status == TRANSPORT_LUN_STATUS_FREE)
+ continue;
+
+ count++;
+ }
+ spin_unlock(&tpg->tpg_lun_lock);
+
+ return count;
+}
+
+static int sbp_update_unit_directory(struct sbp_tport *tport)
+{
+ int num_luns, num_entries, idx = 0, mgt_agt_addr, ret, i;
+ u32 *data;
+
+ if (tport->unit_directory.data) {
+ fw_core_remove_descriptor(&tport->unit_directory);
+ kfree(tport->unit_directory.data);
+ tport->unit_directory.data = NULL;
+ }
+
+ if (!tport->enable || !tport->tpg)
+ return 0;
+
+ num_luns = sbp_count_se_tpg_luns(&tport->tpg->se_tpg);
+
+ /*
+ * Number of entries in the final unit directory:
+ * - all of those in the template
+ * - management_agent
+ * - unit_characteristics
+ * - reconnect_timeout
+ * - unit unique ID
+ * - one for each LUN
+ *
+ * MUST NOT include leaf or sub-directory entries
+ */
+ num_entries = ARRAY_SIZE(sbp_unit_directory_template) + 4 + num_luns;
+
+ if (tport->directory_id != -1)
+ num_entries++;
+
+ /* allocate num_entries + 4 for the header and unique ID leaf */
+ data = kcalloc((num_entries + 4), sizeof(u32), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* directory_length */
+ data[idx++] = num_entries << 16;
+
+ /* directory_id */
+ if (tport->directory_id != -1)
+ data[idx++] = (CSR_DIRECTORY_ID << 24) | tport->directory_id;
+
+ /* unit directory template */
+ memcpy(&data[idx], sbp_unit_directory_template,
+ sizeof(sbp_unit_directory_template));
+ idx += ARRAY_SIZE(sbp_unit_directory_template);
+
+ /* management_agent */
+ mgt_agt_addr = (tport->mgt_agt->handler.offset - CSR_REGISTER_BASE) / 4;
+ data[idx++] = 0x54000000 | (mgt_agt_addr & 0x00ffffff);
+
+ /* unit_characteristics */
+ data[idx++] = 0x3a000000 |
+ (((tport->mgt_orb_timeout * 2) << 8) & 0xff00) |
+ SBP_ORB_FETCH_SIZE;
+
+ /* reconnect_timeout */
+ data[idx++] = 0x3d000000 | (tport->max_reconnect_timeout & 0xffff);
+
+ /* unit unique ID (leaf is just after LUNs) */
+ data[idx++] = 0x8d000000 | (num_luns + 1);
+
+ spin_lock(&tport->tpg->se_tpg.tpg_lun_lock);
+ for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+ struct se_lun *se_lun = tport->tpg->se_tpg.tpg_lun_list[i];
+ struct se_device *dev;
+ int type;
+
+ if (se_lun->lun_status == TRANSPORT_LUN_STATUS_FREE)
+ continue;
+
+ spin_unlock(&tport->tpg->se_tpg.tpg_lun_lock);
+
+ dev = se_lun->lun_se_dev;
+ type = dev->transport->get_device_type(dev);
+
+ /* logical_unit_number */
+ data[idx++] = 0x14000000 |
+ ((type << 16) & 0x1f0000) |
+ (se_lun->unpacked_lun & 0xffff);
+
+ spin_lock(&tport->tpg->se_tpg.tpg_lun_lock);
+ }
+ spin_unlock(&tport->tpg->se_tpg.tpg_lun_lock);
+
+ /* unit unique ID leaf */
+ data[idx++] = 2 << 16;
+ data[idx++] = tport->guid >> 32;
+ data[idx++] = tport->guid;
+
+ tport->unit_directory.length = idx;
+ tport->unit_directory.key = (CSR_DIRECTORY | CSR_UNIT) << 24;
+ tport->unit_directory.data = data;
+
+ ret = fw_core_add_descriptor(&tport->unit_directory);
+ if (ret < 0) {
+ kfree(tport->unit_directory.data);
+ tport->unit_directory.data = NULL;
+ }
+
+ return ret;
+}
+
+static ssize_t sbp_parse_wwn(const char *name, u64 *wwn, int strict)
+{
+ const char *cp;
+ char c, nibble;
+ int pos = 0, err;
+
+ *wwn = 0;
+ for (cp = name; cp < &name[SBP_NAMELEN - 1]; cp++) {
+ c = *cp;
+ if (c == '\n' && cp[1] == '\0')
+ continue;
+ if (c == '\0') {
+ err = 2;
+ if (pos != 16)
+ goto fail;
+ return cp - name;
+ }
+ err = 3;
+ if (isdigit(c))
+ nibble = c - '0';
+ else if (isxdigit(c) && (islower(c) || !strict))
+ nibble = tolower(c) - 'a' + 10;
+ else
+ goto fail;
+ *wwn = (*wwn << 4) | nibble;
+ pos++;
+ }
+ err = 4;
+fail:
+ printk(KERN_INFO "err %u len %zu pos %u\n",
+ err, cp - name, pos);
+ return -1;
+}
+
+static ssize_t sbp_format_wwn(char *buf, size_t len, u64 wwn)
+{
+ return snprintf(buf, len, "%016llx", wwn);
+}
+
+static struct se_node_acl *sbp_make_nodeacl(
+ struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
+{
+ struct se_node_acl *se_nacl, *se_nacl_new;
+ struct sbp_nacl *nacl;
+ u64 guid = 0;
+ u32 nexus_depth = 1;
+
+ if (sbp_parse_wwn(name, &guid, 1) < 0)
+ return ERR_PTR(-EINVAL);
+
+ se_nacl_new = sbp_alloc_fabric_acl(se_tpg);
+ if (!se_nacl_new)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+ * when converting a NodeACL from demo mode -> explict
+ */
+ se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+ name, nexus_depth);
+ if (IS_ERR(se_nacl)) {
+ sbp_release_fabric_acl(se_tpg, se_nacl_new);
+ return se_nacl;
+ }
+
+ nacl = container_of(se_nacl, struct sbp_nacl, se_node_acl);
+ nacl->guid = guid;
+ sbp_format_wwn(nacl->iport_name, SBP_NAMELEN, guid);
+
+ return se_nacl;
+}
+
+static void sbp_drop_nodeacl(struct se_node_acl *se_acl)
+{
+ struct sbp_nacl *nacl =
+ container_of(se_acl, struct sbp_nacl, se_node_acl);
+
+ core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+ kfree(nacl);
+}
+
+static int sbp_post_link_lun(
+ struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+
+ return sbp_update_unit_directory(tpg->tport);
+}
+
+static void sbp_pre_unlink_lun(
+ struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ int ret;
+
+ if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0)
+ tport->enable = 0;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0)
+ pr_err("unlink LUN: failed to update unit directory\n");
+}
+
+static struct se_portal_group *sbp_make_tpg(
+ struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct sbp_tport *tport =
+ container_of(wwn, struct sbp_tport, tport_wwn);
+
+ struct sbp_tpg *tpg;
+ unsigned long tpgt;
+ int ret;
+
+ if (strstr(name, "tpgt_") != name)
+ return ERR_PTR(-EINVAL);
+ if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
+ return ERR_PTR(-EINVAL);
+
+ if (tport->tpg) {
+ pr_err("Only one TPG per Unit is possible.\n");
+ return ERR_PTR(-EBUSY);
+ }
+
+ tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
+ if (!tpg) {
+ pr_err("Unable to allocate struct sbp_tpg\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ tpg->tport = tport;
+ tpg->tport_tpgt = tpgt;
+ tport->tpg = tpg;
+
+ /* default attribute values */
+ tport->enable = 0;
+ tport->directory_id = -1;
+ tport->mgt_orb_timeout = 15;
+ tport->max_reconnect_timeout = 5;
+ tport->max_logins_per_lun = 1;
+
+ tport->mgt_agt = sbp_management_agent_register(tport);
+ if (IS_ERR(tport->mgt_agt)) {
+ ret = PTR_ERR(tport->mgt_agt);
+ kfree(tpg);
+ return ERR_PTR(ret);
+ }
+
+ ret = core_tpg_register(&sbp_fabric_configfs->tf_ops, wwn,
+ &tpg->se_tpg, (void *)tpg,
+ TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0) {
+ sbp_management_agent_unregister(tport->mgt_agt);
+ kfree(tpg);
+ return ERR_PTR(ret);
+ }
+
+ return &tpg->se_tpg;
+}
+
+static void sbp_drop_tpg(struct se_portal_group *se_tpg)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+
+ core_tpg_deregister(se_tpg);
+ sbp_management_agent_unregister(tport->mgt_agt);
+ tport->tpg = NULL;
+ kfree(tpg);
+}
+
+static struct se_wwn *sbp_make_tport(
+ struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct sbp_tport *tport;
+ u64 guid = 0;
+
+ if (sbp_parse_wwn(name, &guid, 1) < 0)
+ return ERR_PTR(-EINVAL);
+
+ tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+ if (!tport) {
+ pr_err("Unable to allocate struct sbp_tport\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ tport->guid = guid;
+ sbp_format_wwn(tport->tport_name, SBP_NAMELEN, guid);
+
+ return &tport->tport_wwn;
+}
+
+static void sbp_drop_tport(struct se_wwn *wwn)
+{
+ struct sbp_tport *tport =
+ container_of(wwn, struct sbp_tport, tport_wwn);
+
+ kfree(tport);
+}
+
+static ssize_t sbp_wwn_show_attr_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "FireWire SBP fabric module %s\n", SBP_VERSION);
+}
+
+TF_WWN_ATTR_RO(sbp, version);
+
+static struct configfs_attribute *sbp_wwn_attrs[] = {
+ &sbp_wwn_version.attr,
+ NULL,
+};
+
+static ssize_t sbp_tpg_show_directory_id(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+
+ if (tport->directory_id == -1)
+ return sprintf(page, "implicit\n");
+ else
+ return sprintf(page, "%06x\n", tport->directory_id);
+}
+
+static ssize_t sbp_tpg_store_directory_id(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+
+ if (tport->enable) {
+ pr_err("Cannot change the directory_id on an active target.\n");
+ return -EBUSY;
+ }
+
+ if (strstr(page, "implicit") == page) {
+ tport->directory_id = -1;
+ } else {
+ if (kstrtoul(page, 16, &val) < 0)
+ return -EINVAL;
+ if (val > 0xffffff)
+ return -EINVAL;
+
+ tport->directory_id = val;
+ }
+
+ return count;
+}
+
+static ssize_t sbp_tpg_show_enable(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->enable);
+}
+
+static ssize_t sbp_tpg_store_enable(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val != 0) && (val != 1))
+ return -EINVAL;
+
+ if (tport->enable == val)
+ return count;
+
+ if (val) {
+ if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0) {
+ pr_err("Cannot enable a target with no LUNs!\n");
+ return -EINVAL;
+ }
+ } else {
+ /* XXX: force-shutdown sessions instead? */
+ spin_lock_bh(&se_tpg->session_lock);
+ if (!list_empty(&se_tpg->tpg_sess_list)) {
+ spin_unlock_bh(&se_tpg->session_lock);
+ return -EBUSY;
+ }
+ spin_unlock_bh(&se_tpg->session_lock);
+ }
+
+ tport->enable = val;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0) {
+ pr_err("Could not update Config ROM\n");
+ return ret;
+ }
+
+ return count;
+}
+
+TF_TPG_BASE_ATTR(sbp, directory_id, S_IRUGO | S_IWUSR);
+TF_TPG_BASE_ATTR(sbp, enable, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_base_attrs[] = {
+ &sbp_tpg_directory_id.attr,
+ &sbp_tpg_enable.attr,
+ NULL,
+};
+
+static ssize_t sbp_tpg_attrib_show_mgt_orb_timeout(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->mgt_orb_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_mgt_orb_timeout(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val < 1) || (val > 127))
+ return -EINVAL;
+
+ if (tport->mgt_orb_timeout == val)
+ return count;
+
+ tport->mgt_orb_timeout = val;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_reconnect_timeout(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->max_reconnect_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_reconnect_timeout(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val < 1) || (val > 32767))
+ return -EINVAL;
+
+ if (tport->max_reconnect_timeout == val)
+ return count;
+
+ tport->max_reconnect_timeout = val;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_logins_per_lun(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->max_logins_per_lun);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_logins_per_lun(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val < 1) || (val > 127))
+ return -EINVAL;
+
+ /* XXX: also check against current count? */
+
+ tport->max_logins_per_lun = val;
+
+ return count;
+}
+
+TF_TPG_ATTRIB_ATTR(sbp, mgt_orb_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_reconnect_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_logins_per_lun, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_attrib_attrs[] = {
+ &sbp_tpg_attrib_mgt_orb_timeout.attr,
+ &sbp_tpg_attrib_max_reconnect_timeout.attr,
+ &sbp_tpg_attrib_max_logins_per_lun.attr,
+ NULL,
+};
+
+static struct target_core_fabric_ops sbp_ops = {
+ .get_fabric_name = sbp_get_fabric_name,
+ .get_fabric_proto_ident = sbp_get_fabric_proto_ident,
+ .tpg_get_wwn = sbp_get_fabric_wwn,
+ .tpg_get_tag = sbp_get_tag,
+ .tpg_get_default_depth = sbp_get_default_depth,
+ .tpg_get_pr_transport_id = sbp_get_pr_transport_id,
+ .tpg_get_pr_transport_id_len = sbp_get_pr_transport_id_len,
+ .tpg_parse_pr_out_transport_id = sbp_parse_pr_out_transport_id,
+ .tpg_check_demo_mode = sbp_check_true,
+ .tpg_check_demo_mode_cache = sbp_check_true,
+ .tpg_check_demo_mode_write_protect = sbp_check_false,
+ .tpg_check_prod_mode_write_protect = sbp_check_false,
+ .tpg_alloc_fabric_acl = sbp_alloc_fabric_acl,
+ .tpg_release_fabric_acl = sbp_release_fabric_acl,
+ .tpg_get_inst_index = sbp_tpg_get_inst_index,
+ .release_cmd = sbp_release_cmd,
+ .shutdown_session = sbp_shutdown_session,
+ .close_session = sbp_close_session,
+ .sess_get_index = sbp_sess_get_index,
+ .write_pending = sbp_write_pending,
+ .write_pending_status = sbp_write_pending_status,
+ .set_default_node_attributes = sbp_set_default_node_attrs,
+ .get_task_tag = sbp_get_task_tag,
+ .get_cmd_state = sbp_get_cmd_state,
+ .queue_data_in = sbp_queue_data_in,
+ .queue_status = sbp_queue_status,
+ .queue_tm_rsp = sbp_queue_tm_rsp,
+ .get_fabric_sense_len = sbp_get_fabric_sense_len,
+ .set_fabric_sense_len = sbp_set_fabric_sense_len,
+ .check_stop_free = sbp_check_stop_free,
+
+ .fabric_make_wwn = sbp_make_tport,
+ .fabric_drop_wwn = sbp_drop_tport,
+ .fabric_make_tpg = sbp_make_tpg,
+ .fabric_drop_tpg = sbp_drop_tpg,
+ .fabric_post_link = sbp_post_link_lun,
+ .fabric_pre_unlink = sbp_pre_unlink_lun,
+ .fabric_make_np = NULL,
+ .fabric_drop_np = NULL,
+ .fabric_make_nodeacl = sbp_make_nodeacl,
+ .fabric_drop_nodeacl = sbp_drop_nodeacl,
+};
+
+static int sbp_register_configfs(void)
+{
+ struct target_fabric_configfs *fabric;
+ int ret;
+
+ fabric = target_fabric_configfs_init(THIS_MODULE, "sbp");
+ if (!fabric) {
+ pr_err("target_fabric_configfs_init() failed\n");
+ return -ENOMEM;
+ }
+
+ fabric->tf_ops = sbp_ops;
+
+ /*
+ * Setup default attribute lists for various fabric->tf_cit_tmpl
+ */
+ TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = sbp_wwn_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = sbp_tpg_base_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = sbp_tpg_attrib_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+
+ ret = target_fabric_configfs_register(fabric);
+ if (ret < 0) {
+ pr_err("target_fabric_configfs_register() failed for SBP\n");
+ return ret;
+ }
+
+ sbp_fabric_configfs = fabric;
+
+ return 0;
+};
+
+static void sbp_deregister_configfs(void)
+{
+ if (!sbp_fabric_configfs)
+ return;
+
+ target_fabric_configfs_deregister(sbp_fabric_configfs);
+ sbp_fabric_configfs = NULL;
+};
+
+static int __init sbp_init(void)
+{
+ int ret;
+
+ ret = sbp_register_configfs();
+ if (ret < 0)
+ return ret;
+
+ return 0;
+};
+
+static void sbp_exit(void)
+{
+ sbp_deregister_configfs();
+};
+
+MODULE_DESCRIPTION("FireWire SBP fabric driver");
+MODULE_LICENSE("GPL");
+module_init(sbp_init);
+module_exit(sbp_exit);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 06/11] firewire-sbp-target: Add sbp_fabric.{c,h}
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (4 preceding siblings ...)
2012-04-11 14:20 ` [PATCH 05/11] firewire-sbp-target: Add sbp_configfs.c Chris Boot
@ 2012-04-11 14:20 ` Chris Boot
2012-04-11 14:20 ` [PATCH 07/11] firewire-sbp-target: Add sbp_management_agent.{c,h} Chris Boot
` (6 subsequent siblings)
12 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-11 14:20 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This serves as further glue between the target framework and SBP-2, in
this case dealing with SCSI command submission and data in/out.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_fabric.c | 314 +++++++++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_fabric.h | 40 +++++
2 files changed, 354 insertions(+)
create mode 100644 drivers/target/sbp/sbp_fabric.c
create mode 100644 drivers/target/sbp/sbp_fabric.h
diff --git a/drivers/target/sbp/sbp_fabric.c b/drivers/target/sbp/sbp_fabric.c
new file mode 100644
index 0000000..5230851
--- /dev/null
+++ b/drivers/target/sbp/sbp_fabric.c
@@ -0,0 +1,314 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+int sbp_check_true(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+int sbp_check_false(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+char *sbp_get_fabric_name(void)
+{
+ return "sbp";
+}
+
+char *sbp_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+
+ return &tport->tport_name[0];
+}
+
+u16 sbp_get_tag(struct se_portal_group *se_tpg)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ return tpg->tport_tpgt;
+}
+
+u32 sbp_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+ struct sbp_nacl *nacl;
+
+ nacl = kzalloc(sizeof(struct sbp_nacl), GFP_KERNEL);
+ if (!nacl) {
+ pr_err("Unable to alocate struct sbp_nacl\n");
+ return NULL;
+ }
+
+ return &nacl->se_node_acl;
+}
+
+void sbp_release_fabric_acl(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl)
+{
+ struct sbp_nacl *nacl =
+ container_of(se_nacl, struct sbp_nacl, se_node_acl);
+ kfree(nacl);
+}
+
+u32 sbp_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+void sbp_release_cmd(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ sbp_free_request(req);
+}
+
+int sbp_shutdown_session(struct se_session *se_sess)
+{
+ return 0;
+}
+
+void sbp_close_session(struct se_session *se_sess)
+{
+ return;
+}
+
+u32 sbp_sess_get_index(struct se_session *se_sess)
+{
+ return 0;
+}
+
+int sbp_write_pending(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+ int ret;
+
+ ret = sbp_rw_data(req);
+ if (ret) {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(
+ STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(
+ SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ return ret;
+ }
+
+ transport_generic_process_write(se_cmd);
+
+ return 0;
+}
+
+int sbp_write_pending_status(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+void sbp_set_default_node_attrs(struct se_node_acl *nacl)
+{
+ return;
+}
+
+u32 sbp_get_task_tag(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ /* only used for printk until we do TMRs */
+ return (u32)req->orb_pointer;
+}
+
+int sbp_get_cmd_state(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+int sbp_queue_data_in(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+ int ret;
+
+ ret = sbp_rw_data(req);
+ if (ret) {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ return ret;
+ }
+
+ return sbp_send_sense(req);
+}
+
+/*
+ * Called after command (no data transfer) or after the write (to device)
+ * operation is completed
+ */
+int sbp_queue_status(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ return sbp_send_sense(req);
+}
+
+int sbp_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+u16 sbp_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length)
+{
+ return 0;
+}
+
+u16 sbp_get_fabric_sense_len(void)
+{
+ return 0;
+}
+
+int sbp_check_stop_free(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ transport_generic_free_cmd(&req->se_cmd, 0);
+ return 1;
+}
+
+/*
+ * Handlers for Serial Bus Protocol 2/3 (SBP-2 / SBP-3)
+ */
+u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+ /*
+ * Return a IEEE 1394 SCSI Protocol identifier for loopback operations
+ * This is defined in section 7.5.1 Table 362 in spc4r17
+ */
+ return SCSI_PROTOCOL_SBP;
+}
+
+u32 sbp_get_pr_transport_id(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code,
+ unsigned char *buf)
+{
+ int ret;
+
+ /*
+ * Set PROTOCOL IDENTIFIER to 3h for SBP
+ */
+ buf[0] = SCSI_PROTOCOL_SBP;
+ /*
+ * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+ * over IEEE 1394
+ */
+ ret = hex2bin(&buf[8], se_nacl->initiatorname, 8);
+ if (ret < 0)
+ pr_debug("sbp transport_id: invalid hex string\n");
+
+ /*
+ * The IEEE 1394 Transport ID is a hardcoded 24-byte length
+ */
+ return 24;
+}
+
+u32 sbp_get_pr_transport_id_len(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code)
+{
+ *format_code = 0;
+ /*
+ * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+ * over IEEE 1394
+ *
+ * The SBP Transport ID is a hardcoded 24-byte length
+ */
+ return 24;
+}
+
+/*
+ * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above
+ * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations.
+ */
+char *sbp_parse_pr_out_transport_id(
+ struct se_portal_group *se_tpg,
+ const char *buf,
+ u32 *out_tid_len,
+ char **port_nexus_ptr)
+{
+ /*
+ * Assume the FORMAT CODE 00b from spc4r17, 7.5.4.4 TransportID
+ * for initiator ports using SCSI over SBP Serial SCSI Protocol
+ *
+ * The TransportID for a IEEE 1394 Initiator Port is of fixed size of
+ * 24 bytes, and IEEE 1394 does not contain a I_T nexus identifier,
+ * so we return the **port_nexus_ptr set to NULL.
+ */
+ *port_nexus_ptr = NULL;
+ *out_tid_len = 24;
+
+ return (char *)&buf[8];
+}
diff --git a/drivers/target/sbp/sbp_fabric.h b/drivers/target/sbp/sbp_fabric.h
new file mode 100644
index 0000000..cf66eca
--- /dev/null
+++ b/drivers/target/sbp/sbp_fabric.h
@@ -0,0 +1,40 @@
+
+#include <linux/types.h>
+#include <target/target_core_base.h>
+
+int sbp_check_true(struct se_portal_group *);
+int sbp_check_false(struct se_portal_group *);
+char *sbp_get_fabric_name(void);
+char *sbp_get_fabric_wwn(struct se_portal_group *);
+u16 sbp_get_tag(struct se_portal_group *);
+u32 sbp_get_default_depth(struct se_portal_group *);
+struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *);
+void sbp_release_fabric_acl(struct se_portal_group *,
+ struct se_node_acl *);
+u32 sbp_tpg_get_inst_index(struct se_portal_group *);
+void sbp_release_cmd(struct se_cmd *se_cmd);
+int sbp_shutdown_session(struct se_session *);
+void sbp_close_session(struct se_session *);
+u32 sbp_sess_get_index(struct se_session *);
+int sbp_write_pending(struct se_cmd *);
+int sbp_write_pending_status(struct se_cmd *);
+void sbp_set_default_node_attrs(struct se_node_acl *);
+u32 sbp_get_task_tag(struct se_cmd *);
+int sbp_get_cmd_state(struct se_cmd *);
+int sbp_queue_data_in(struct se_cmd *);
+int sbp_queue_status(struct se_cmd *);
+int sbp_queue_tm_rsp(struct se_cmd *);
+u16 sbp_set_fabric_sense_len(struct se_cmd *, u32);
+u16 sbp_get_fabric_sense_len(void);
+int sbp_check_stop_free(struct se_cmd *se_cmd);
+
+u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg);
+u32 sbp_get_pr_transport_id(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl, struct t10_pr_registration *pr_reg,
+ int *format_code, unsigned char *buf);
+u32 sbp_get_pr_transport_id_len(
+ struct se_portal_group *se_tpg, struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg, int *format_code);
+char *sbp_parse_pr_out_transport_id(
+ struct se_portal_group *se_tpg, const char *buf,
+ u32 *out_tid_len, char **port_nexus_ptr);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 07/11] firewire-sbp-target: Add sbp_management_agent.{c,h}
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (5 preceding siblings ...)
2012-04-11 14:20 ` [PATCH 06/11] firewire-sbp-target: Add sbp_fabric.{c,h} Chris Boot
@ 2012-04-11 14:20 ` Chris Boot
2012-04-11 14:20 ` [PATCH 08/11] firewire-sbp-target: Add sbp_login.{c,h} Chris Boot
` (5 subsequent siblings)
12 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-11 14:20 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This code implements the SBP-2 Management Agent. This is the first of
two firewire address handlers that are used to communicate with the
target. The Management Agent is used to handle login, reconnect and
logout to a SCSI LUN as well as task management functions.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_management_agent.c | 255 +++++++++++++++++++++++++++++
drivers/target/sbp/sbp_management_agent.h | 34 ++++
2 files changed, 289 insertions(+)
create mode 100644 drivers/target/sbp/sbp_management_agent.c
create mode 100644 drivers/target/sbp/sbp_management_agent.h
diff --git a/drivers/target/sbp/sbp_management_agent.c b/drivers/target/sbp/sbp_management_agent.c
new file mode 100644
index 0000000..71f7b2f
--- /dev/null
+++ b/drivers/target/sbp/sbp_management_agent.c
@@ -0,0 +1,255 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/kref.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_scsi_cmnd.h"
+
+static void sbp_mgt_agent_process(struct work_struct *work)
+{
+ struct sbp_management_agent *agent =
+ container_of(work, struct sbp_management_agent, work);
+ struct sbp_management_request *req = agent->request;
+ int ret;
+ int status_data_len = 0;
+
+ /* fetch the ORB from the initiator */
+ ret = sbp_run_transaction(req->card, TCODE_READ_BLOCK_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ agent->orb_offset, &req->orb, sizeof(req->orb));
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("mgt_orb fetch failed: %x\n", ret);
+ goto out;
+ }
+
+ pr_debug("mgt_orb ptr1:0x%llx ptr2:0x%llx misc:0x%x len:0x%x status_fifo:0x%llx\n",
+ sbp2_pointer_to_addr(&req->orb.ptr1),
+ sbp2_pointer_to_addr(&req->orb.ptr2),
+ be32_to_cpu(req->orb.misc), be32_to_cpu(req->orb.length),
+ sbp2_pointer_to_addr(&req->orb.status_fifo));
+
+ if (!ORB_NOTIFY(be32_to_cpu(req->orb.misc)) ||
+ ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc)) != 0) {
+ pr_err("mgt_orb bad request\n");
+ goto out;
+ }
+
+ switch (MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc))) {
+ case MANAGEMENT_ORB_FUNCTION_LOGIN:
+ sbp_management_request_login(agent, req, &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS:
+ sbp_management_request_query_logins(agent, req,
+ &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_RECONNECT:
+ sbp_management_request_reconnect(agent, req, &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_SET_PASSWORD:
+ pr_notice("SET PASSWORD not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_LOGOUT:
+ sbp_management_request_logout(agent, req, &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_ABORT_TASK:
+ pr_notice("ABORT TASK not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET:
+ pr_notice("ABORT TASK SET not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET:
+ pr_notice("LOGICAL UNIT RESET not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_TARGET_RESET:
+ pr_notice("TARGET RESET not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ default:
+ pr_notice("unknown management function 0x%x\n",
+ MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc)));
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+ }
+
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_SRC(1) | /* Response to ORB, next_ORB absent */
+ STATUS_BLOCK_LEN(DIV_ROUND_UP(status_data_len, 4) + 1) |
+ STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_offset >> 32));
+ req->status.orb_low = cpu_to_be32(agent->orb_offset);
+
+ /* write the status block back to the initiator */
+ ret = sbp_run_transaction(req->card, TCODE_WRITE_BLOCK_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ sbp2_pointer_to_addr(&req->orb.status_fifo),
+ &req->status, 8 + status_data_len);
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("mgt_orb status write failed: %x\n", ret);
+ goto out;
+ }
+
+out:
+ fw_card_put(req->card);
+ kfree(req);
+
+ spin_lock_bh(&agent->lock);
+ agent->state = MANAGEMENT_AGENT_STATE_IDLE;
+ spin_unlock_bh(&agent->lock);
+}
+
+static void sbp_mgt_agent_rw(struct fw_card *card,
+ struct fw_request *request, int tcode, int destination, int source,
+ int generation, unsigned long long offset, void *data, size_t length,
+ void *callback_data)
+{
+ struct sbp_management_agent *agent = callback_data;
+ struct sbp2_pointer *ptr = data;
+ int rcode = RCODE_ADDRESS_ERROR;
+
+ if (!agent->tport->enable)
+ goto out;
+
+ if ((offset != agent->handler.offset) || (length != 8))
+ goto out;
+
+ if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+ struct sbp_management_request *req;
+ int prev_state;
+
+ spin_lock_bh(&agent->lock);
+ prev_state = agent->state;
+ agent->state = MANAGEMENT_AGENT_STATE_BUSY;
+ spin_unlock_bh(&agent->lock);
+
+ if (prev_state == MANAGEMENT_AGENT_STATE_BUSY) {
+ pr_notice("ignoring management request while busy\n");
+ rcode = RCODE_CONFLICT_ERROR;
+ goto out;
+ }
+
+ req = kzalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ rcode = RCODE_CONFLICT_ERROR;
+ goto out;
+ }
+
+ req->card = fw_card_get(card);
+ req->generation = generation;
+ req->node_addr = source;
+ req->speed = fw_get_request_speed(request);
+
+ agent->orb_offset = sbp2_pointer_to_addr(ptr);
+ agent->request = req;
+
+ queue_work(system_unbound_wq, &agent->work);
+ rcode = RCODE_COMPLETE;
+ } else if (tcode == TCODE_READ_BLOCK_REQUEST) {
+ addr_to_sbp2_pointer(agent->orb_offset, ptr);
+ rcode = RCODE_COMPLETE;
+ } else {
+ rcode = RCODE_TYPE_ERROR;
+ }
+
+out:
+ fw_send_response(card, request, rcode);
+}
+
+struct sbp_management_agent *sbp_management_agent_register(
+ struct sbp_tport *tport)
+{
+ int ret;
+ struct sbp_management_agent *agent;
+
+ agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+ if (!agent)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock_init(&agent->lock);
+ agent->tport = tport;
+ agent->handler.length = 0x08;
+ agent->handler.address_callback = sbp_mgt_agent_rw;
+ agent->handler.callback_data = agent;
+ agent->state = MANAGEMENT_AGENT_STATE_IDLE;
+ INIT_WORK(&agent->work, sbp_mgt_agent_process);
+ agent->orb_offset = 0;
+ agent->request = NULL;
+
+ ret = fw_core_add_address_handler(&agent->handler,
+ &sbp_register_region);
+ if (ret < 0) {
+ kfree(agent);
+ return ERR_PTR(ret);
+ }
+
+ return agent;
+}
+
+void sbp_management_agent_unregister(struct sbp_management_agent *agent)
+{
+ fw_core_remove_address_handler(&agent->handler);
+ cancel_work_sync(&agent->work);
+ kfree(agent);
+}
diff --git a/drivers/target/sbp/sbp_management_agent.h b/drivers/target/sbp/sbp_management_agent.h
new file mode 100644
index 0000000..73237ae
--- /dev/null
+++ b/drivers/target/sbp/sbp_management_agent.h
@@ -0,0 +1,34 @@
+#ifndef _SBP_MANAGEMENT_AGENT_H
+#define _SBP_MANAGEMENT_AGENT_H
+
+#include <linux/types.h>
+#include <linux/firewire.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#include "sbp_base.h"
+
+struct sbp_management_agent {
+ spinlock_t lock;
+ struct sbp_tport *tport;
+ struct fw_address_handler handler;
+ int state;
+ struct work_struct work;
+ u64 orb_offset;
+ struct sbp_management_request *request;
+};
+
+struct sbp_management_request {
+ struct sbp_management_orb orb;
+ struct sbp_status_block status;
+ struct fw_card *card;
+ int generation;
+ int node_addr;
+ int speed;
+};
+
+struct sbp_management_agent *sbp_management_agent_register(
+ struct sbp_tport *tport);
+void sbp_management_agent_unregister(struct sbp_management_agent *agent);
+
+#endif
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 08/11] firewire-sbp-target: Add sbp_login.{c,h}
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (6 preceding siblings ...)
2012-04-11 14:20 ` [PATCH 07/11] firewire-sbp-target: Add sbp_management_agent.{c,h} Chris Boot
@ 2012-04-11 14:20 ` Chris Boot
2012-04-14 10:17 ` Stefan Richter
2012-04-11 14:20 ` [PATCH 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h} Chris Boot
` (4 subsequent siblings)
12 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-04-11 14:20 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This file contains the implementation of the login, reconnect and logout
management ORBs in SBP-2.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_login.c | 672 ++++++++++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_login.h | 17 +
2 files changed, 689 insertions(+)
create mode 100644 drivers/target/sbp/sbp_login.c
create mode 100644 drivers/target/sbp/sbp_login.h
diff --git a/drivers/target/sbp/sbp_login.c b/drivers/target/sbp/sbp_login.c
new file mode 100644
index 0000000..bbcc618
--- /dev/null
+++ b/drivers/target/sbp/sbp_login.c
@@ -0,0 +1,672 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kref.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+#define SESSION_MAINTENANCE_INTERVAL HZ
+
+static atomic_t login_id = ATOMIC_INIT(0);
+
+static void session_maintenance_work(struct work_struct *work);
+
+static int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
+{
+ int ret;
+ __be32 high, low;
+
+ ret = sbp_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
+ &high, sizeof(high));
+ if (ret != RCODE_COMPLETE)
+ return ret;
+
+ ret = sbp_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
+ &low, sizeof(low));
+ if (ret != RCODE_COMPLETE)
+ return ret;
+
+ *guid = (u64)be32_to_cpu(high) << 32 | be32_to_cpu(low);
+
+ return RCODE_COMPLETE;
+}
+
+static struct sbp_session *sbp_session_find_by_guid(
+ struct sbp_tpg *tpg, u64 guid)
+{
+ struct se_session *se_sess;
+ struct sbp_session *sess, *found = NULL;
+
+ spin_lock_bh(&tpg->se_tpg.session_lock);
+ list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+ sess = se_sess->fabric_sess_ptr;
+ if (sess->guid == guid)
+ found = sess;
+ }
+ spin_unlock_bh(&tpg->se_tpg.session_lock);
+
+ return found;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_lun(
+ struct sbp_session *session, struct se_lun *lun)
+{
+ struct sbp_login_descriptor *login, *found = NULL;
+
+ spin_lock_bh(&session->lock);
+ list_for_each_entry(login, &session->login_list, link) {
+ if (login->lun == lun)
+ found = login;
+ }
+ spin_unlock_bh(&session->lock);
+
+ return found;
+}
+
+static int sbp_login_count_all_by_lun(
+ struct sbp_tpg *tpg,
+ struct se_lun *lun,
+ int exclusive)
+{
+ struct se_session *se_sess;
+ struct sbp_session *sess;
+ struct sbp_login_descriptor *login;
+ int count = 0;
+
+ spin_lock_bh(&tpg->se_tpg.session_lock);
+ list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+ sess = se_sess->fabric_sess_ptr;
+
+ spin_lock_bh(&sess->lock);
+ list_for_each_entry(login, &sess->login_list, link) {
+ if (login->lun != lun)
+ continue;
+
+ if (!exclusive || login->exclusive)
+ count++;
+ }
+ spin_unlock_bh(&sess->lock);
+ }
+ spin_unlock_bh(&tpg->se_tpg.session_lock);
+
+ return count;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_id(
+ struct sbp_tpg *tpg, int login_id)
+{
+ struct se_session *se_sess;
+ struct sbp_session *sess;
+ struct sbp_login_descriptor *login, *found = NULL;
+
+ spin_lock_bh(&tpg->se_tpg.session_lock);
+ list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+ sess = se_sess->fabric_sess_ptr;
+
+ spin_lock_bh(&sess->lock);
+ list_for_each_entry(login, &sess->login_list, link) {
+ if (login->login_id == login_id)
+ found = login;
+ }
+ spin_unlock_bh(&sess->lock);
+ }
+ spin_unlock_bh(&tpg->se_tpg.session_lock);
+
+ return found;
+}
+
+static struct se_lun *sbp_get_lun_from_tpg(struct sbp_tpg *tpg, int lun)
+{
+ struct se_portal_group *se_tpg = &tpg->se_tpg;
+ struct se_lun *se_lun;
+
+ if (lun >= TRANSPORT_MAX_LUNS_PER_TPG)
+ return ERR_PTR(-EINVAL);
+
+ spin_lock(&se_tpg->tpg_lun_lock);
+ se_lun = se_tpg->tpg_lun_list[lun];
+
+ if (se_lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE)
+ se_lun = ERR_PTR(-ENODEV);
+
+ spin_unlock(&se_tpg->tpg_lun_lock);
+
+ return se_lun;
+}
+
+static struct sbp_session *sbp_session_create(
+ struct sbp_tpg *tpg,
+ u64 guid)
+{
+ struct sbp_session *sess;
+ int ret;
+ char guid_str[17];
+ struct se_node_acl *se_nacl;
+
+ sess = kmalloc(sizeof(*sess), GFP_KERNEL);
+ if (!sess) {
+ pr_err("failed to allocate session descriptor\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sess->se_sess = transport_init_session();
+ if (IS_ERR(sess->se_sess)) {
+ pr_err("failed to init se_session\n");
+
+ ret = PTR_ERR(sess->se_sess);
+ kfree(sess);
+ return ERR_PTR(ret);
+ }
+
+ snprintf(guid_str, sizeof(guid_str), "%016llx", guid);
+
+ se_nacl = core_tpg_check_initiator_node_acl(&tpg->se_tpg, guid_str);
+ if (!se_nacl) {
+ pr_warn("Node ACL not found for %s\n", guid_str);
+
+ transport_free_session(sess->se_sess);
+ kfree(sess);
+
+ return ERR_PTR(-EPERM);
+ }
+
+ sess->se_sess->se_node_acl = se_nacl;
+
+ spin_lock_init(&sess->lock);
+ INIT_LIST_HEAD(&sess->login_list);
+ INIT_DELAYED_WORK(&sess->maint_work, session_maintenance_work);
+
+ sess->guid = guid;
+
+ transport_register_session(&tpg->se_tpg, se_nacl, sess->se_sess, sess);
+
+ return sess;
+}
+
+static void sbp_session_release(struct sbp_session *sess, bool cancel_work)
+{
+ spin_lock_bh(&sess->lock);
+ if (!list_empty(&sess->login_list)) {
+ spin_unlock_bh(&sess->lock);
+ return;
+ }
+ spin_unlock_bh(&sess->lock);
+
+ if (cancel_work)
+ cancel_delayed_work_sync(&sess->maint_work);
+
+ transport_deregister_session_configfs(sess->se_sess);
+ transport_deregister_session(sess->se_sess);
+
+ if (sess->card)
+ fw_card_put(sess->card);
+
+ kfree(sess);
+}
+
+static void sbp_login_release(struct sbp_login_descriptor *login,
+ bool cancel_work)
+{
+ struct sbp_session *sess = login->sess;
+
+ /* FIXME: abort/wait on tasks */
+
+ sbp_target_agent_unregister(login->tgt_agt);
+
+ if (sess) {
+ spin_lock_bh(&sess->lock);
+ list_del(&login->link);
+ spin_unlock_bh(&sess->lock);
+
+ sbp_session_release(sess, cancel_work);
+ }
+
+ kfree(login);
+}
+
+void sbp_management_request_login(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ struct sbp_tport *tport = agent->tport;
+ struct sbp_tpg *tpg = tport->tpg;
+ struct se_lun *se_lun;
+ int ret;
+ u64 guid;
+ struct sbp_session *sess;
+ struct sbp_login_descriptor *login;
+ struct sbp_login_response_block *response;
+ int login_response_len;
+
+ se_lun = sbp_get_lun_from_tpg(tpg,
+ LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+ if (IS_ERR(se_lun)) {
+ pr_notice("login to unknown LUN: %d\n",
+ LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LUN_NOTSUPP));
+ return;
+ }
+
+ ret = read_peer_guid(&guid, req);
+ if (ret != RCODE_COMPLETE) {
+ pr_warn("failed to read peer GUID: %d\n", ret);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ return;
+ }
+
+ pr_notice("mgt_agent LOGIN to LUN %d from %016llx\n",
+ se_lun->unpacked_lun, guid);
+
+ sess = sbp_session_find_by_guid(tpg, guid);
+ if (sess) {
+ login = sbp_login_find_by_lun(sess, se_lun);
+ if (login) {
+ pr_notice("initiator already logged-in\n");
+
+ /*
+ * SBP-2 R4 says we should return access denied, but
+ * that can confuse initiators. Instead we need to
+ * treat this like a reconnect, but send the login
+ * response block like a fresh login.
+ *
+ * This is required particularly in the case of Apple
+ * devices booting off the FireWire target, where
+ * the firmware has an active login to the target. When
+ * the OS takes control of the session it issues its own
+ * LOGIN rather than a RECONNECT. To avoid the machine
+ * waiting until the reconnect_hold expires, we can skip
+ * the ACCESS_DENIED errors to speed things up.
+ */
+
+ goto already_logged_in;
+ }
+ }
+
+ /*
+ * check exclusive bit in login request
+ * reject with access_denied if any logins present
+ */
+ if (LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc)) &&
+ sbp_login_count_all_by_lun(tpg, se_lun, 0)) {
+ pr_warn("refusing exclusive login with other active logins\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ /*
+ * check exclusive bit in any existing login descriptor
+ * reject with access_denied if any exclusive logins present
+ */
+ if (sbp_login_count_all_by_lun(tpg, se_lun, 1)) {
+ pr_warn("refusing login while another exclusive login present\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ /*
+ * check we haven't exceeded the number of allowed logins
+ * reject with resources_unavailable if we have
+ */
+ if (sbp_login_count_all_by_lun(tpg, se_lun, 0) >=
+ tport->max_logins_per_lun) {
+ pr_warn("max number of logins reached\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ if (!sess) {
+ sess = sbp_session_create(tpg, guid);
+ if (IS_ERR(sess)) {
+ switch (PTR_ERR(sess)) {
+ case -EPERM:
+ ret = SBP_STATUS_ACCESS_DENIED;
+ break;
+ default:
+ ret = SBP_STATUS_RESOURCES_UNAVAIL;
+ break;
+ }
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(
+ STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(ret));
+ return;
+ }
+
+ sess->node_id = req->node_addr;
+ sess->card = fw_card_get(req->card);
+ sess->generation = req->generation;
+ sess->speed = req->speed;
+
+ schedule_delayed_work(&sess->maint_work,
+ SESSION_MAINTENANCE_INTERVAL);
+ }
+
+ /* only take the latest reconnect_hold into account */
+ sess->reconnect_hold = min(
+ 1 << LOGIN_ORB_RECONNECT(be32_to_cpu(req->orb.misc)),
+ tport->max_reconnect_timeout) - 1;
+
+ login = kmalloc(sizeof(*login), GFP_KERNEL);
+ if (!login) {
+ pr_err("failed to allocate login descriptor\n");
+
+ sbp_session_release(sess, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ login->sess = sess;
+ login->lun = se_lun;
+ login->status_fifo_addr = sbp2_pointer_to_addr(&req->orb.status_fifo);
+ login->exclusive = LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc));
+ login->login_id = atomic_inc_return(&login_id);
+
+ login->tgt_agt = sbp_target_agent_register(login);
+ if (IS_ERR(login->tgt_agt)) {
+ ret = PTR_ERR(login->tgt_agt);
+ pr_err("failed to map command block handler: %d\n", ret);
+
+ sbp_session_release(sess, true);
+ kfree(login);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ spin_lock_bh(&sess->lock);
+ list_add_tail(&login->link, &sess->login_list);
+ spin_unlock_bh(&sess->lock);
+
+already_logged_in:
+ response = kzalloc(sizeof(*response), GFP_KERNEL);
+ if (!response) {
+ pr_err("failed to allocate login response block\n");
+
+ sbp_login_release(login, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ login_response_len = clamp_val(
+ LOGIN_ORB_RESPONSE_LENGTH(be32_to_cpu(req->orb.length)),
+ 12, sizeof(*response));
+ response->misc = cpu_to_be32(
+ ((login_response_len & 0xffff) << 16) |
+ (login->login_id & 0xffff));
+ response->reconnect_hold = cpu_to_be32(sess->reconnect_hold & 0xffff);
+ addr_to_sbp2_pointer(login->tgt_agt->handler.offset,
+ &response->command_block_agent);
+
+ ret = sbp_run_transaction(sess->card, TCODE_WRITE_BLOCK_REQUEST,
+ sess->node_id, sess->generation, sess->speed,
+ sbp2_pointer_to_addr(&req->orb.ptr2), response,
+ login_response_len);
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("failed to write login response block: %x\n", ret);
+
+ kfree(response);
+ sbp_login_release(login, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ return;
+ }
+
+ kfree(response);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+void sbp_management_request_query_logins(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ pr_notice("QUERY LOGINS not implemented\n");
+ /* FIXME: implement */
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+}
+
+void sbp_management_request_reconnect(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ struct sbp_tport *tport = agent->tport;
+ struct sbp_tpg *tpg = tport->tpg;
+ int ret;
+ u64 guid;
+ struct sbp_login_descriptor *login;
+
+ ret = read_peer_guid(&guid, req);
+ if (ret != RCODE_COMPLETE) {
+ pr_warn("failed to read peer GUID: %d\n", ret);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ return;
+ }
+
+ pr_notice("mgt_agent RECONNECT from %016llx\n", guid);
+
+ login = sbp_login_find_by_id(tpg,
+ RECONNECT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc)));
+
+ if (!login) {
+ pr_err("mgt_agent RECONNECT unknown login ID\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ if (login->sess->guid != guid) {
+ pr_err("mgt_agent RECONNECT login GUID doesn't match\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ spin_lock_bh(&login->sess->lock);
+ if (login->sess->card)
+ fw_card_put(login->sess->card);
+
+ /* update the node details */
+ login->sess->generation = req->generation;
+ login->sess->node_id = req->node_addr;
+ login->sess->card = fw_card_get(req->card);
+ login->sess->speed = req->speed;
+ spin_unlock_bh(&login->sess->lock);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+void sbp_management_request_logout(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ struct sbp_tport *tport = agent->tport;
+ struct sbp_tpg *tpg = tport->tpg;
+ int login_id;
+ struct sbp_login_descriptor *login;
+
+ login_id = LOGOUT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc));
+
+ login = sbp_login_find_by_id(tpg, login_id);
+ if (!login) {
+ pr_warn("cannot find login: %d\n", login_id);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LOGIN_ID_UNKNOWN));
+ return;
+ }
+
+ pr_info("mgt_agent LOGOUT from LUN %d session %d\n",
+ login->lun->unpacked_lun, login->login_id);
+
+ if (req->node_addr != login->sess->node_id) {
+ pr_warn("logout from different node ID\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ sbp_login_release(login, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static void session_check_for_reset(struct sbp_session *sess)
+{
+ bool card_valid = false;
+
+ spin_lock_bh(&sess->lock);
+
+ if (sess->card) {
+ spin_lock_irq(&sess->card->lock);
+ card_valid = (sess->card->local_node != NULL);
+ spin_unlock_irq(&sess->card->lock);
+
+ if (!card_valid) {
+ fw_card_put(sess->card);
+ sess->card = NULL;
+ }
+ }
+
+ if (!card_valid || (sess->generation != sess->card->generation)) {
+ pr_info("Waiting for reconnect from node: %016llx\n",
+ sess->guid);
+
+ sess->node_id = -1;
+ sess->reconnect_expires = get_jiffies_64() +
+ ((sess->reconnect_hold + 1) * HZ);
+ }
+
+ spin_unlock_bh(&sess->lock);
+}
+
+static void session_reconnect_expired(struct sbp_session *sess)
+{
+ struct sbp_login_descriptor *login, *temp;
+ LIST_HEAD(login_list);
+
+ pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
+
+ spin_lock_bh(&sess->lock);
+ list_for_each_entry_safe(login, temp, &sess->login_list, link) {
+ login->sess = NULL;
+ list_del(&login->link);
+ list_add_tail(&login->link, &login_list);
+ }
+ spin_unlock_bh(&sess->lock);
+
+ list_for_each_entry_safe(login, temp, &login_list, link) {
+ list_del(&login->link);
+ sbp_login_release(login, false);
+ }
+
+ sbp_session_release(sess, false);
+}
+
+static void session_maintenance_work(struct work_struct *work)
+{
+ struct sbp_session *sess = container_of(work, struct sbp_session,
+ maint_work.work);
+
+ /* could be called while tearing down the session */
+ spin_lock_bh(&sess->lock);
+ if (list_empty(&sess->login_list)) {
+ spin_unlock_bh(&sess->lock);
+ return;
+ }
+ spin_unlock_bh(&sess->lock);
+
+ if (sess->node_id != -1) {
+ /* check for bus reset and make node_id invalid */
+ session_check_for_reset(sess);
+
+ schedule_delayed_work(&sess->maint_work,
+ SESSION_MAINTENANCE_INTERVAL);
+ } else if (!time_after64(get_jiffies_64(), sess->reconnect_expires)) {
+ /* still waiting for reconnect */
+ schedule_delayed_work(&sess->maint_work,
+ SESSION_MAINTENANCE_INTERVAL);
+ } else {
+ /* reconnect timeout has expired */
+ session_reconnect_expired(sess);
+ }
+}
+
diff --git a/drivers/target/sbp/sbp_login.h b/drivers/target/sbp/sbp_login.h
new file mode 100644
index 0000000..3b016f0
--- /dev/null
+++ b/drivers/target/sbp/sbp_login.h
@@ -0,0 +1,17 @@
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+
+extern void sbp_management_request_login(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size);
+extern void sbp_management_request_query_logins(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size);
+extern void sbp_management_request_reconnect(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size);
+extern void sbp_management_request_logout(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size);
+
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH 08/11] firewire-sbp-target: Add sbp_login.{c,h}
2012-04-11 14:20 ` [PATCH 08/11] firewire-sbp-target: Add sbp_login.{c,h} Chris Boot
@ 2012-04-14 10:17 ` Stefan Richter
0 siblings, 0 replies; 104+ messages in thread
From: Stefan Richter @ 2012-04-14 10:17 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Apr 11 Chris Boot wrote:
> +static void session_check_for_reset(struct sbp_session *sess)
> +{
> + bool card_valid = false;
> +
> + spin_lock_bh(&sess->lock);
> +
> + if (sess->card) {
> + spin_lock_irq(&sess->card->lock);
> + card_valid = (sess->card->local_node != NULL);
> + spin_unlock_irq(&sess->card->lock);
> +
> + if (!card_valid) {
> + fw_card_put(sess->card);
> + sess->card = NULL;
> + }
> + }
> +
> + if (!card_valid || (sess->generation != sess->card->generation)) {
> + pr_info("Waiting for reconnect from node: %016llx\n",
> + sess->guid);
> +
> + sess->node_id = -1;
> + sess->reconnect_expires = get_jiffies_64() +
> + ((sess->reconnect_hold + 1) * HZ);
> + }
> +
> + spin_unlock_bh(&sess->lock);
> +}
The card->local_node != NULL test by itself is atomic, it does not benefit
from being wrapped by card->lock acquisition.
Well, OK, the lock effectively forces the compiler to determine the value
of card_valid only once. If the lock weren't there I guess the compiler
might feel entitled to reload card->local_node in the second !card_valid
test. But even if you lose ACCESS_ONCE behavior by removing the card->lock
acquisition, I can't see how that could be detrimental relative to the
current code.
I am wondering on the other hand if there isn't actually a dependency
between this local_node test and something else, e.g. the generation test.
I.e. might the locking be incomplete? Not sure about that. I think I
rather want to look at that again when I received the code through
mainline.
BTW "card_valid" sounds rather generic; maybe call it "topology_valid"?
Either way, it can turn valid or invalid any time when firewire-core gets
to handle a self-ID-complete event.
--
Stefan Richter
-=====-===-- -=-- -===-
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (7 preceding siblings ...)
2012-04-11 14:20 ` [PATCH 08/11] firewire-sbp-target: Add sbp_login.{c,h} Chris Boot
@ 2012-04-11 14:20 ` Chris Boot
2012-04-14 10:49 ` Stefan Richter
2012-04-11 14:20 ` [PATCH 10/11] firewire-sbp-target: Add sbp_scsi_cmnd.{c,h} Chris Boot
` (3 subsequent siblings)
12 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-04-11 14:20 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This implements the SBP-2 Command Block Agent, or Target Agent. This is
what receives SCSI commands and forwards them to the target framework.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_target_agent.c | 395 +++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_target_agent.h | 40 ++++
2 files changed, 435 insertions(+)
create mode 100644 drivers/target/sbp/sbp_target_agent.c
create mode 100644 drivers/target/sbp/sbp_target_agent.h
diff --git a/drivers/target/sbp/sbp_target_agent.c b/drivers/target/sbp/sbp_target_agent.c
new file mode 100644
index 0000000..5fdd401
--- /dev/null
+++ b/drivers/target/sbp/sbp_target_agent.c
@@ -0,0 +1,395 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+static int tgt_agent_rw_agent_state(struct fw_card *card, int tcode, void *data,
+ struct sbp_target_agent *agent)
+{
+ __be32 state;
+
+ switch (tcode) {
+ case TCODE_READ_QUADLET_REQUEST:
+ pr_debug("tgt_agent AGENT_STATE READ\n");
+
+ spin_lock_bh(&agent->lock);
+ state = cpu_to_be32(agent->state);
+ spin_unlock_bh(&agent->lock);
+ memcpy(data, &state, sizeof(state));
+
+ return RCODE_COMPLETE;
+
+ case TCODE_WRITE_QUADLET_REQUEST:
+ /* ignored */
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_agent_reset(struct fw_card *card, int tcode, void *data,
+ struct sbp_target_agent *agent)
+{
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ pr_debug("tgt_agent AGENT_RESET\n");
+ spin_lock_bh(&agent->lock);
+ agent->state = AGENT_STATE_RESET;
+ spin_unlock_bh(&agent->lock);
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_orb_pointer(struct fw_card *card, int tcode, void *data,
+ struct sbp_target_agent *agent)
+{
+ struct sbp2_pointer *ptr = data;
+
+ switch (tcode) {
+ case TCODE_WRITE_BLOCK_REQUEST:
+ spin_lock_bh(&agent->lock);
+ if (agent->state != AGENT_STATE_SUSPENDED &&
+ agent->state != AGENT_STATE_RESET) {
+ spin_unlock_bh(&agent->lock);
+ pr_notice("Ignoring ORB_POINTER write while active.\n");
+ return RCODE_CONFLICT_ERROR;
+ }
+ agent->state = AGENT_STATE_ACTIVE;
+ spin_unlock_bh(&agent->lock);
+
+ agent->orb_pointer = sbp2_pointer_to_addr(ptr);
+ agent->doorbell = false;
+
+ pr_debug("tgt_agent ORB_POINTER write: 0x%llx\n",
+ agent->orb_pointer);
+
+ queue_work(system_unbound_wq, &agent->work);
+
+ return RCODE_COMPLETE;
+
+ case TCODE_READ_BLOCK_REQUEST:
+ pr_debug("tgt_agent ORB_POINTER READ\n");
+ spin_lock_bh(&agent->lock);
+ addr_to_sbp2_pointer(agent->orb_pointer, ptr);
+ spin_unlock_bh(&agent->lock);
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_doorbell(struct fw_card *card, int tcode, void *data,
+ struct sbp_target_agent *agent)
+{
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ spin_lock_bh(&agent->lock);
+ if (agent->state != AGENT_STATE_SUSPENDED) {
+ spin_unlock_bh(&agent->lock);
+ pr_debug("Ignoring DOORBELL while active.\n");
+ return RCODE_CONFLICT_ERROR;
+ }
+ agent->state = AGENT_STATE_ACTIVE;
+ spin_unlock_bh(&agent->lock);
+
+ agent->doorbell = true;
+
+ pr_debug("tgt_agent DOORBELL\n");
+
+ queue_work(system_unbound_wq, &agent->work);
+
+ return RCODE_COMPLETE;
+
+ case TCODE_READ_QUADLET_REQUEST:
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_unsolicited_status_enable(struct fw_card *card,
+ int tcode, void *data, struct sbp_target_agent *agent)
+{
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ pr_debug("tgt_agent UNSOLICITED_STATUS_ENABLE\n");
+ /* ignored as we don't send unsolicited status */
+ return RCODE_COMPLETE;
+
+ case TCODE_READ_QUADLET_REQUEST:
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static void tgt_agent_rw(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source, int generation,
+ unsigned long long offset, void *data, size_t length,
+ void *callback_data)
+{
+ struct sbp_target_agent *agent = callback_data;
+ struct sbp_session *sess = agent->login->sess;
+ int sess_gen, sess_node, rcode;
+
+ spin_lock_bh(&sess->lock);
+ sess_gen = sess->generation;
+ sess_node = sess->node_id;
+ spin_unlock_bh(&sess->lock);
+
+ if (generation != sess_gen) {
+ pr_notice("ignoring request with wrong generation\n");
+ rcode = RCODE_TYPE_ERROR;
+ goto out;
+ }
+
+ if (source != sess_node) {
+ pr_notice("ignoring request from foreign node (%x != %x)\n",
+ source, sess_node);
+ rcode = RCODE_TYPE_ERROR;
+ goto out;
+ }
+
+ /* turn offset into the offset from the start of the block */
+ offset -= agent->handler.offset;
+
+ if (offset == 0x00 && length == 4) {
+ /* AGENT_STATE */
+ rcode = tgt_agent_rw_agent_state(card, tcode, data, agent);
+ } else if (offset == 0x04 && length == 4) {
+ /* AGENT_RESET */
+ rcode = tgt_agent_rw_agent_reset(card, tcode, data, agent);
+ } else if (offset == 0x08 && length == 8) {
+ /* ORB_POINTER */
+ rcode = tgt_agent_rw_orb_pointer(card, tcode, data, agent);
+ } else if (offset == 0x10 && length == 4) {
+ /* DOORBELL */
+ rcode = tgt_agent_rw_doorbell(card, tcode, data, agent);
+ } else if (offset == 0x14 && length == 4) {
+ /* UNSOLICITED_STATUS_ENABLE */
+ rcode = tgt_agent_rw_unsolicited_status_enable(card, tcode,
+ data, agent);
+ } else {
+ rcode = RCODE_ADDRESS_ERROR;
+ }
+
+out:
+ fw_send_response(card, request, rcode);
+}
+
+static void tgt_agent_process_work(struct work_struct *work)
+{
+ struct sbp_target_request *req =
+ container_of(work, struct sbp_target_request, work);
+
+ pr_debug("tgt_orb ptr:0x%llx next_ORB:0x%llx data_descriptor:0x%llx misc:0x%x\n",
+ req->orb_pointer,
+ sbp2_pointer_to_addr(&req->orb.next_orb),
+ sbp2_pointer_to_addr(&req->orb.data_descriptor),
+ be32_to_cpu(req->orb.misc));
+
+ if (req->orb_pointer >> 32)
+ pr_debug("ORB with high bits set\n");
+
+ switch (ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc))) {
+ case 0:/* Format specified by this standard */
+ sbp_handle_command(req);
+ return;
+ case 1: /* Reserved for future standardization */
+ case 2: /* Vendor-dependent */
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(
+ STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(
+ SBP_STATUS_REQ_TYPE_NOTSUPP));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ case 3: /* Dummy ORB */
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(
+ STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(
+ SBP_STATUS_DUMMY_ORB_COMPLETE));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ default:
+ BUG();
+ }
+}
+
+/* used to double-check we haven't been issued an AGENT_RESET */
+static inline bool tgt_agent_check_active(struct sbp_target_agent *agent)
+{
+ bool active;
+
+ spin_lock_bh(&agent->lock);
+ active = (agent->state == AGENT_STATE_ACTIVE);
+ spin_unlock_bh(&agent->lock);
+
+ return active;
+}
+
+static void tgt_agent_fetch_work(struct work_struct *work)
+{
+ struct sbp_target_agent *agent =
+ container_of(work, struct sbp_target_agent, work);
+ struct sbp_session *sess = agent->login->sess;
+ struct sbp_target_request *req;
+ int ret;
+ bool doorbell = agent->doorbell;
+ u64 next_orb = agent->orb_pointer;
+
+ while (next_orb && tgt_agent_check_active(agent)) {
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req) {
+ spin_lock_bh(&agent->lock);
+ agent->state = AGENT_STATE_DEAD;
+ spin_unlock_bh(&agent->lock);
+ return;
+ }
+
+ req->login = agent->login;
+ req->orb_pointer = next_orb;
+
+ req->status.status = cpu_to_be32(STATUS_BLOCK_ORB_OFFSET_HIGH(
+ req->orb_pointer >> 32));
+ req->status.orb_low = cpu_to_be32(
+ req->orb_pointer & 0xfffffffc);
+
+ /* read in the ORB */
+ ret = sbp_run_transaction(sess->card, TCODE_READ_BLOCK_REQUEST,
+ sess->node_id, sess->generation, sess->speed,
+ req->orb_pointer, &req->orb, sizeof(req->orb));
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("tgt_orb fetch failed: %x\n", ret);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_SRC(
+ STATUS_SRC_ORB_FINISHED) |
+ STATUS_BLOCK_RESP(
+ STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(1) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(
+ SBP_STATUS_UNSPECIFIED_ERROR));
+ spin_lock_bh(&agent->lock);
+ agent->state = AGENT_STATE_DEAD;
+ spin_unlock_bh(&agent->lock);
+
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ }
+
+ /* check the next_ORB field */
+ if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+ next_orb = 0;
+ req->status.status |= cpu_to_be32(STATUS_BLOCK_SRC(
+ STATUS_SRC_ORB_FINISHED));
+ } else {
+ next_orb = sbp2_pointer_to_addr(&req->orb.next_orb);
+ req->status.status |= cpu_to_be32(STATUS_BLOCK_SRC(
+ STATUS_SRC_ORB_CONTINUING));
+ }
+
+ if (tgt_agent_check_active(agent) && !doorbell) {
+ INIT_WORK(&req->work, tgt_agent_process_work);
+ queue_work(system_unbound_wq, &req->work);
+ } else {
+ /* don't process this request, just check next_ORB */
+ sbp_free_request(req);
+ }
+
+ spin_lock_bh(&agent->lock);
+ doorbell = agent->doorbell = false;
+
+ /* check if we should carry on processing */
+ if (next_orb)
+ agent->orb_pointer = next_orb;
+ else
+ agent->state = AGENT_STATE_SUSPENDED;
+
+ spin_unlock_bh(&agent->lock);
+ };
+}
+
+struct sbp_target_agent *sbp_target_agent_register(
+ struct sbp_login_descriptor *login)
+{
+ struct sbp_target_agent *agent;
+ int ret;
+
+ agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+ if (!agent)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock_init(&agent->lock);
+
+ agent->handler.length = 0x20;
+ agent->handler.address_callback = tgt_agent_rw;
+ agent->handler.callback_data = agent;
+
+ agent->login = login;
+ agent->state = AGENT_STATE_RESET;
+ INIT_WORK(&agent->work, tgt_agent_fetch_work);
+ agent->orb_pointer = 0;
+ agent->doorbell = false;
+
+ ret = fw_core_add_address_handler(&agent->handler,
+ &sbp_register_region);
+ if (ret < 0) {
+ kfree(agent);
+ return ERR_PTR(ret);
+ }
+
+ return agent;
+}
+
+void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+ fw_core_remove_address_handler(&agent->handler);
+ cancel_work_sync(&agent->work);
+ kfree(agent);
+}
diff --git a/drivers/target/sbp/sbp_target_agent.h b/drivers/target/sbp/sbp_target_agent.h
new file mode 100644
index 0000000..b5d76fd
--- /dev/null
+++ b/drivers/target/sbp/sbp_target_agent.h
@@ -0,0 +1,40 @@
+#ifndef _SBP_TARGET_AGENT_H
+#define _SBP_TARGET_AGENT_H
+
+#include <linux/firewire.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+
+struct sbp_target_agent {
+ spinlock_t lock;
+ struct fw_address_handler handler;
+ struct sbp_login_descriptor *login;
+ int state;
+ struct work_struct work;
+ u64 orb_pointer;
+ bool doorbell;
+};
+
+struct sbp_target_request {
+ struct sbp_login_descriptor *login;
+ u64 orb_pointer;
+ struct sbp_command_block_orb orb;
+ struct sbp_status_block status;
+ struct work_struct work;
+
+ struct se_cmd se_cmd;
+ struct sbp_page_table_entry *pg_tbl;
+ void *cmd_buf;
+
+ unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+struct sbp_target_agent *sbp_target_agent_register(
+ struct sbp_login_descriptor *login);
+void sbp_target_agent_unregister(struct sbp_target_agent *agent);
+
+#endif
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}
2012-04-11 14:20 ` [PATCH 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h} Chris Boot
@ 2012-04-14 10:49 ` Stefan Richter
2012-04-14 11:33 ` Stefan Richter
0 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-04-14 10:49 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Apr 11 Chris Boot wrote:
> +static int tgt_agent_rw_agent_state(struct fw_card *card, int tcode, void *data,
> + struct sbp_target_agent *agent)
> +{
> + __be32 state;
> +
> + switch (tcode) {
> + case TCODE_READ_QUADLET_REQUEST:
> + pr_debug("tgt_agent AGENT_STATE READ\n");
> +
> + spin_lock_bh(&agent->lock);
> + state = cpu_to_be32(agent->state);
> + spin_unlock_bh(&agent->lock);
> + memcpy(data, &state, sizeof(state));
> +
> + return RCODE_COMPLETE;
> +
> + case TCODE_WRITE_QUADLET_REQUEST:
> + /* ignored */
> + return RCODE_COMPLETE;
> +
> + default:
> + return RCODE_TYPE_ERROR;
> + }
> +}
agent->state is an int; reading an int is atomic. Hence the access on
its own does not benefit from lock acquisition.
The memcpy can be simplified to
*(__be32 *)data = cpu_to_be32(agent->state);
if data is always at least quadlet aligned. Thy type cast is only to tell
code checkers like sparse that we know what we are doing. If data may be
unaligned, you could use
put_unaligned_be32(agent->state, data);
--
Stefan Richter
-=====-===-- -=-- -===-
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}
2012-04-14 10:49 ` Stefan Richter
@ 2012-04-14 11:33 ` Stefan Richter
0 siblings, 0 replies; 104+ messages in thread
From: Stefan Richter @ 2012-04-14 11:33 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, nab
On Apr 14 Stefan Richter wrote:
> On Apr 11 Chris Boot wrote:
> > +static int tgt_agent_rw_agent_state(struct fw_card *card, int tcode, void *data,
> > + struct sbp_target_agent *agent)
> > +{
> > + __be32 state;
> > +
> > + switch (tcode) {
> > + case TCODE_READ_QUADLET_REQUEST:
> > + pr_debug("tgt_agent AGENT_STATE READ\n");
> > +
> > + spin_lock_bh(&agent->lock);
> > + state = cpu_to_be32(agent->state);
> > + spin_unlock_bh(&agent->lock);
> > + memcpy(data, &state, sizeof(state));
> > +
> > + return RCODE_COMPLETE;
> > +
> > + case TCODE_WRITE_QUADLET_REQUEST:
> > + /* ignored */
> > + return RCODE_COMPLETE;
> > +
> > + default:
> > + return RCODE_TYPE_ERROR;
> > + }
> > +}
>
> agent->state is an int; reading an int is atomic. Hence the access on
> its own does not benefit from lock acquisition.
Actually this is only true because all write accesses to agent->state are
merely assignments (not increments or the like). And I have to admit that
I don't remember whether this is only how compilers work in practice or it
is actually required by the C language specification.
> The memcpy can be simplified to
>
> *(__be32 *)data = cpu_to_be32(agent->state);
>
> if data is always at least quadlet aligned. Thy type cast is only to tell
> code checkers like sparse that we know what we are doing.
So unless there is such an atomicity guarantee, leave the locking as is
and prefer
int state;
spin_lock_bh(&agent->lock);
state = agent->state;
spin_unlock_bh(&agent->lock);
*(__be32 *)data = cpu_to_be32(state);
> If data may be
> unaligned, you could use
>
> put_unaligned_be32(agent->state, data);
OK, I read further. This is part of your handler.address_callback.
data will point to quadlet aligned memory then. It is no where written as
an API specification, but you may rest assured that firewire-core will
always align quadlet read or block read response buffers at least on
quadlet boundary. You can safely cast data into an u32 or __be32 pointer.
--
Stefan Richter
-=====-===-- -=-- -===-
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH 10/11] firewire-sbp-target: Add sbp_scsi_cmnd.{c,h}
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (8 preceding siblings ...)
2012-04-11 14:20 ` [PATCH 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h} Chris Boot
@ 2012-04-11 14:20 ` Chris Boot
2012-04-11 14:20 ` [PATCH 11/11] firewire-sbp-target: Add to target Kconfig and Makefile Chris Boot
` (2 subsequent siblings)
12 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-11 14:20 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
Miscellaneous functions for dealing with SCSI commands, status, sense
data and data read/write. This is where the real grunt work of pushing
data in and out of the FireWire bus happens.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
drivers/target/sbp/sbp_scsi_cmnd.c | 430 ++++++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_scsi_cmnd.h | 12 +
2 files changed, 442 insertions(+)
create mode 100644 drivers/target/sbp/sbp_scsi_cmnd.c
create mode 100644 drivers/target/sbp/sbp_scsi_cmnd.h
diff --git a/drivers/target/sbp/sbp_scsi_cmnd.c b/drivers/target/sbp/sbp_scsi_cmnd.c
new file mode 100644
index 0000000..13407cf
--- /dev/null
+++ b/drivers/target/sbp/sbp_scsi_cmnd.c
@@ -0,0 +1,430 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+
+#include "sbp_base.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+/*
+ * Simple wrapper around fw_run_transaction that retries the transaction several
+ * times in case of failure, with an exponential backoff.
+ */
+int sbp_run_transaction(struct fw_card *card, int tcode, int destination_id,
+ int generation, int speed, unsigned long long offset,
+ void *payload, size_t length)
+{
+ int attempt, ret, delay;
+
+ for (attempt = 1; attempt <= 5; attempt++) {
+ ret = fw_run_transaction(card, tcode, destination_id,
+ generation, speed, offset, payload, length);
+
+ switch (ret) {
+ case RCODE_COMPLETE:
+ case RCODE_TYPE_ERROR:
+ case RCODE_ADDRESS_ERROR:
+ case RCODE_GENERATION:
+ return ret;
+
+ default:
+ delay = 5 * attempt * attempt;
+ usleep_range(delay, delay * 2);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Wrapper around sbp_run_transaction that gets the card, destination,
+ * generation and speed out of the request's session.
+ */
+static int sbp_run_request_transaction(struct sbp_target_request *req,
+ int tcode, unsigned long long offset, void *payload,
+ size_t length)
+{
+ struct sbp_login_descriptor *login = req->login;
+ struct sbp_session *sess = login->sess;
+ struct fw_card *card;
+ int node_id, generation, speed, ret;
+
+ spin_lock_bh(&sess->lock);
+ card = fw_card_get(sess->card);
+ node_id = sess->node_id;
+ generation = sess->generation;
+ speed = sess->speed;
+ spin_unlock_bh(&sess->lock);
+
+ ret = sbp_run_transaction(card, tcode, node_id, generation, speed,
+ offset, payload, length);
+
+ fw_card_put(card);
+
+ return ret;
+}
+
+static int sbp_fetch_command(struct sbp_target_request *req)
+{
+ int ret, cmd_len, copy_len;
+
+ cmd_len = scsi_command_size(req->orb.command_block);
+
+ req->cmd_buf = kmalloc(cmd_len, GFP_KERNEL);
+ if (!req->cmd_buf)
+ return -ENOMEM;
+
+ memcpy(req->cmd_buf, req->orb.command_block,
+ min_t(int, cmd_len, sizeof(req->orb.command_block)));
+
+ if (cmd_len > sizeof(req->orb.command_block)) {
+ pr_debug("sbp_fetch_command: filling in long command\n");
+ copy_len = cmd_len - sizeof(req->orb.command_block);
+
+ ret = sbp_run_request_transaction(req,
+ TCODE_READ_BLOCK_REQUEST,
+ req->orb_pointer + sizeof(req->orb),
+ req->cmd_buf + sizeof(req->orb.command_block),
+ copy_len);
+ if (ret != RCODE_COMPLETE)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sbp_fetch_page_table(struct sbp_target_request *req)
+{
+ int pg_tbl_sz, ret;
+ struct sbp_page_table_entry *pg_tbl;
+
+ if (!CMDBLK_ORB_PG_TBL_PRESENT(be32_to_cpu(req->orb.misc)))
+ return 0;
+
+ pg_tbl_sz = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc)) *
+ sizeof(struct sbp_page_table_entry);
+
+ pg_tbl = kmalloc(pg_tbl_sz, GFP_KERNEL);
+ if (!pg_tbl)
+ return -ENOMEM;
+
+ ret = sbp_run_request_transaction(req, TCODE_READ_BLOCK_REQUEST,
+ sbp2_pointer_to_addr(&req->orb.data_descriptor),
+ pg_tbl, pg_tbl_sz);
+ if (ret != RCODE_COMPLETE) {
+ kfree(pg_tbl);
+ return -EIO;
+ }
+
+ req->pg_tbl = pg_tbl;
+ return 0;
+}
+
+static void sbp_calc_data_length_direction(struct sbp_target_request *req,
+ u32 *data_len, enum dma_data_direction *data_dir)
+{
+ int data_size, direction, idx;
+
+ data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+ direction = CMDBLK_ORB_DIRECTION(be32_to_cpu(req->orb.misc));
+
+ if (!data_size) {
+ *data_len = 0;
+ *data_dir = DMA_NONE;
+ return;
+ }
+
+ *data_dir = direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (req->pg_tbl) {
+ *data_len = 0;
+ for (idx = 0; idx < data_size; idx++) {
+ *data_len += be16_to_cpu(
+ req->pg_tbl[idx].segment_length);
+ }
+ } else {
+ *data_len = data_size;
+ }
+}
+
+void sbp_handle_command(struct sbp_target_request *req)
+{
+ struct sbp_login_descriptor *login = req->login;
+ struct sbp_session *sess = login->sess;
+ int ret, unpacked_lun;
+ u32 data_length;
+ enum dma_data_direction data_dir;
+
+ ret = sbp_fetch_command(req);
+ if (ret) {
+ pr_debug("sbp_handle_command: fetch command failed: %d\n", ret);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ }
+
+ ret = sbp_fetch_page_table(req);
+ if (ret) {
+ pr_debug("sbp_handle_command: fetch page table failed: %d\n",
+ ret);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ }
+
+ unpacked_lun = req->login->lun->unpacked_lun;
+ sbp_calc_data_length_direction(req, &data_length, &data_dir);
+
+ pr_debug("sbp_handle_command ORB:0x%llx unpacked_lun:%d data_len:%d data_dir:%d\n",
+ req->orb_pointer, unpacked_lun, data_length, data_dir);
+
+ target_submit_cmd(&req->se_cmd, sess->se_sess, req->cmd_buf,
+ req->sense_buf, unpacked_lun, data_length,
+ MSG_SIMPLE_TAG, data_dir, 0);
+}
+
+/*
+ * DMA_TO_DEVICE = read from initiator (SCSI WRITE)
+ * DMA_FROM_DEVICE = write to initiator (SCSI READ)
+ */
+int sbp_rw_data(struct sbp_target_request *req)
+{
+ struct sbp_session *sess = req->login->sess;
+ int tcode, sg_miter_flags, max_payload, pg_size, speed, node_id,
+ generation, num_pte, length, tfr_length,
+ rcode = RCODE_COMPLETE;
+ struct sbp_page_table_entry *pte;
+ unsigned long long offset;
+ struct fw_card *card;
+ struct sg_mapping_iter iter;
+
+ if (req->se_cmd.data_direction == DMA_FROM_DEVICE) {
+ tcode = TCODE_WRITE_BLOCK_REQUEST;
+ sg_miter_flags = SG_MITER_FROM_SG;
+ } else {
+ tcode = TCODE_READ_BLOCK_REQUEST;
+ sg_miter_flags = SG_MITER_TO_SG;
+ }
+
+ max_payload = 4 << CMDBLK_ORB_MAX_PAYLOAD(be32_to_cpu(req->orb.misc));
+ speed = CMDBLK_ORB_SPEED(be32_to_cpu(req->orb.misc));
+
+ pg_size = CMDBLK_ORB_PG_SIZE(be32_to_cpu(req->orb.misc));
+ if (pg_size) {
+ pr_err("sbp_run_transaction: page size ignored\n");
+ pg_size = 0x100 << pg_size;
+ }
+
+ spin_lock_bh(&sess->lock);
+ card = fw_card_get(sess->card);
+ node_id = sess->node_id;
+ generation = sess->generation;
+ spin_unlock_bh(&sess->lock);
+
+ if (req->pg_tbl) {
+ pte = req->pg_tbl;
+ num_pte = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+
+ offset = 0;
+ length = 0;
+ } else {
+ pte = NULL;
+ num_pte = 0;
+
+ offset = sbp2_pointer_to_addr(&req->orb.data_descriptor);
+ length = req->se_cmd.data_length;
+ }
+
+ sg_miter_start(&iter, req->se_cmd.t_data_sg, req->se_cmd.t_data_nents,
+ sg_miter_flags);
+
+ while (length || num_pte) {
+ if (!length) {
+ offset = (u64)be16_to_cpu(pte->segment_base_hi) << 32 |
+ be32_to_cpu(pte->segment_base_lo);
+ length = be16_to_cpu(pte->segment_length);
+
+ pte++;
+ num_pte--;
+ }
+
+ sg_miter_next(&iter);
+
+ tfr_length = min3(length, max_payload, (int)iter.length);
+
+ /* FIXME: take page_size into account */
+
+ rcode = sbp_run_transaction(card, tcode, node_id,
+ generation, speed,
+ offset, iter.addr, tfr_length);
+
+ if (rcode != RCODE_COMPLETE)
+ break;
+
+ length -= tfr_length;
+ offset += tfr_length;
+ iter.consumed = tfr_length;
+ }
+
+ sg_miter_stop(&iter);
+ fw_card_put(card);
+
+ if (rcode == RCODE_COMPLETE) {
+ WARN_ON(length != 0);
+ return 0;
+ } else {
+ return -EIO;
+ }
+}
+
+int sbp_send_status(struct sbp_target_request *req)
+{
+ int ret, length;
+ struct sbp_login_descriptor *login = req->login;
+
+ length = (((be32_to_cpu(req->status.status) >> 24) & 0x07) + 1) * 4;
+
+ ret = sbp_run_request_transaction(req, TCODE_WRITE_BLOCK_REQUEST,
+ login->status_fifo_addr, &req->status, length);
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("sbp_send_status: write failed: 0x%x\n", ret);
+ return -EIO;
+ }
+
+ pr_debug("sbp_send_status: status write complete for ORB: 0x%llx\n",
+ req->orb_pointer);
+
+ return 0;
+}
+
+static void sbp_sense_mangle(struct sbp_target_request *req)
+{
+ struct se_cmd *se_cmd = &req->se_cmd;
+ u8 *sense = req->sense_buf;
+ u8 *status = req->status.data;
+
+ WARN_ON(se_cmd->scsi_sense_length < 18);
+
+ switch (sense[0] & 0x7f) { /* sfmt */
+ case 0x70: /* current, fixed */
+ status[0] = 0 << 6;
+ break;
+ case 0x71: /* deferred, fixed */
+ status[0] = 1 << 6;
+ break;
+ case 0x72: /* current, descriptor */
+ case 0x73: /* deferred, descriptor */
+ default:
+ /*
+ * TODO: SBP-3 specifies what we should do with descriptor
+ * format sense data
+ */
+ pr_err("sbp_send_sense: unknown sense format: 0x%x\n",
+ sense[0]);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQUEST_ABORTED));
+ return;
+ }
+
+ status[0] |= se_cmd->scsi_status & 0x3f;/* status */
+ status[1] =
+ (sense[0] & 0x80) | /* valid */
+ ((sense[2] & 0xe0) >> 1) | /* mark, eom, ili */
+ (sense[2] & 0x0f); /* sense_key */
+ status[2] = se_cmd->scsi_asc; /* sense_code */
+ status[3] = se_cmd->scsi_ascq; /* sense_qualifier */
+
+ /* information */
+ status[4] = sense[3];
+ status[5] = sense[4];
+ status[6] = sense[5];
+ status[7] = sense[6];
+
+ /* CDB-dependent */
+ status[8] = sense[8];
+ status[9] = sense[9];
+ status[10] = sense[10];
+ status[11] = sense[11];
+
+ /* fru */
+ status[12] = sense[14];
+
+ /* sense_key-dependent */
+ status[13] = sense[15];
+ status[14] = sense[16];
+ status[15] = sense[17];
+
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(5) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+int sbp_send_sense(struct sbp_target_request *req)
+{
+ struct se_cmd *se_cmd = &req->se_cmd;
+
+ if (se_cmd->scsi_sense_length) {
+ sbp_sense_mangle(req);
+ } else {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+ }
+
+ return sbp_send_status(req);
+}
+
+void sbp_free_request(struct sbp_target_request *req)
+{
+ kfree(req->pg_tbl);
+ kfree(req->cmd_buf);
+ kfree(req);
+}
diff --git a/drivers/target/sbp/sbp_scsi_cmnd.h b/drivers/target/sbp/sbp_scsi_cmnd.h
new file mode 100644
index 0000000..d02f4ca
--- /dev/null
+++ b/drivers/target/sbp/sbp_scsi_cmnd.h
@@ -0,0 +1,12 @@
+
+#include "sbp_target_agent.h"
+
+int sbp_run_transaction(struct fw_card *card, int tcode, int destination_id,
+ int generation, int speed, unsigned long long offset,
+ void *payload, size_t length);
+
+void sbp_handle_command(struct sbp_target_request *req);
+int sbp_rw_data(struct sbp_target_request *req);
+int sbp_send_status(struct sbp_target_request *req);
+int sbp_send_sense(struct sbp_target_request *req);
+void sbp_free_request(struct sbp_target_request *req);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 11/11] firewire-sbp-target: Add to target Kconfig and Makefile
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (9 preceding siblings ...)
2012-04-11 14:20 ` [PATCH 10/11] firewire-sbp-target: Add sbp_scsi_cmnd.{c,h} Chris Boot
@ 2012-04-11 14:20 ` Chris Boot
2012-04-12 21:02 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Andy Grover
2012-04-13 3:03 ` Nicholas A. Bellinger
12 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-11 14:20 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
This commit also adds an entry to the MAINTAINERS file.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
---
MAINTAINERS | 9 +++++++++
drivers/target/Kconfig | 1 +
drivers/target/Makefile | 1 +
3 files changed, 11 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index b31bdff..b084020 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2754,6 +2754,15 @@ T: git git://git.alsa-project.org/alsa-kernel.git
S: Maintained
F: sound/firewire/
+FIREWIRE SBP-2 TARGET
+M: Chris Boot <bootc@bootc.net>
+L: linux-scsi@vger.kernel.org
+L: target-devel@vger.kernel.org
+L: linux1394-devel@lists.sourceforge.net
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core-2.6.git master
+S: Maintained
+F: drivers/target/sbp/
+
FIREWIRE SUBSYSTEM
M: Stefan Richter <stefanr@s5r6.in-berlin.de>
L: linux1394-devel@lists.sourceforge.net
diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig
index fc5fa9f..2cfa467 100644
--- a/drivers/target/Kconfig
+++ b/drivers/target/Kconfig
@@ -41,5 +41,6 @@ source "drivers/target/tcm_fc/Kconfig"
source "drivers/target/iscsi/Kconfig"
source "drivers/target/tcm_vhost/Kconfig"
source "drivers/target/usb-gadget/Kconfig"
+source "drivers/target/sbp/Kconfig"
endif
diff --git a/drivers/target/Makefile b/drivers/target/Makefile
index 6b5f526..1ae8862 100644
--- a/drivers/target/Makefile
+++ b/drivers/target/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_TCM_FC) += tcm_fc/
obj-$(CONFIG_ISCSI_TARGET) += iscsi/
obj-$(CONFIG_TCM_VHOST) += tcm_vhost/
obj-$(CONFIG_TARGET_USB_GADGET) += usb-gadget/
+obj-$(CONFIG_FIREWIRE_SBP_TARGET) += sbp/
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (10 preceding siblings ...)
2012-04-11 14:20 ` [PATCH 11/11] firewire-sbp-target: Add to target Kconfig and Makefile Chris Boot
@ 2012-04-12 21:02 ` Andy Grover
2012-04-13 3:03 ` Nicholas A. Bellinger
12 siblings, 0 replies; 104+ messages in thread
From: Andy Grover @ 2012-04-12 21:02 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, clemens, nab, stefanr
On 04/11/2012 07:20 AM, Chris Boot wrote:
> The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection
> as a SCSI transport. This module uses the SCSI Target framework to
> expose LUNs to other machines attached to a FireWire bus, in effect
> acting as a FireWire hard disk similar to FireWire Target Disk mode on
> many Apple computers.
>
> Sorry this latest revision has been a long time coming. I was trying to
> chase down a crashing bug (but haven't been able to replicate it), changed
> my mind about a large portion of the code twice, and life got in the way as
> well!
I just want to say this is awesome and I can't wait to try it!
-- Andy
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target
2012-04-11 14:20 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
` (11 preceding siblings ...)
2012-04-12 21:02 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Andy Grover
@ 2012-04-13 3:03 ` Nicholas A. Bellinger
2012-04-13 13:16 ` Chris Boot
12 siblings, 1 reply; 104+ messages in thread
From: Nicholas A. Bellinger @ 2012-04-13 3:03 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens,
stefanr, Christoph Hellwig
On Wed, 2012-04-11 at 15:20 +0100, Chris Boot wrote:
> The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection
> as a SCSI transport. This module uses the SCSI Target framework to
> expose LUNs to other machines attached to a FireWire bus, in effect
> acting as a FireWire hard disk similar to FireWire Target Disk mode on
> many Apple computers.
>
> Sorry this latest revision has been a long time coming. I was trying to
> chase down a crashing bug (but haven't been able to replicate it), changed
> my mind about a large portion of the code twice, and life got in the way as
> well!
>
Some very nice work to make this all go Chris.. 8-)
Thanks again btw for your target_core_mod patches along the way to v3!
> Changes in v3:
> * Updates for target framework API changes
> * Attempt to make headers self-contained
> * Remove bad use of atomics and memory barriers
> * Rework use of locking
> * Use system workqueues
> * Wrap fw_run_transaction() to retry failed transactions
> * Coding style fixes
> * Fix a few bugs
> * Overhaul sbp_rw_data()
> * Overhaul target fetch agent
>
So I've merged a squashed version this series from:
https://github.com/bootc/Linux-SBP-2-Target.git patch-v3
into lio-core/master, and have now pushed to k.o upstream
This looks to me like a reasonable candidate for a v3.5 mainline merge,
so we need to go ahead and get this into target-pending.. Also, thanks
for getting Stefan's ACK's here on the drivers/firmware/ specific
changes..
So for new fabric drivers (as discused with Sebastian and usb-gadget
recently) that the target team has been enforcing that new fabric
modules convert to use a single source / header file for readability and
maintainability purposes.
It would be great if you can publish one more branch (no need to re-post
the full diff) that takes your patch-v3 down to a single .c/.h file.
Otherwise, I'm fine to take the 15 minutes to do this conversion ahead
of dropping into target-pending for the sunday night linux-next build.
Thanks again Chris!
--nab
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target
2012-04-13 3:03 ` Nicholas A. Bellinger
@ 2012-04-13 13:16 ` Chris Boot
2012-04-14 1:23 ` Nicholas A. Bellinger
0 siblings, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-04-13 13:16 UTC (permalink / raw)
To: Nicholas A. Bellinger
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens,
stefanr, Christoph Hellwig
On 13/04/2012 04:03, Nicholas A. Bellinger wrote:
> On Wed, 2012-04-11 at 15:20 +0100, Chris Boot wrote:
>> The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection
>> as a SCSI transport. This module uses the SCSI Target framework to
>> expose LUNs to other machines attached to a FireWire bus, in effect
>> acting as a FireWire hard disk similar to FireWire Target Disk mode on
>> many Apple computers.
>>
>> Sorry this latest revision has been a long time coming. I was trying to
>> chase down a crashing bug (but haven't been able to replicate it), changed
>> my mind about a large portion of the code twice, and life got in the way as
>> well!
>>
> Some very nice work to make this all go Chris.. 8-)
>
> Thanks again btw for your target_core_mod patches along the way to v3!
No problem, it's been a very useful learning experience and I'm really
glad I did it! :-)
>> Changes in v3:
>> * Updates for target framework API changes
>> * Attempt to make headers self-contained
>> * Remove bad use of atomics and memory barriers
>> * Rework use of locking
>> * Use system workqueues
>> * Wrap fw_run_transaction() to retry failed transactions
>> * Coding style fixes
>> * Fix a few bugs
>> * Overhaul sbp_rw_data()
>> * Overhaul target fetch agent
>>
> So I've merged a squashed version this series from:
>
> https://github.com/bootc/Linux-SBP-2-Target.git patch-v3
>
> into lio-core/master, and have now pushed to k.o upstream
Thanks!
> This looks to me like a reasonable candidate for a v3.5 mainline merge,
> so we need to go ahead and get this into target-pending.. Also, thanks
> for getting Stefan's ACK's here on the drivers/firmware/ specific
> changes..
>
> So for new fabric drivers (as discused with Sebastian and usb-gadget
> recently) that the target team has been enforcing that new fabric
> modules convert to use a single source / header file for readability and
> maintainability purposes.
>
> It would be great if you can publish one more branch (no need to re-post
> the full diff) that takes your patch-v3 down to a single .c/.h file.
> Otherwise, I'm fine to take the 15 minutes to do this conversion ahead
> of dropping into target-pending for the sunday night linux-next build.
I'll get onto it over the weekend, I hope. I assume in this case I just
stick it all in drivers/target/ instead of having a sbp/ directory
underneath that?
Cheers,
Chris
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target
2012-04-13 13:16 ` Chris Boot
@ 2012-04-14 1:23 ` Nicholas A. Bellinger
2012-04-14 9:12 ` [PATCH 0/2] sbp-target: cleanup after merge into single file Chris Boot
2012-04-17 10:48 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
0 siblings, 2 replies; 104+ messages in thread
From: Nicholas A. Bellinger @ 2012-04-14 1:23 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens,
stefanr, Christoph Hellwig
On Fri, 2012-04-13 at 14:16 +0100, Chris Boot wrote:
> On 13/04/2012 04:03, Nicholas A. Bellinger wrote:
<SNIP>
> > So I've merged a squashed version this series from:
> >
> > https://github.com/bootc/Linux-SBP-2-Target.git patch-v3
> >
> > into lio-core/master, and have now pushed to k.o upstream
>
> Thanks!
>
> > This looks to me like a reasonable candidate for a v3.5 mainline merge,
> > so we need to go ahead and get this into target-pending.. Also, thanks
> > for getting Stefan's ACK's here on the drivers/firmware/ specific
> > changes..
> >
> > So for new fabric drivers (as discused with Sebastian and usb-gadget
> > recently) that the target team has been enforcing that new fabric
> > modules convert to use a single source / header file for readability and
> > maintainability purposes.
> >
> > It would be great if you can publish one more branch (no need to re-post
> > the full diff) that takes your patch-v3 down to a single .c/.h file.
> > Otherwise, I'm fine to take the 15 minutes to do this conversion ahead
> > of dropping into target-pending for the sunday night linux-next build.
>
> I'll get onto it over the weekend, I hope. I assume in this case I just
> stick it all in drivers/target/ instead of having a sbp/ directory
> underneath that?
>
I was eager to see this change, so I decided to go ahead and do the
mechanical conversion myself in the following branch:
git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core.git sbp-merge
As mentioned, it converts drivers/target/sbp/ to use a single sbp_target.[c,h]
source/header file following mainline convention for new target fabric
drivers. Also make as many functions as possible statically defined and
add inline prototypes where necessary.
This patch also renames the sbp-target config entry from:
FIREWIRE_SBP_TARGET -> SBP_TARGET
and also renames the final generated module name from:
fireware-sbp-target.ko -> sbp_target.ko
Here is what the diffstat ends up looking like:
drivers/target/Makefile | 2 +-
drivers/target/sbp/Kconfig | 6 +-
drivers/target/sbp/Makefile | 12 +-
drivers/target/sbp/sbp_base.h | 211 ---
drivers/target/sbp/sbp_configfs.c | 737 --------
drivers/target/sbp/sbp_fabric.c | 314 ----
drivers/target/sbp/sbp_fabric.h | 40 -
drivers/target/sbp/sbp_login.c | 672 --------
drivers/target/sbp/sbp_login.h | 17 -
drivers/target/sbp/sbp_management_agent.c | 255 ---
drivers/target/sbp/sbp_management_agent.h | 34 -
drivers/target/sbp/sbp_scsi_cmnd.c | 430 -----
drivers/target/sbp/sbp_scsi_cmnd.h | 12 -
drivers/target/sbp/sbp_target.c | 2621 +++++++++++++++++++++++++++++
drivers/target/sbp/sbp_target.h | 254 +++
drivers/target/sbp/sbp_target_agent.c | 395 -----
drivers/target/sbp/sbp_target_agent.h | 40 -
17 files changed, 2879 insertions(+), 3173 deletions(-)
delete mode 100644 drivers/target/sbp/sbp_base.h
delete mode 100644 drivers/target/sbp/sbp_configfs.c
delete mode 100644 drivers/target/sbp/sbp_fabric.c
delete mode 100644 drivers/target/sbp/sbp_fabric.h
delete mode 100644 drivers/target/sbp/sbp_login.c
delete mode 100644 drivers/target/sbp/sbp_login.h
delete mode 100644 drivers/target/sbp/sbp_management_agent.c
delete mode 100644 drivers/target/sbp/sbp_management_agent.h
delete mode 100644 drivers/target/sbp/sbp_scsi_cmnd.c
delete mode 100644 drivers/target/sbp/sbp_scsi_cmnd.h
create mode 100644 drivers/target/sbp/sbp_target.c
create mode 100644 drivers/target/sbp/sbp_target.h
delete mode 100644 drivers/target/sbp/sbp_target_agent.c
delete mode 100644 drivers/target/sbp/sbp_target_agent.h
Please have a look at this branch over the next days, and if you're OK
with the re-org changes I'll go ahead and merge this into
lio-core/master, and drop into target-pending for sunday night's build.
Thanks Chris!
--nab
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH 0/2] sbp-target: cleanup after merge into single file
2012-04-14 1:23 ` Nicholas A. Bellinger
@ 2012-04-14 9:12 ` Chris Boot
2012-04-14 9:12 ` [PATCH 1/2] sbp-target: minor cleanups after merging " Chris Boot
` (2 more replies)
2012-04-17 10:48 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
1 sibling, 3 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-14 9:12 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
Thanks to Nick Bellinger for doing the work for me and merging the
sbp-target code into single source/header files. This little series
does a bit of cleanup and updates the TODO file.
I'll also take this opportunity to add my sign-off to Nick's patch that
merges everything together, which this series is on top of:
Acked-by: Chris Boot <bootc@bootc.net>
Cheers,
Chris
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
Cc: Nicholas Bellinger <nab@linux-iscsi.org>
Chris Boot (2):
sbp-target: minor cleanups after merging into single file
sbp-target: update TODO file
drivers/target/sbp/TODO | 8 ++++----
drivers/target/sbp/sbp_target.c | 6 +++---
drivers/target/sbp/sbp_target.h | 3 ---
3 files changed, 7 insertions(+), 10 deletions(-)
--
1.7.9.5
^ permalink raw reply [flat|nested] 104+ messages in thread
* [PATCH 1/2] sbp-target: minor cleanups after merging into single file
2012-04-14 9:12 ` [PATCH 0/2] sbp-target: cleanup after merge into single file Chris Boot
@ 2012-04-14 9:12 ` Chris Boot
2012-04-14 9:12 ` [PATCH 2/2] sbp-target: update TODO file Chris Boot
2012-04-14 21:44 ` [PATCH 0/2] sbp-target: cleanup after merge into single file Nicholas A. Bellinger
2 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-14 9:12 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
Just a few minor cleanups to make a couple more elements static and
remove some extern definitions from the header which are not exported.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
Cc: Nicholas Bellinger <nab@linux-iscsi.org>
---
drivers/target/sbp/sbp_target.c | 6 +++---
drivers/target/sbp/sbp_target.h | 3 ---
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c
index d7c9ac9..37c6098 100644
--- a/drivers/target/sbp/sbp_target.c
+++ b/drivers/target/sbp/sbp_target.c
@@ -43,10 +43,10 @@
#include "sbp_target.h"
/* Local pointer to allocated TCM configfs fabric module */
-struct target_fabric_configfs *sbp_fabric_configfs;
+static struct target_fabric_configfs *sbp_fabric_configfs;
/* FireWire address region for management and command block address handlers */
-const struct fw_address_region sbp_register_region = {
+static const struct fw_address_region sbp_register_region = {
.start = CSR_REGISTER_BASE + 0x10000,
.end = 0x1000000000000ULL,
};
@@ -1435,7 +1435,7 @@ static void sbp_sense_mangle(struct sbp_target_request *req)
STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
}
-int sbp_send_sense(struct sbp_target_request *req)
+static int sbp_send_sense(struct sbp_target_request *req)
{
struct se_cmd *se_cmd = &req->se_cmd;
diff --git a/drivers/target/sbp/sbp_target.h b/drivers/target/sbp/sbp_target.h
index 1ec2269..6d0d74a 100644
--- a/drivers/target/sbp/sbp_target.h
+++ b/drivers/target/sbp/sbp_target.h
@@ -193,9 +193,6 @@ struct sbp_tport {
int max_logins_per_lun;
};
-extern struct target_fabric_configfs *sbp_fabric_configfs;
-extern const struct fw_address_region sbp_register_region;
-
static inline u64 sbp2_pointer_to_addr(const struct sbp2_pointer *ptr)
{
return (u64)(be32_to_cpu(ptr->high) & 0x0000ffff) << 32 |
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* [PATCH 2/2] sbp-target: update TODO file
2012-04-14 9:12 ` [PATCH 0/2] sbp-target: cleanup after merge into single file Chris Boot
2012-04-14 9:12 ` [PATCH 1/2] sbp-target: minor cleanups after merging " Chris Boot
@ 2012-04-14 9:12 ` Chris Boot
2012-04-14 21:44 ` [PATCH 0/2] sbp-target: cleanup after merge into single file Nicholas A. Bellinger
2 siblings, 0 replies; 104+ messages in thread
From: Chris Boot @ 2012-04-14 9:12 UTC (permalink / raw)
To: linux1394-devel, target-devel
Cc: linux-kernel, agrover, clemens, nab, stefanr, Chris Boot
The TODO wasn't updated after the last batch of changes before the
merge, so remove those items that have been completed. This patch also
re-orders the existing items in priority order and adds a new one.
Signed-off-by: Chris Boot <bootc@bootc.net>
Cc: Andy Grover <agrover@redhat.com>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Stefan Richter <stefanr@s5r6.in-berlin.de>
Cc: Nicholas Bellinger <nab@linux-iscsi.org>
---
drivers/target/sbp/TODO | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/target/sbp/TODO b/drivers/target/sbp/TODO
index eaec1c9..e800058 100644
--- a/drivers/target/sbp/TODO
+++ b/drivers/target/sbp/TODO
@@ -1,7 +1,7 @@
-* Force-terminate sessions when disabling targets
+1. Improve FireWire data transfer rate by queuing up several simultaneous transfers
+2. Force-terminate sessions when disabling targets
+3. Implement TASK MANAGEMENT functionality
* Ability to have several SCSI commands in-flight (TCQ?)
-* Retry failed FireWire transactions a few times with exponential backoff
-* Take into account the page_size field for transfers and/or page tables
+* Take into account the page_size field for transfers
* Handle descriptor format sense data
* Implement QUERY LOGINS management ORB
-* Implement TASK MANAGEMENT functionality
--
1.7.9.5
^ permalink raw reply related [flat|nested] 104+ messages in thread
* Re: [PATCH 0/2] sbp-target: cleanup after merge into single file
2012-04-14 9:12 ` [PATCH 0/2] sbp-target: cleanup after merge into single file Chris Boot
2012-04-14 9:12 ` [PATCH 1/2] sbp-target: minor cleanups after merging " Chris Boot
2012-04-14 9:12 ` [PATCH 2/2] sbp-target: update TODO file Chris Boot
@ 2012-04-14 21:44 ` Nicholas A. Bellinger
2012-04-14 23:11 ` Stefan Richter
2 siblings, 1 reply; 104+ messages in thread
From: Nicholas A. Bellinger @ 2012-04-14 21:44 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens, stefanr
On Sat, 2012-04-14 at 10:12 +0100, Chris Boot wrote:
> Thanks to Nick Bellinger for doing the work for me and merging the
> sbp-target code into single source/header files. This little series
> does a bit of cleanup and updates the TODO file.
>
> I'll also take this opportunity to add my sign-off to Nick's patch that
> merges everything together, which this series is on top of:
>
> Acked-by: Chris Boot <bootc@bootc.net>
>
Thanks for the Ack. ;)
I'll go ahead and merge the sbp-merge branch back into lio-core/master
now, and apply this series on top of master.
Also, it looks like Stefan has a few more comments from the last round
of review.. I'll go ahead and get the latest HEAD prepped for
target-pending today, and plan to rebase any forthcoming incremental
patches from you into a single merge commit in target-pending.
Thanks Chris!
--nab
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH 0/2] sbp-target: cleanup after merge into single file
2012-04-14 21:44 ` [PATCH 0/2] sbp-target: cleanup after merge into single file Nicholas A. Bellinger
@ 2012-04-14 23:11 ` Stefan Richter
2012-04-15 1:22 ` Nicholas A. Bellinger
0 siblings, 1 reply; 104+ messages in thread
From: Stefan Richter @ 2012-04-14 23:11 UTC (permalink / raw)
To: Nicholas A. Bellinger
Cc: Chris Boot, linux1394-devel, target-devel, linux-kernel, agrover,
clemens
On Apr 14 Nicholas A. Bellinger wrote:
> Also, it looks like Stefan has a few more comments from the last round
> of review..
Don't consider them blocking. I would welcome it if the code gets merged
soon, it looks very nice and maintainable. The card state <-> session
state interactions which I was wondering about will be more comfortable for
me to investigate when it is in mainline; I believe there is no urgency
for me to look into that. Other obscure but simple things like making use
of memory alignment guarantees from firewire-core are more effectively
addressed if I can send patches rather than comments.
--
Stefan Richter
-=====-===-- -=-- -====
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH 0/2] sbp-target: cleanup after merge into single file
2012-04-14 23:11 ` Stefan Richter
@ 2012-04-15 1:22 ` Nicholas A. Bellinger
0 siblings, 0 replies; 104+ messages in thread
From: Nicholas A. Bellinger @ 2012-04-15 1:22 UTC (permalink / raw)
To: Stefan Richter
Cc: Chris Boot, linux1394-devel, target-devel, linux-kernel, agrover,
clemens
On Sun, 2012-04-15 at 01:11 +0200, Stefan Richter wrote:
> On Apr 14 Nicholas A. Bellinger wrote:
> > Also, it looks like Stefan has a few more comments from the last round
> > of review..
>
> Don't consider them blocking. I would welcome it if the code gets merged
> soon, it looks very nice and maintainable. The card state <-> session
> state interactions which I was wondering about will be more comfortable for
> me to investigate when it is in mainline; I believe there is no urgency
> for me to look into that. Other obscure but simple things like making use
> of memory alignment guarantees from firewire-core are more effectively
> addressed if I can send patches rather than comments.
<nod> In that case, I'll go ahead and add your Acked-by to the initial
merge commit for sbp-target that will be appearing in
target-pending/for-next-merge shortly.
Thanks Stefan!
--nab
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target
2012-04-14 1:23 ` Nicholas A. Bellinger
2012-04-14 9:12 ` [PATCH 0/2] sbp-target: cleanup after merge into single file Chris Boot
@ 2012-04-17 10:48 ` Chris Boot
2012-04-18 7:17 ` Nicholas A. Bellinger
1 sibling, 1 reply; 104+ messages in thread
From: Chris Boot @ 2012-04-17 10:48 UTC (permalink / raw)
To: Nicholas A. Bellinger
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens,
stefanr, Christoph Hellwig
On 14/04/2012 02:23, Nicholas A. Bellinger wrote:
>>> This looks to me like a reasonable candidate for a v3.5 mainline merge,
>>> so we need to go ahead and get this into target-pending.. Also, thanks
>>> for getting Stefan's ACK's here on the drivers/firmware/ specific
>>> changes..
>>>
>>> So for new fabric drivers (as discused with Sebastian and usb-gadget
>>> recently) that the target team has been enforcing that new fabric
>>> modules convert to use a single source / header file for readability and
>>> maintainability purposes.
>>>
>>> It would be great if you can publish one more branch (no need to re-post
>>> the full diff) that takes your patch-v3 down to a single .c/.h file.
>>> Otherwise, I'm fine to take the 15 minutes to do this conversion ahead
>>> of dropping into target-pending for the sunday night linux-next build.
>> I'll get onto it over the weekend, I hope. I assume in this case I just
>> stick it all in drivers/target/ instead of having a sbp/ directory
>> underneath that?
>>
> I was eager to see this change, so I decided to go ahead and do the
> mechanical conversion myself in the following branch:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core.git sbp-merge
>
> As mentioned, it converts drivers/target/sbp/ to use a single sbp_target.[c,h]
> source/header file following mainline convention for new target fabric
> drivers. Also make as many functions as possible statically defined and
> add inline prototypes where necessary.
>
> This patch also renames the sbp-target config entry from:
>
> FIREWIRE_SBP_TARGET -> SBP_TARGET
>
> and also renames the final generated module name from:
>
> fireware-sbp-target.ko -> sbp_target.ko
Nick,
I only just noticed this, but it looks like your merge lost my changed
to the MAINTAINERS file. Want me to send a fixup patch?
Cheers,
Chris
--
Chris Boot
bootc@bootc.net
^ permalink raw reply [flat|nested] 104+ messages in thread
* Re: [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target
2012-04-17 10:48 ` [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target Chris Boot
@ 2012-04-18 7:17 ` Nicholas A. Bellinger
0 siblings, 0 replies; 104+ messages in thread
From: Nicholas A. Bellinger @ 2012-04-18 7:17 UTC (permalink / raw)
To: Chris Boot
Cc: linux1394-devel, target-devel, linux-kernel, agrover, clemens,
stefanr, Christoph Hellwig
On Tue, 2012-04-17 at 11:48 +0100, Chris Boot wrote:
> On 14/04/2012 02:23, Nicholas A. Bellinger wrote:
<SNIP>
> > I was eager to see this change, so I decided to go ahead and do the
> > mechanical conversion myself in the following branch:
> >
> > git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core.git sbp-merge
> >
> > As mentioned, it converts drivers/target/sbp/ to use a single sbp_target.[c,h]
> > source/header file following mainline convention for new target fabric
> > drivers. Also make as many functions as possible statically defined and
> > add inline prototypes where necessary.
> >
> > This patch also renames the sbp-target config entry from:
> >
> > FIREWIRE_SBP_TARGET -> SBP_TARGET
> >
> > and also renames the final generated module name from:
> >
> > fireware-sbp-target.ko -> sbp_target.ko
>
> Nick,
>
> I only just noticed this, but it looks like your merge lost my changed
> to the MAINTAINERS file. Want me to send a fixup patch?
>
Whoops, sorry about that Chris. I'll be sure to get this fixed up in
the next target-pending update.
Thanks!
--nab
^ permalink raw reply [flat|nested] 104+ messages in thread