lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1328989452-20921-11-git-send-email-bootc@bootc.net>
Date:	Sat, 11 Feb 2012 19:44:09 +0000
From:	Chris Boot <bootc@...tc.net>
To:	linux1394-devel@...ts.sourceforge.net, target-devel@...r.kernel.org
Cc:	linux-kernel@...r.kernel.org, agrover@...hat.com,
	clemens@...isch.de, nab@...ux-iscsi.org, stefanr@...6.in-berlin.de,
	Chris Boot <bootc@...tc.net>
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 <bootc@...tc.net>
Cc: Andy Grover <agrover@...hat.com>
Cc: Clemens Ladisch <clemens@...isch.de>
Cc: Nicholas A. Bellinger <nab@...ux-iscsi.org>
Cc: Stefan Richter <stefanr@...6.in-berlin.de>
---
 drivers/target/sbp/sbp_target_agent.c |  360 +++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_target_agent.h |   30 +++
 2 files changed, 390 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_target_agent.c
 create mode 100644 drivers/target/sbp/sbp_target_agent.h

diff --git a/drivers/target/sbp/sbp_target_agent.c b/drivers/target/sbp/sbp_target_agent.c
new file mode 100644
index 0000000..4ab2000
--- /dev/null
+++ b/drivers/target/sbp/sbp_target_agent.c
@@ -0,0 +1,360 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <bootc@...tc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+#include "sbp_util.h"
+
+static int tgt_agent_rw_agent_state(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	if (tcode == TCODE_READ_QUADLET_REQUEST) {
+		__be32 state;
+
+		pr_debug("tgt_agent AGENT_STATE READ\n");
+
+		state = cpu_to_be32(atomic_read(&agent->state));
+		memcpy(data, &state, sizeof(state));
+
+		return RCODE_COMPLETE;
+	} else if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
+		/* ignored */
+		return RCODE_COMPLETE;
+	} else {
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_agent_reset(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
+		pr_debug("tgt_agent AGENT_RESET\n");
+		atomic_set(&agent->state, AGENT_STATE_RESET);
+		return RCODE_COMPLETE;
+	} else {
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_orb_pointer(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	struct sbp2_pointer *ptr = data;
+
+	if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+		int ret;
+
+		smp_wmb();
+		atomic_cmpxchg(&agent->state,
+				AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
+		smp_wmb();
+		if (atomic_cmpxchg(&agent->state,
+					AGENT_STATE_SUSPENDED,
+					AGENT_STATE_ACTIVE)
+				!= AGENT_STATE_SUSPENDED)
+			return RCODE_CONFLICT_ERROR;
+		smp_wmb();
+
+		agent->orb_pointer = sbp2_pointer_to_addr(ptr);
+
+		pr_debug("tgt_agent ORB_POINTER write: %llu\n",
+			agent->orb_pointer);
+
+		ret = queue_work(fw_workqueue, &agent->work);
+		if (!ret)
+			return RCODE_CONFLICT_ERROR;
+
+		return RCODE_COMPLETE;
+	} else if (tcode == TCODE_READ_BLOCK_REQUEST) {
+		pr_debug("tgt_agent ORB_POINTER READ\n");
+		addr_to_sbp2_pointer(agent->orb_pointer, ptr);
+		return RCODE_COMPLETE;
+	} else {
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_doorbell(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
+		int ret;
+
+		smp_wmb();
+		if (atomic_cmpxchg(&agent->state,
+					AGENT_STATE_SUSPENDED,
+					AGENT_STATE_ACTIVE)
+				!= AGENT_STATE_SUSPENDED)
+			return RCODE_CONFLICT_ERROR;
+		smp_wmb();
+
+		pr_debug("tgt_agent DOORBELL\n");
+
+		ret = queue_work(fw_workqueue, &agent->work);
+		if (!ret)
+			return RCODE_CONFLICT_ERROR;
+
+		return RCODE_COMPLETE;
+	} else if (tcode == TCODE_READ_QUADLET_REQUEST) {
+		return RCODE_COMPLETE;
+	} else {
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_unsolicited_status_enable(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
+		pr_debug("tgt_agent UNSOLICITED_STATUS_ENABLE\n");
+		atomic_set(&agent->login->unsolicited_status_enable, 1);
+		return RCODE_COMPLETE;
+	} else if (tcode == TCODE_READ_QUADLET_REQUEST) {
+		return RCODE_COMPLETE;
+	} else {
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static void tgt_agent_rw(struct fw_card *card,
+	struct fw_request *request, int tcode, int destination, int source,
+	int generation, unsigned long long offset, void *data, size_t length,
+	void *callback_data)
+{
+	struct sbp_target_agent *agent = callback_data;
+	int rcode = RCODE_ADDRESS_ERROR;
+
+	/* turn offset into the offset from the start of the block */
+	offset -= agent->handler.offset;
+
+	/* check the source matches the login */
+	if (source != agent->login->sess->node_id) {
+		pr_notice("ignoring request from foreign node (%x != %x)\n",
+			source, agent->login->sess->node_id);
+		fw_send_response(card, request, RCODE_TYPE_ERROR);
+		return;
+	}
+
+	if (offset == 0x00 && length == 4) {
+		/* AGENT_STATE */
+		rcode = tgt_agent_rw_agent_state(card, tcode,
+			generation, data, agent);
+	} else if (offset == 0x04 && length == 4) {
+		/* AGENT_RESET */
+		rcode = tgt_agent_rw_agent_reset(card, tcode,
+			generation, data, agent);
+	} else if (offset == 0x08 && length == 8) {
+		/* ORB_POINTER */
+		rcode = tgt_agent_rw_orb_pointer(card, tcode,
+			generation, data, agent);
+	} else if (offset == 0x10 && length == 4) {
+		/* DOORBELL */
+		rcode = tgt_agent_rw_doorbell(card, tcode,
+			generation, data, agent);
+	} else if (offset == 0x14 && length == 4) {
+		/* UNSOLICITED_STATUS_ENABLE */
+		rcode = tgt_agent_rw_unsolicited_status_enable(card, tcode,
+			generation, data, agent);
+	}
+
+	fw_send_response(card, request, rcode);
+}
+
+static void tgt_agent_process_work(struct work_struct *work)
+{
+	struct sbp_target_request *req =
+		container_of(work, struct sbp_target_request, work);
+
+	switch (ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc))) {
+	case 0:/* Format specified by this standard */
+		sbp_handle_command(req);
+		return;
+	case 1: /* Reserved for future standardization */
+	case 2: /* Vendor-dependent */
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	case 3: /* Dummy ORB */
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_DUMMY_ORB_COMPLETE));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	default:
+		BUG();
+	}
+}
+
+static void tgt_agent_fetch_work(struct work_struct *work)
+{
+	struct sbp_target_agent *agent =
+		container_of(work, struct sbp_target_agent, work);
+	struct sbp_session *sess = agent->login->sess;
+	struct sbp_target_request *req;
+	int ret;
+
+	smp_rmb();
+	if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE)
+		return;
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		goto out;
+
+	smp_rmb();
+	if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE) {
+		sbp_free_request(req);
+		return;
+	}
+
+	/* read in the ORB */
+	ret = fw_run_transaction(sess->card, TCODE_READ_BLOCK_REQUEST,
+		sess->node_id, sess->generation, sess->speed,
+		agent->orb_pointer, &req->orb, sizeof(req->orb));
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("tgt_orb fetch failed: %x\n", ret);
+		sbp_free_request(req);
+		goto out;
+	}
+
+	smp_rmb();
+	if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE) {
+		sbp_free_request(req);
+		return;
+	}
+
+	req->agent = agent;
+	req->orb_pointer = agent->orb_pointer;
+
+	pr_debug("tgt_orb ptr:0x%llx next_orb:0x%llx data_descriptor:0x%llx misc:0x%x\n",
+			req->orb_pointer,
+			sbp2_pointer_to_addr(&req->orb.next_orb),
+			sbp2_pointer_to_addr(&req->orb.data_descriptor),
+			be32_to_cpu(req->orb.misc));
+
+	if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+		req->status.status = cpu_to_be32(
+				STATUS_BLOCK_SRC(STATUS_SRC_ORB_FINISHED));
+	} else {
+		req->status.status = cpu_to_be32(
+				STATUS_BLOCK_SRC(STATUS_SRC_ORB_CONTINUING));
+	}
+
+	req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_pointer >> 32));
+	req->status.orb_low = cpu_to_be32(agent->orb_pointer & 0xfffffffc);
+	INIT_WORK(&req->work, tgt_agent_process_work);
+
+	ret = queue_work(fw_workqueue, &req->work);
+	if (!ret) {
+		pr_err("tgt_orb queue_work failed\n");
+		sbp_free_request(req);
+		goto out;
+	}
+
+	/* check if we should carry on processing */
+	if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+		/* null next_orb */
+		goto out;
+	}
+
+	smp_rmb();
+	if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE)
+		return;
+
+	agent->orb_pointer = sbp2_pointer_to_addr(&req->orb.next_orb);
+
+	if (!queue_work(fw_workqueue, &agent->work)) {
+		pr_err("tgt_orb fetch queue_work failed\n");
+		goto out;
+	}
+
+	return;
+
+out:
+	/* finished */
+	atomic_cmpxchg(&agent->state,
+		AGENT_STATE_ACTIVE, AGENT_STATE_SUSPENDED);
+}
+
+struct sbp_target_agent *sbp_target_agent_register(
+		struct sbp_login_descriptor *login)
+{
+	struct sbp_target_agent *agent;
+	int ret;
+
+	agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+	if (!agent)
+		return ERR_PTR(-ENOMEM);
+
+	agent->handler.length = 0x20;
+	agent->handler.address_callback = tgt_agent_rw;
+	agent->handler.callback_data = agent;
+
+	agent->login = login;
+	atomic_set(&agent->state, AGENT_STATE_RESET);
+	INIT_WORK(&agent->work, tgt_agent_fetch_work);
+	agent->orb_pointer = (u64)-1;
+
+	ret = fw_core_add_address_handler(&agent->handler,
+		&sbp_register_region);
+	if (ret < 0) {
+		kfree(agent);
+		return ERR_PTR(ret);
+	}
+
+	return agent;
+}
+
+void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+	if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
+		flush_work_sync(&agent->work);
+
+	fw_core_remove_address_handler(&agent->handler);
+	kfree(agent);
+}
+
diff --git a/drivers/target/sbp/sbp_target_agent.h b/drivers/target/sbp/sbp_target_agent.h
new file mode 100644
index 0000000..870b901
--- /dev/null
+++ b/drivers/target/sbp/sbp_target_agent.h
@@ -0,0 +1,30 @@
+
+struct sbp_target_agent {
+	struct fw_address_handler handler;
+	struct sbp_login_descriptor *login;
+	atomic_t state;
+	struct work_struct work;
+	u64 orb_pointer;
+};
+
+struct sbp_target_request {
+	struct sbp_target_agent *agent;
+	u64 orb_pointer;
+	struct sbp_command_block_orb orb;
+	struct sbp_status_block status;
+	struct work_struct work;
+
+	struct se_cmd se_cmd;
+	struct sbp_page_table_entry *pg_tbl;
+	void *cmd_buf;
+	int unpacked_lun;
+	u32 data_len;
+	enum dma_data_direction	data_dir;
+	void *data_buf;
+
+	unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+struct sbp_target_agent *sbp_target_agent_register(
+		struct sbp_login_descriptor *login);
+void sbp_target_agent_unregister(struct sbp_target_agent *agent);
-- 
1.7.9

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ