2012-02-11 19:45:04

by Chris Boot

[permalink] [raw]
Subject: [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target

Hi folks,

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.

This is not production ready code, and not ready for merging IMO. I know my
locking (or more the lack of it) leaves something to be desired, and I know my
use of atomics and memory barriers is wrong too. Performance is also a bit
meagre and I'm sure it can be improved substantially - I already have some
ideas for things that can be improved.

For testing, I use:

GUID=5254000b8f01e6f6
DIR_ID=abcdef

modprobe firewire-sbp-target
mkdir /sys/kernel/config/target/sbp
mkdir /sys/kernel/config/target/sbp/$GUID
mkdir /sys/kernel/config/target/sbp/$GUID/tpgt_1
mkdir /sys/kernel/config/target/sbp/$GUID/tpgt_1/lun/lun_0
ln -s /sys/kernel/config/target/core/iblock_0/sbptest /sys/kernel/config/target/sbp/$GUID/tpgt_1/lun/lun_0/sbptest
echo $DIR_ID > /sys/kernel/config/target/sbp/$GUID/tpgt_1/directory_id
echo 1 > /sys/kernel/config/target/sbp/$GUID/tpgt_1/enable

Setting the directory_id is optional and something I have only just added, but
it appears to work as planned.

Any and all comments much appreciated.

Thanks,
Chris

--
Chris Boot
[email protected]


2012-02-11 19:44:23

by Chris Boot

[permalink] [raw]
Subject: [PATCH 02/13] firewire: Add EXPORT_SYMBOL_GPL(fw_card_release)

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 <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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

2012-02-11 19:45:07

by Chris Boot

[permalink] [raw]
Subject: [PATCH 11/13] firewire-sbp-target: Add sbp_scsi_cmnd.{c,h}

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-11 19:45:22

by Chris Boot

[permalink] [raw]
Subject: [PATCH 01/13] firewire: Add function to get speed from opaque struct fw_request

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 <[email protected]>
Acked-by: Stefan Richter <[email protected]>
Cc: Clemens Ladisch <[email protected]>
---
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

2012-02-11 19:45:20

by Chris Boot

[permalink] [raw]
Subject: [PATCH 08/13] firewire-sbp-target: add sbp_management_agent.{c,h}

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-11 19:45:17

by Chris Boot

[permalink] [raw]
Subject: [PATCH 07/13] firewire-sbp-target: Add sbp_proto.{c,h}

These are functions to generate TransportID identifiers as per SPC-4
revision 17.

Signed-off-by: Chris Boot <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-11 19:45:16

by Chris Boot

[permalink] [raw]
Subject: [PATCH 04/13] firewire-sbp-target: Add sbp_base.h header

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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

2012-02-11 19:45:14

by Chris Boot

[permalink] [raw]
Subject: [PATCH 05/13] firewire-sbp-target: Add sbp_configfs.c

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-11 19:45:12

by Chris Boot

[permalink] [raw]
Subject: [PATCH 13/13] firewire-sbp-target: Add to target Kconfig and Makefile

This commit also adds an entry to the MAINTAINERS file.

Signed-off-by: Chris Boot <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+L: [email protected]
+L: [email protected]
+L: [email protected]
+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 <[email protected]>
L: [email protected]
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

2012-02-11 19:45:10

by Chris Boot

[permalink] [raw]
Subject: [PATCH 12/13] firewire-sbp-target: Add sbp_util.{c,h}

Common helper functions and global declarations.

Signed-off-by: Chris Boot <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-11 19:45:08

by Chris Boot

[permalink] [raw]
Subject: [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h}

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-11 19:45:02

by Chris Boot

[permalink] [raw]
Subject: [PATCH 09/13] firewire-sbp-target: Add sbp_login.{c,h}

This file contains the implementation of the login, reconnect and logout
management ORBs in SBP-2.

Signed-off-by: Chris Boot <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-11 19:44:59

by Chris Boot

[permalink] [raw]
Subject: [PATCH 10/13] firewire-sbp-target: Add sbp_target_agent.{c,h}

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-11 19:44:57

by Chris Boot

[permalink] [raw]
Subject: [PATCH 03/13] firewire-sbp-target: Add Kconfig, Makefile and TODO

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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

2012-02-12 14:13:23

by Stefan Richter

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target

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/

2012-02-12 15:13:29

by Chris Boot

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target

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
[email protected]

2012-02-12 16:16:47

by Stefan Richter

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target

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/

2012-02-13 12:50:15

by Nicholas A. Bellinger

[permalink] [raw]
Subject: Re: [PATCH 03/13] firewire-sbp-target: Add Kconfig, Makefile and TODO

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 <[email protected]>
> Cc: Andy Grover <[email protected]>
> Cc: Clemens Ladisch <[email protected]>
> Cc: Nicholas A. Bellinger <[email protected]>
> Cc: Stefan Richter <[email protected]>
> ---
> 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

2012-02-13 13:06:05

by Nicholas A. Bellinger

[permalink] [raw]
Subject: Re: [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h}

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 <[email protected]>
> Cc: Andy Grover <[email protected]>
> Cc: Clemens Ladisch <[email protected]>
> Cc: Nicholas A. Bellinger <[email protected]>
> Cc: Stefan Richter <[email protected]>
> ---
> 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 <[email protected]>
> + *
> + * 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

2012-02-13 19:54:14

by Stefan Richter

[permalink] [raw]
Subject: Re: [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h}

On Feb 13 Chris Boot wrote:
> I'd like to support ABORT_TASK and friends at some point. The problem
> I see here though is that tags in SBP-2/3 are 48-bits wide (the ORB
> address in the initiator node's memory), and the target framework seems
> to use a u32 for this. SAM-3 Section 4.11 seems to say "A task tag is a
> value that is composed of up to 64 bits" so it might be worth getting
> that changed in the target framework?

In practice, its is quite likely that only 32 bit wide SBP tags are being
used. But there is no way to guarantee this.
--
Stefan Richter
-=====-===-- --=- -==-=
http://arcgraph.de/sr/

2012-02-13 22:42:03

by Nicholas A. Bellinger

[permalink] [raw]
Subject: Re: [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h}

On Mon, 2012-02-13 at 14:01 +0000, Chris Boot wrote:
> On 13 Feb 2012, at 13:06, Nicholas A. Bellinger wrote:
>
> > 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 <[email protected]>
> >> Cc: Andy Grover <[email protected]>
> >> Cc: Clemens Ladisch <[email protected]>
> >> Cc: Nicholas A. Bellinger <[email protected]>
> >> Cc: Stefan Richter <[email protected]>
> >> ---
> >> 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
>
> <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.
>
> So I can just set TFO->new_cmd_map to NULL?
>

Just drop the sbp_ops->new_cmd_map assignment, no need to explictly set
it to NULL..

> >> +
> >> +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.
>
> I'd like to support ABORT_TASK and friends at some point. The problem
> I see here though is that tags in SBP-2/3 are 48-bits wide (the ORB
> address in the initiator node's memory), and the target framework
> seems to use a u32 for this. SAM-3 Section 4.11 seems to say "A task
> tag is a value that is composed of up to 64 bits" so it might be worth
> getting that changed in the target framework?

Bumping TFO->get_task_tag and se_tmr_req->ref_task_tag to u64 is easy
enough, but it would make sense to figure out if this is really required
in practice first..

--nab

2012-02-15 14:50:52

by Chris Boot

[permalink] [raw]
Subject: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request

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 <[email protected]>
Acked-by: Stefan Richter <[email protected]>
Cc: Clemens Ladisch <[email protected]>
---
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

2012-02-15 14:50:59

by Chris Boot

[permalink] [raw]
Subject: [PATCH v2 10/11] firewire-sbp-target: Add sbp_scsi_cmnd.{c,h}

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-15 14:51:03

by Chris Boot

[permalink] [raw]
Subject: [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h}

This file contains the implementation of the login, reconnect and logout
management ORBs in SBP-2.

Signed-off-by: Chris Boot <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-15 14:51:27

by Chris Boot

[permalink] [raw]
Subject: [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-15 14:51:46

by Chris Boot

[permalink] [raw]
Subject: [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h}

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-15 14:54:03

by Chris Boot

[permalink] [raw]
Subject: [PATCH v2 11/11] firewire-sbp-target: Add to target Kconfig and Makefile

This commit also adds an entry to the MAINTAINERS file.

Signed-off-by: Chris Boot <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+L: [email protected]
+L: [email protected]
+L: [email protected]
+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 <[email protected]>
L: [email protected]
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

2012-02-15 14:54:09

by Chris Boot

[permalink] [raw]
Subject: [PATCH v2 06/11] firewire-sbp-target: Add sbp_fabric.{c,h}

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-15 14:54:06

by Chris Boot

[permalink] [raw]
Subject: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2012-02-15 14:50:49

by Chris Boot

[permalink] [raw]
Subject: [PATCH v2 03/11] firewire-sbp-target: Add Kconfig, Makefile and TODO

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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

2012-02-15 14:55:46

by Chris Boot

[permalink] [raw]
Subject: [PATCH v2 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target

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
[email protected].

2012-02-15 14:56:07

by Chris Boot

[permalink] [raw]
Subject: [PATCH v2 04/11] firewire-sbp-target: Add sbp_base.h header

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 <[email protected]>
Cc: Andy Grover <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Nicholas A. Bellinger <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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

2012-02-15 14:56:06

by Chris Boot

[permalink] [raw]
Subject: [PATCH v2 02/11] firewire: Move fw_card kref functions into linux/firewire.h

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 <[email protected]>
Cc: Clemens Ladisch <[email protected]>
Cc: Stefan Richter <[email protected]>
---
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

2012-02-15 19:10:06

by Stefan Richter

[permalink] [raw]
Subject: Re: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request

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/

2012-02-15 19:10:49

by Chris Boot

[permalink] [raw]
Subject: Re: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request

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
[email protected]

2012-02-15 19:10:46

by Stefan Richter

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] firewire: Move fw_card kref functions into linux/firewire.h

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 <[email protected]>
> Cc: Clemens Ladisch <[email protected]>

Acked-by: Stefan Richter <[email protected]>

> ---
> 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/

2012-02-15 19:15:47

by Stefan Richter

[permalink] [raw]
Subject: Re: [PATCH v2 04/11] firewire-sbp-target: Add sbp_base.h header

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/

2012-02-15 19:22:14

by Stefan Richter

[permalink] [raw]
Subject: Re: [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c

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 <[email protected]>
> + *
> + * 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/

2012-02-15 19:49:25

by Stefan Richter

[permalink] [raw]
Subject: Re: [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h}

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/

2012-02-15 21:01:36

by Stefan Richter

[permalink] [raw]
Subject: Re: [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h}

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 <[email protected]>
> Cc: Andy Grover <[email protected]>
> Cc: Clemens Ladisch <[email protected]>
> Cc: Nicholas A. Bellinger <[email protected]>
> Cc: Stefan Richter <[email protected]>
> ---
> 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 <[email protected]>
> + *
> + * 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/

2012-02-15 21:28:18

by Stefan Richter

[permalink] [raw]
Subject: Re: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}

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/

2012-02-15 22:02:29

by Stefan Richter

[permalink] [raw]
Subject: Re: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request

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/

2012-02-16 09:13:00

by Chris Boot

[permalink] [raw]
Subject: Re: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request

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
[email protected]

2012-02-16 09:18:29

by Chris Boot

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] firewire: Move fw_card kref functions into linux/firewire.h

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<[email protected]>
>> Cc: Clemens Ladisch<[email protected]>
>
> Acked-by: Stefan Richter<[email protected]>

Thanks!

[snip]

Cheers,
Chris

--
Chris Boot
[email protected]

2012-02-16 09:55:54

by Chris Boot

[permalink] [raw]
Subject: Re: [PATCH v2 04/11] firewire-sbp-target: Add sbp_base.h header

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
[email protected]

2012-02-16 09:57:35

by Chris Boot

[permalink] [raw]
Subject: Re: [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c

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<[email protected]>
>> + *
>> + * 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
[email protected]

2012-02-16 10:28:45

by Chris Boot

[permalink] [raw]
Subject: Re: [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h}

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
[email protected]

2012-02-16 11:21:13

by Chris Boot

[permalink] [raw]
Subject: Re: [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h}

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<[email protected]>
>> Cc: Andy Grover<[email protected]>
>> Cc: Clemens Ladisch<[email protected]>
>> Cc: Nicholas A. Bellinger<[email protected]>
>> Cc: Stefan Richter<[email protected]>
>> ---
>> 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<[email protected]>
>> + *
>> + * 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
[email protected]

2012-02-16 11:25:12

by Chris Boot

[permalink] [raw]
Subject: Re: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}

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
[email protected]

2012-02-16 13:48:44

by Stefan Richter

[permalink] [raw]
Subject: Re: [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c

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/

2012-02-16 14:13:03

by Stefan Richter

[permalink] [raw]
Subject: Re: [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h}

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/

2012-02-18 14:59:37

by Stefan Richter

[permalink] [raw]
Subject: Re: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}

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/

2012-02-18 15:06:07

by Chris Boot

[permalink] [raw]
Subject: Re: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}

On 18 Feb 2012, at 14:59, Stefan Richter <[email protected]> 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
[email protected]