Skip to content

Commit

Permalink
[SCSI] iscsi class: Add new NETLINK_ISCSI messages for cnic/bnx2i dri…
Browse files Browse the repository at this point in the history
…ver.

Add ISCSI_NETLINK messages for iSCSI NICs to get information such as
path from userspace.  Original iscsid messages are now always sent as
multicast to group 1.  The new messages are sent to group 2.

The multicast changes were made by Mike Christie.

Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: Benjamin Li <benli@broadcom.com>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
  • Loading branch information
Michael Chan authored and James Bottomley committed Jun 9, 2009
1 parent 126c5cc commit 4351477
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 36 deletions.
122 changes: 86 additions & 36 deletions drivers/scsi/scsi_transport_iscsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
#define ISCSI_TRANSPORT_VERSION "2.0-870"

struct iscsi_internal {
int daemon_pid;
struct scsi_transport_template t;
struct iscsi_transport *iscsi_transport;
struct list_head list;
Expand Down Expand Up @@ -938,23 +937,9 @@ iscsi_if_transport_lookup(struct iscsi_transport *tt)
}

static int
iscsi_broadcast_skb(struct sk_buff *skb, gfp_t gfp)
iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp)
{
return netlink_broadcast(nls, skb, 0, 1, gfp);
}

static int
iscsi_unicast_skb(struct sk_buff *skb, int pid)
{
int rc;

rc = netlink_unicast(nls, skb, pid, MSG_DONTWAIT);
if (rc < 0) {
printk(KERN_ERR "iscsi: can not unicast skb (%d)\n", rc);
return rc;
}

return 0;
return nlmsg_multicast(nls, skb, 0, group, gfp);
}

int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
Expand All @@ -980,7 +965,7 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
return -ENOMEM;
}

nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
ev = NLMSG_DATA(nlh);
memset(ev, 0, sizeof(*ev));
ev->transport_handle = iscsi_handle(conn->transport);
Expand All @@ -991,10 +976,45 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
memcpy(pdu, hdr, sizeof(struct iscsi_hdr));
memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size);

return iscsi_unicast_skb(skb, priv->daemon_pid);
return iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
}
EXPORT_SYMBOL_GPL(iscsi_recv_pdu);

int iscsi_offload_mesg(struct Scsi_Host *shost,
struct iscsi_transport *transport, uint32_t type,
char *data, uint16_t data_size)
{
struct nlmsghdr *nlh;
struct sk_buff *skb;
struct iscsi_uevent *ev;
int len = NLMSG_SPACE(sizeof(*ev) + data_size);

skb = alloc_skb(len, GFP_NOIO);
if (!skb) {
printk(KERN_ERR "can not deliver iscsi offload message:OOM\n");
return -ENOMEM;
}

nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
ev = NLMSG_DATA(nlh);
memset(ev, 0, sizeof(*ev));
ev->type = type;
ev->transport_handle = iscsi_handle(transport);
switch (type) {
case ISCSI_KEVENT_PATH_REQ:
ev->r.req_path.host_no = shost->host_no;
break;
case ISCSI_KEVENT_IF_DOWN:
ev->r.notify_if_down.host_no = shost->host_no;
break;
}

memcpy((char *)ev + sizeof(*ev), data, data_size);

return iscsi_multicast_skb(skb, ISCSI_NL_GRP_UIP, GFP_NOIO);
}
EXPORT_SYMBOL_GPL(iscsi_offload_mesg);

void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
{
struct nlmsghdr *nlh;
Expand All @@ -1014,24 +1034,24 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
return;
}

nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
ev = NLMSG_DATA(nlh);
ev->transport_handle = iscsi_handle(conn->transport);
ev->type = ISCSI_KEVENT_CONN_ERROR;
ev->r.connerror.error = error;
ev->r.connerror.cid = conn->cid;
ev->r.connerror.sid = iscsi_conn_get_sid(conn);

iscsi_broadcast_skb(skb, GFP_ATOMIC);
iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);

iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n",
error);
}
EXPORT_SYMBOL_GPL(iscsi_conn_error_event);

static int
iscsi_if_send_reply(int pid, int seq, int type, int done, int multi,
void *payload, int size)
iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
void *payload, int size)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
Expand All @@ -1045,10 +1065,10 @@ iscsi_if_send_reply(int pid, int seq, int type, int done, int multi,
return -ENOMEM;
}

nlh = __nlmsg_put(skb, pid, seq, t, (len - sizeof(*nlh)), 0);
nlh = __nlmsg_put(skb, 0, 0, t, (len - sizeof(*nlh)), 0);
nlh->nlmsg_flags = flags;
memcpy(NLMSG_DATA(nlh), payload, size);
return iscsi_unicast_skb(skb, pid);
return iscsi_multicast_skb(skb, group, GFP_ATOMIC);
}

static int
Expand Down Expand Up @@ -1085,7 +1105,7 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
return -ENOMEM;
}

nlhstat = __nlmsg_put(skbstat, priv->daemon_pid, 0, 0,
nlhstat = __nlmsg_put(skbstat, 0, 0, 0,
(len - sizeof(*nlhstat)), 0);
evstat = NLMSG_DATA(nlhstat);
memset(evstat, 0, sizeof(*evstat));
Expand All @@ -1109,7 +1129,8 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
skb_trim(skbstat, NLMSG_ALIGN(actual_size));
nlhstat->nlmsg_len = actual_size;

err = iscsi_unicast_skb(skbstat, priv->daemon_pid);
err = iscsi_multicast_skb(skbstat, ISCSI_NL_GRP_ISCSID,
GFP_ATOMIC);
} while (err < 0 && err != -ECONNREFUSED);

return err;
Expand Down Expand Up @@ -1143,7 +1164,7 @@ int iscsi_session_event(struct iscsi_cls_session *session,
return -ENOMEM;
}

nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
ev = NLMSG_DATA(nlh);
ev->transport_handle = iscsi_handle(session->transport);

Expand Down Expand Up @@ -1172,7 +1193,7 @@ int iscsi_session_event(struct iscsi_cls_session *session,
* this will occur if the daemon is not up, so we just warn
* the user and when the daemon is restarted it will handle it
*/
rc = iscsi_broadcast_skb(skb, GFP_KERNEL);
rc = iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
if (rc == -ESRCH)
iscsi_cls_session_printk(KERN_ERR, session,
"Cannot notify userspace of session "
Expand Down Expand Up @@ -1393,7 +1414,31 @@ iscsi_set_host_param(struct iscsi_transport *transport,
}

static int
iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev)
{
struct Scsi_Host *shost;
struct iscsi_path *params;
int err;

if (!transport->set_path)
return -ENOSYS;

shost = scsi_host_lookup(ev->u.set_path.host_no);
if (!shost) {
printk(KERN_ERR "set path could not find host no %u\n",
ev->u.set_path.host_no);
return -ENODEV;
}

params = (struct iscsi_path *)((char *)ev + sizeof(*ev));
err = transport->set_path(shost, params);

scsi_host_put(shost);
return err;
}

static int
iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
{
int err = 0;
struct iscsi_uevent *ev = NLMSG_DATA(nlh);
Expand All @@ -1403,6 +1448,11 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
struct iscsi_cls_conn *conn;
struct iscsi_endpoint *ep = NULL;

if (nlh->nlmsg_type == ISCSI_UEVENT_PATH_UPDATE)
*group = ISCSI_NL_GRP_UIP;
else
*group = ISCSI_NL_GRP_ISCSID;

priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle));
if (!priv)
return -EINVAL;
Expand All @@ -1411,8 +1461,6 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
if (!try_module_get(transport->owner))
return -EINVAL;

priv->daemon_pid = NETLINK_CREDS(skb)->pid;

switch (nlh->nlmsg_type) {
case ISCSI_UEVENT_CREATE_SESSION:
err = iscsi_if_create_session(priv, ep, ev,
Expand Down Expand Up @@ -1506,6 +1554,9 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
case ISCSI_UEVENT_SET_HOST_PARAM:
err = iscsi_set_host_param(transport, ev);
break;
case ISCSI_UEVENT_PATH_UPDATE:
err = iscsi_set_path(transport, ev);
break;
default:
err = -ENOSYS;
break;
Expand All @@ -1528,6 +1579,7 @@ iscsi_if_rx(struct sk_buff *skb)
uint32_t rlen;
struct nlmsghdr *nlh;
struct iscsi_uevent *ev;
uint32_t group;

nlh = nlmsg_hdr(skb);
if (nlh->nlmsg_len < sizeof(*nlh) ||
Expand All @@ -1540,7 +1592,7 @@ iscsi_if_rx(struct sk_buff *skb)
if (rlen > skb->len)
rlen = skb->len;

err = iscsi_if_recv_msg(skb, nlh);
err = iscsi_if_recv_msg(skb, nlh, &group);
if (err) {
ev->type = ISCSI_KEVENT_IF_ERROR;
ev->iferror = err;
Expand All @@ -1554,8 +1606,7 @@ iscsi_if_rx(struct sk_buff *skb)
*/
if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
break;
err = iscsi_if_send_reply(
NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq,
err = iscsi_if_send_reply(group, nlh->nlmsg_seq,
nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
} while (err < 0 && err != -ECONNREFUSED);
skb_pull(skb, rlen);
Expand Down Expand Up @@ -1803,7 +1854,6 @@ iscsi_register_transport(struct iscsi_transport *tt)
if (!priv)
return NULL;
INIT_LIST_HEAD(&priv->list);
priv->daemon_pid = -1;
priv->iscsi_transport = tt;
priv->t.user_scan = iscsi_user_scan;
priv->t.create_work_queue = 1;
Expand Down
42 changes: 42 additions & 0 deletions include/scsi/iscsi_if.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
#define ISCSI_IF_H

#include <scsi/iscsi_proto.h>
#include <linux/in.h>
#include <linux/in6.h>

#define ISCSI_NL_GRP_ISCSID 1
#define ISCSI_NL_GRP_UIP 2

#define UEVENT_BASE 10
#define KEVENT_BASE 100
Expand Down Expand Up @@ -53,13 +58,18 @@ enum iscsi_uevent_e {
ISCSI_UEVENT_CREATE_BOUND_SESSION = UEVENT_BASE + 18,
ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST = UEVENT_BASE + 19,

ISCSI_UEVENT_PATH_UPDATE = UEVENT_BASE + 20,

/* up events */
ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1,
ISCSI_KEVENT_CONN_ERROR = KEVENT_BASE + 2,
ISCSI_KEVENT_IF_ERROR = KEVENT_BASE + 3,
ISCSI_KEVENT_DESTROY_SESSION = KEVENT_BASE + 4,
ISCSI_KEVENT_UNBIND_SESSION = KEVENT_BASE + 5,
ISCSI_KEVENT_CREATE_SESSION = KEVENT_BASE + 6,

ISCSI_KEVENT_PATH_REQ = KEVENT_BASE + 7,
ISCSI_KEVENT_IF_DOWN = KEVENT_BASE + 8,
};

enum iscsi_tgt_dscvr {
Expand Down Expand Up @@ -159,6 +169,9 @@ struct iscsi_uevent {
uint32_t param; /* enum iscsi_host_param */
uint32_t len;
} set_host_param;
struct msg_set_path {
uint32_t host_no;
} set_path;
} u;
union {
/* messages k -> u */
Expand Down Expand Up @@ -192,9 +205,38 @@ struct iscsi_uevent {
struct msg_transport_connect_ret {
uint64_t handle;
} ep_connect_ret;
struct msg_req_path {
uint32_t host_no;
} req_path;
struct msg_notify_if_down {
uint32_t host_no;
} notify_if_down;
} r;
} __attribute__ ((aligned (sizeof(uint64_t))));

/*
* To keep the struct iscsi_uevent size the same for userspace code
* compatibility, the main structure for ISCSI_UEVENT_PATH_UPDATE and
* ISCSI_KEVENT_PATH_REQ is defined separately and comes after the
* struct iscsi_uevent in the NETLINK_ISCSI message.
*/
struct iscsi_path {
uint64_t handle;
uint8_t mac_addr[6];
uint8_t mac_addr_old[6];
uint32_t ip_addr_len; /* 4 or 16 */
union {
struct in_addr v4_addr;
struct in6_addr v6_addr;
} src;
union {
struct in_addr v4_addr;
struct in6_addr v6_addr;
} dst;
uint16_t vlan_id;
uint16_t pmtu;
} __attribute__ ((aligned (sizeof(uint64_t))));

/*
* Common error codes
*/
Expand Down
5 changes: 5 additions & 0 deletions include/scsi/scsi_transport_iscsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ struct iscsi_transport {
void (*ep_disconnect) (struct iscsi_endpoint *ep);
int (*tgt_dscvr) (struct Scsi_Host *shost, enum iscsi_tgt_dscvr type,
uint32_t enable, struct sockaddr *dst_addr);
int (*set_path) (struct Scsi_Host *shost, struct iscsi_path *params);
};

/*
Expand All @@ -149,6 +150,10 @@ extern void iscsi_conn_error_event(struct iscsi_cls_conn *conn,
extern int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
char *data, uint32_t data_size);

extern int iscsi_offload_mesg(struct Scsi_Host *shost,
struct iscsi_transport *transport, uint32_t type,
char *data, uint16_t data_size);

struct iscsi_cls_conn {
struct list_head conn_list; /* item in connlist */
void *dd_data; /* LLD private data */
Expand Down

0 comments on commit 4351477

Please sign in to comment.