Skip to contentSkip to conversation

scsi tgt: scsi target netlink interface

PATCH In review Mike Christie 1 participant 1 message Feb 16, 2006

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 5/6 scsi tgt: scsi target netlink interface

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).

drivers/scsi/scsi_tgt_if.c new file +214 -0
new file mode 100644
index 0000000..38b35da
@@ -0,0 +1,214 @@
+/*
+ * SCSI target kernel/user interface functions
+ *
+ * Copyright (C) 2005 FUJITA Tomonori <[email protected]>
+ * Copyright (C) 2005 Mike Christie <[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., 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;
+}
include/linux/netlink.h +1 -0
index 6a2ccf7..580fb42 100644
@@ -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		
 
include/scsi/scsi_tgt_if.h new file +88 -0

88 lines not on this page · raw message