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 <michaelc@xxxxxxxxxxx> Signed-off-by: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> 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 <tomof@xxxxxxx> + * Copyright (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx> + * + * 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 <tomof@xxxxxxx> + * Copyright (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx> + * + * 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 - : send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html