Subject: [PATCH 5/6] scsi tgt: scsi target netlink interface

From: Mike Christie <[email-masked]>
Date: Thu, 16 Feb 2006 13:53:44 -0600

This patch implments a netlink interface for the scsi tgt framework.
I was not sure if code using the netlink interface had to get reviewed
by the netdev guys. I am ccing them on this patch and providing
a basic review of why/how we want to use netlink.

I did not think the netdev people wanted to see the scsi and
block layer code, so I am just sending the netlink interface part
of this patchset to netdev. I can resend the other parts if
needed.

The scsi tgt framework, adds support for scsi target mode
cards. So instead of using the scsi card in your box as a initiator/host
you can use it as a target/server.

The reason for the netlink use is becuase the target normally
receives a interrupt indicating that a command or event is
ready to be processed. The scsi card's driver will then call
a scsi lib function which eventually calls scsi_tgt_uspace_send (in
this patch below) to tell userspace to begin to process the request
(userspace contains the state model). Later userspace will call back
into the kernel by sending a netlink msg, and instruct the scsi driver
what to do next. When the scsi driver is done executing the
operation, it will send a netlink message back to userspace
to indicate the success or failure of the operation (using 
scsi_tgt_uspace_send_status in the patch below).

Signed-off-by: Mike Christie <[email-masked]>
Signed-off-by: FUJITA Tomonori <[email-masked]>

diff --git a/drivers/scsi/scsi_tgt_if.c b/drivers/scsi/scsi_tgt_if.c
new file mode 100644
index 0000000..38b35da
--- /dev/null
+++ b/drivers/scsi/scsi_tgt_if.c
@@ -0,0 +1,214 @@
+/*
+ * SCSI target kernel/user interface functions
+ *
+ * Copyright (C) 2005 FUJITA Tomonori <[email-masked]>
+ * Copyright (C) 2005 Mike Christie <[email-masked]>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/blkdev.h>
+#include <linux/file.h>
+#include <linux/netlink.h>
+#include <net/tcp.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tgt.h>
+#include <scsi/scsi_tgt_if.h>
+
+#include "scsi_tgt_priv.h"
+
+static int tgtd_pid;
+static struct sock *nl_sk;
+
+static int send_event_res(uint16_t type, struct tgt_event *p,
+			  void *data, int dlen, gfp_t flags, pid_t pid)
+{
+	struct tgt_event *ev;
+	struct nlmsghdr *nlh;
+	struct sk_buff *skb;
+	uint32_t len;
+
+	len = NLMSG_SPACE(sizeof(*ev) + dlen);
+	skb = alloc_skb(len, flags);
+	if (!skb)
+		return -ENOMEM;
+
+	nlh = __nlmsg_put(skb, pid, 0, type, len - sizeof(*nlh), 0);
+
+	ev = NLMSG_DATA(nlh);
+	memcpy(ev, p, sizeof(*ev));
+	if (dlen)
+		memcpy(ev->data, data, dlen);
+
+	return netlink_unicast(nl_sk, skb, pid, 0);
+}
+
+int scsi_tgt_uspace_send(struct scsi_cmnd *cmd, struct scsi_lun *lun, gfp_t gfp_mask)
+{
+	struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd);
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	struct tgt_event *ev;
+	struct tgt_cmd *tcmd;
+	int err, len;
+
+	len = NLMSG_SPACE(sizeof(*ev) + sizeof(struct tgt_cmd));
+	/*
+	 * TODO: add MAX_COMMAND_SIZE to ev and add mempool
+	 */
+	skb = alloc_skb(NLMSG_SPACE(len), gfp_mask);
+	if (!skb)
+		return -ENOMEM;
+
+	nlh = __nlmsg_put(skb, tgtd_pid, 0, TGT_KEVENT_CMD_REQ,
+			  len - sizeof(*nlh), 0);
+
+	ev = NLMSG_DATA(nlh);
+	ev->k.cmd_req.host_no = shost->host_no;
+	ev->k.cmd_req.cid = cmd->request->tag;
+	ev->k.cmd_req.data_len = cmd->request_bufflen;
+
+	dprintk("%d %u %u\n", ev->k.cmd_req.host_no, ev->k.cmd_req.cid,
+		ev->k.cmd_req.data_len);
+
+	/* FIXME: we need scsi core to do that. */
+	memcpy(cmd->cmnd, cmd->data_cmnd, MAX_COMMAND_SIZE);
+
+	tcmd = (struct tgt_cmd *) ev->data;
+	memcpy(tcmd->scb, cmd->cmnd, sizeof(tcmd->scb));
+	memcpy(tcmd->lun, lun, sizeof(struct scsi_lun));
+
+	err = netlink_unicast(nl_sk, skb, tgtd_pid, 0);
+	if (err < 0)
+		printk(KERN_ERR "scsi_tgt_uspace_send: could not send skb %d\n",
+		       err);
+	return err;
+}
+
+int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, gfp_t gfp_mask)
+{
+	struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd);
+	struct tgt_event ev;
+	char dummy[sizeof(struct tgt_cmd)];
+
+	memset(&ev, 0, sizeof(ev));
+	ev.k.cmd_done.host_no = shost->host_no;
+	ev.k.cmd_done.cid = cmd->request->tag;
+	ev.k.cmd_done.result = cmd->result;
+
+	return send_event_res(TGT_KEVENT_CMD_DONE, &ev, dummy, sizeof(dummy),
+			      gfp_mask, tgtd_pid);
+}
+
+static int event_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+	struct tgt_event *ev = NLMSG_DATA(nlh);
+	int err = 0;
+
+	dprintk("%d %d %d\n", nlh->nlmsg_type,
+		nlh->nlmsg_pid, current->pid);
+
+	switch (nlh->nlmsg_type) {
+	case TGT_UEVENT_TGTD_BIND:
+		tgtd_pid = NETLINK_CREDS(skb)->pid;
+		break;
+	case TGT_UEVENT_CMD_RES:
+		/* TODO: handle multiple cmds in one event */
+		err = scsi_tgt_kspace_exec(ev->u.cmd_res.host_no,
+					   ev->u.cmd_res.cid,
+					   ev->u.cmd_res.result,
+					   ev->u.cmd_res.len,
+					   ev->u.cmd_res.offset,
+					   ev->u.cmd_res.uaddr,
+					   ev->u.cmd_res.rw,
+					   ev->u.cmd_res.try_map);
+		break;
+	default:
+		eprintk("unknown type %d\n", nlh->nlmsg_type);
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+static int event_recv_skb(struct sk_buff *skb)
+{
+	int err;
+	uint32_t rlen;
+	struct nlmsghdr	*nlh;
+
+	while (skb->len >= NLMSG_SPACE(0)) {
+		nlh = (struct nlmsghdr *) skb->data;
+		if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
+			return 0;
+		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+		if (rlen > skb->len)
+			rlen = skb->len;
+		err = event_recv_msg(skb, nlh);
+
+		dprintk("%d %d\n", nlh->nlmsg_type, err);
+		/*
+		 * TODO for passthru commands the lower level should
+		 * probably handle the result or we should modify this
+		 */
+		if (nlh->nlmsg_type != TGT_UEVENT_CMD_RES) {
+			struct tgt_event ev;
+
+			memset(&ev, 0, sizeof(ev));
+			ev.k.event_res.err = err;
+			send_event_res(TGT_KEVENT_RESPONSE, &ev, NULL, 0,
+				       GFP_KERNEL | __GFP_NOFAIL,
+					nlh->nlmsg_pid);
+		}
+		skb_pull(skb, rlen);
+	}
+	return 0;
+}
+
+static void event_recv(struct sock *sk, int length)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
+		if (NETLINK_CREDS(skb)->uid) {
+			skb_pull(skb, skb->len);
+			kfree_skb(skb);
+			continue;
+		}
+
+		if (event_recv_skb(skb) && skb->len)
+			skb_queue_head(&sk->sk_receive_queue, skb);
+		else
+			kfree_skb(skb);
+	}
+}
+
+void __exit scsi_tgt_if_exit(void)
+{
+	sock_release(nl_sk->sk_socket);
+}
+
+int __init scsi_tgt_if_init(void)
+{
+	nl_sk = netlink_kernel_create(NETLINK_TGT, 1, event_recv,
+				    THIS_MODULE);
+	if (!nl_sk)
+		return -ENOMEM;
+
+	return 0;
+}
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 6a2ccf7..580fb42 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -21,6 +21,7 @@
 #define NETLINK_DNRTMSG		14	/* DECnet routing messages */
 #define NETLINK_KOBJECT_UEVENT	15	/* Kernel messages to userspace */
 #define NETLINK_GENERIC		16
+#define NETLINK_TGT		17	/* SCSI target */
 
 #define MAX_LINKS 32		
 
diff --git a/include/scsi/scsi_tgt_if.h b/include/scsi/scsi_tgt_if.h
new file mode 100644
index 0000000..da3a808
--- /dev/null
+++ b/include/scsi/scsi_tgt_if.h
@@ -0,0 +1,88 @@
+/*
+ * SCSI target kernel/user interface
+ *
+ * Copyright (C) 2005 FUJITA Tomonori <[email-masked]>
+ * Copyright (C) 2005 Mike Christie <[email-masked]>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#ifndef __SCSI_TARGET_IF_H
+#define __SCSI_TARGET_IF_H
+
+enum tgt_event_type {
+	/* user -> kernel */
+	TGT_UEVENT_TGTD_BIND,
+	TGT_UEVENT_TARGET_SETUP,
+	TGT_UEVENT_CMD_RES,
+
+	/* kernel -> user */
+	TGT_KEVENT_RESPONSE,
+	TGT_KEVENT_CMD_REQ,
+	TGT_KEVENT_CMD_DONE,
+};
+
+struct tgt_event {
+	/* user-> kernel */
+	union {
+		struct {
+			int pk_fd;
+		} tgtd_bind;
+		struct {
+			int host_no;
+			uint32_t cid;
+			uint32_t len;
+			int result;
+			uint64_t uaddr;
+			uint64_t offset;
+			uint8_t rw;
+			uint8_t try_map;
+		} cmd_res;
+	} u;
+
+	/* kernel -> user */
+	union {
+		struct {
+			int err;
+		} event_res;
+		struct {
+			int host_no;
+			uint32_t cid;
+			uint32_t data_len;
+			uint64_t dev_id;
+		} cmd_req;
+		struct {
+			int host_no;
+			uint32_t cid;
+			int result;
+		} cmd_done;
+	} k;
+
+	/*
+	 * I think a pointer is a unsigned long but this struct
+	 * gets passed around from the kernel to userspace and
+	 * back again so to handle some ppc64 setups where userspace is
+	 * 32 bits but the kernel is 64 we do this odd thing
+	 */
+	uint64_t data[0];
+} __attribute__ ((aligned (sizeof(uint64_t))));
+
+struct tgt_cmd {
+	uint8_t scb[16];
+	uint8_t lun[8];
+	int tags;
+} __attribute__ ((aligned (sizeof(uint64_t))));
+
+#endif