From: Rakesh Ranjan <rakesh@chelsio.com>
To: Mike Christie <michaelc@cs.wisc.edu>
Cc: "open-iscsi@googlegroups.com" <open-iscsi@googlegroups.com>,
netdev@vger.kernel.org, LKML <linux-kernel@vger.kernel.org>,
David Miller <davem@davemloft.net>,
James Bottomley <James.Bottomley@suse.de>,
Karen Xie <kxie@chelsio.com>, Rakesh Ranjan <rakesh@chelsio.com>
Subject: [RFC-PATCH] libiscsi dhcp handler
Date: Mon, 16 Nov 2009 18:41:49 +0530 [thread overview]
Message-ID: <4B014F95.8000906@chelsio.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 267 bytes --]
Hi mike,
Herein attached patches to support dhcp based provisioning for iSCSI
offload capable cards. I have made dhcp code as generic as possible,
please go through the code. Based on the feedback I will submit final
version of these patches.
Regards
Rakesh Ranjan
[-- Attachment #2: 0001-Required-changes-for-libiscsi-to-support-dhcp-based.patch --]
[-- Type: text/x-patch, Size: 4228 bytes --]
>From 472d53f2ca0122b7fbfeabbcbde2c9a15499042b Mon Sep 17 00:00:00 2001
From: Rakesh Ranjan <rakesh@chelsio.com>
Date: Mon, 16 Nov 2009 18:11:56 +0530
Subject: [PATCH 1/2] Required changes for libiscsi to support dhcp based provisioning for offload capable cards.
These changes adds ISCSI_UEVENT_REQ_IPCONF message to invoke dhcp handler.
Signed-off-by: Rakesh Ranjan <rakesh@chelsio.com>
---
Makefile | 3 ++-
include/scsi/iscsi_if.h | 4 ++++
include/scsi/libiscsi.h | 20 ++++++++++++++++++++
include/scsi/scsi_transport_iscsi.h | 1 +
scsi_transport_iscsi.c | 25 +++++++++++++++++++++++++
5 files changed, 52 insertions(+), 1 deletions(-)
diff --git a/Makefile b/Makefile
index 26d33e5..3e5d461 100644
--- a/Makefile
+++ b/Makefile
@@ -2,10 +2,11 @@
# libiscsi modules
#
ifneq ($(KERNELRELEASE),)
- obj-m += libiscsi.o
obj-m += libiscsi_tcp.o
obj-m += scsi_transport_iscsi.o
obj-m += iscsi_tcp.o
+ obj-m += libiscsi.o
+ obj-m += libiscsi_ipconfig.o
EXTRA_CFLAGS += -I$(src)/include
else
diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h
index d67dda2..135f229 100644
--- a/include/scsi/iscsi_if.h
+++ b/include/scsi/iscsi_if.h
@@ -59,6 +59,7 @@ enum iscsi_uevent_e {
ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST = UEVENT_BASE + 19,
ISCSI_UEVENT_PATH_UPDATE = UEVENT_BASE + 20,
+ ISCSI_UEVENT_REQ_IPCONF = UEVENT_BASE + 21,
/* up events */
ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1,
@@ -172,6 +173,9 @@ struct iscsi_uevent {
struct msg_set_path {
uint32_t host_no;
} set_path;
+ struct mag_req_ipconf {
+ uint32_t host_no;
+ } req_ipconf;
} u;
union {
/* messages k -> u */
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index a72edd4..a9af95e 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -330,6 +330,18 @@ struct iscsi_host {
char workq_name[20];
};
+/* libiscsi ipconfig */
+struct dhcp_info {
+ __be32 ltime;
+ __be32 serverid;
+ __be32 ipaddr;
+ __be32 netmask;
+ __be32 dnsaddr;
+ __be32 gipaddr;
+ __u8 *mac_addr;
+};
+
+
/*
* scsi host template
*/
@@ -427,6 +439,14 @@ extern void iscsi_pool_free(struct iscsi_pool *);
extern int iscsi_pool_init(struct iscsi_pool *, int, void ***, int);
/*
+ * libiscsi ipconfig helpers
+ */
+extern int libiscsi_do_ipconf(struct net_device *ndev,
+ struct dhcp_info *dinfo);
+extern int libiscsi_ipconfig_recv(struct net_device *ndev,
+ struct sk_buff *skb);
+
+/*
* inline functions to deal with padding.
*/
static inline unsigned int
diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
index 349c7f3..3e5fd96 100644
--- a/include/scsi/scsi_transport_iscsi.h
+++ b/include/scsi/scsi_transport_iscsi.h
@@ -134,6 +134,7 @@ struct iscsi_transport {
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);
+ int (*req_ipconf) (struct Scsi_Host *shost);
};
/*
diff --git a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c
index ad897df..4897a3f 100644
--- a/scsi_transport_iscsi.c
+++ b/scsi_transport_iscsi.c
@@ -1508,6 +1508,28 @@ iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev)
}
static int
+iscsi_req_ipconf(struct iscsi_transport *transport, struct iscsi_uevent *ev)
+{
+ struct Scsi_Host *shost;
+ int err;
+
+ if (!transport->req_ipconf)
+ return -ENOSYS;
+
+ shost = scsi_host_lookup(ev->u.req_ipconf.host_no);
+ if (!shost) {
+ printk(KERN_ERR "ipconf req could not find host no %u\n",
+ ev->u.req_ipconf.host_no);
+ return -ENODEV;
+ }
+
+ err = transport->req_ipconf(shost);
+
+ 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;
@@ -1627,6 +1649,9 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
case ISCSI_UEVENT_PATH_UPDATE:
err = iscsi_set_path(transport, ev);
break;
+ case ISCSI_UEVENT_REQ_IPCONF:
+ err = iscsi_req_ipconf(transport, ev);
+ break;
default:
err = -ENOSYS;
break;
--
1.6.0.6
[-- Attachment #3: 0002-dhcp-handler-for-libiscsi-to-support-dhcp-provisioni.patch --]
[-- Type: text/x-patch, Size: 11280 bytes --]
>From 8df0fd4d0a972bd76bb71d7fdac274b273cec50d Mon Sep 17 00:00:00 2001
From: Rakesh Ranjan <rakesh@chelsio.com>
Date: Mon, 16 Nov 2009 18:32:57 +0530
Subject: [PATCH 2/2] dhcp handler for libiscsi to support dhcp provisioning for offload capable cards.
This module contains a compact dhcp handler to support dhcp based provisioning for
offload capable cards.
Signed-off-by: Rakesh Ranjan <rakesh@chelsio.com>
---
libiscsi_ipconfig.c | 466 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 466 insertions(+), 0 deletions(-)
create mode 100644 libiscsi_ipconfig.c
diff --git a/libiscsi_ipconfig.c b/libiscsi_ipconfig.c
new file mode 100644
index 0000000..4057aae
--- /dev/null
+++ b/libiscsi_ipconfig.c
@@ -0,0 +1,466 @@
+/* libiscsi_ipconfig.c: libiscsi dhcp client.
+ *
+ * Copyright (c) 2009 Chelsio Communications, Inc.
+ *
+ * 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.
+ *
+ * Written by: Rakesh Ranjan (rakesh@chelsio.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <linux/if_arp.h>
+#include <net/ip.h>
+#include <scsi/iscsi_if.h>
+#include <scsi/libiscsi.h>
+
+#define DHCP_REQUEST 1
+#define DHCP_REPLY 2
+#define DHCP_HTYPE_ETHERNET 1
+#define DHCP_HLEN_ETHERNET 6
+#define DHCP_MSG_LEN 236
+
+#define DHCPC_SERVER_PORT 67
+#define DHCPC_CLIENT_PORT 68
+
+/* DHCP message types */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+/* DHCP options */
+#define DHCP_OPTION_SUBNET_MASK 1
+#define DHCP_OPTION_ROUTER 3
+#define DHCP_OPTION_DNS_SERVER 6
+#define DHCP_OPTION_MTU 26
+#define DHCP_OPTION_REQ_IPADDR 50
+#define DHCP_OPTION_LEASE_TIME 51
+#define DHCP_OPTION_MSG_TYPE 53
+#define DHCP_OPTION_SERVER_ID 54
+#define DHCP_OPTION_REQ_LIST 55
+#define DHCP_OPTION_VCID 60
+#define DHCP_OPTION_END 255
+
+enum {
+ STATE_INIT = 0,
+ STATE_SENDING,
+ STATE_OFFER_REC,
+ STATE_CONFIG_REC,
+};
+
+struct dhcp_pkt {
+ struct iphdr iph;
+ struct udphdr udph;
+ u8 op;
+ u8 htype;
+ u8 hlen;
+ u8 hops;
+ __be32 xid;
+ __be16 secs;
+ __be16 flags;
+ __be32 cipaddr;
+ __be32 yipaddr;
+ __be32 sipaddr;
+ __be32 ripaddr;
+ u8 chwaddr[16];
+ u8 sname[64];
+ u8 bfile[128];
+ u8 options[312];
+};
+
+
+static struct dhcp_client_state {
+ struct sk_buff *skb;
+ struct dhcp_pkt *pkt;
+ struct net_device *ndev;
+ struct list_head list;
+ struct dhcp_info dinfo;
+
+ volatile __u8 state;
+ __be32 xid;
+
+} client_state;
+
+static DEFINE_SPINLOCK(rcv_lock);
+
+static const char *RFC2132_VENDOR_CLASS_ID = "libiscsi client";
+
+static const u8 magic_cookie[4] = { 99, 130, 83, 99 };
+
+static inline u8 *add_msg_type(u8 *optptr, u8 type)
+{
+ *optptr++ = DHCP_OPTION_MSG_TYPE;
+ *optptr++ = 1;
+ *optptr++ = type;
+ return optptr;
+}
+
+static inline u8 *add_req_options(u8 *optptr)
+{
+ *optptr++ = DHCP_OPTION_REQ_LIST;
+ *optptr++ = 4;
+ *optptr++ = DHCP_OPTION_SUBNET_MASK;
+ *optptr++ = DHCP_OPTION_ROUTER;
+ *optptr++ = DHCP_OPTION_DNS_SERVER;
+ *optptr++ = DHCP_OPTION_MTU;
+ return optptr;
+}
+
+static inline u8 *add_vendor_cid(u8 *optptr)
+{
+ u8 len = strlen(RFC2132_VENDOR_CLASS_ID);
+ *optptr++ = DHCP_OPTION_VCID;
+ *optptr++ = len;
+ memcpy(optptr, RFC2132_VENDOR_CLASS_ID, len);
+ optptr += len;
+ return optptr;
+}
+
+static inline u8 *add_server_id(__be32 *sid, u8 *optptr)
+{
+ *optptr++ = DHCP_OPTION_SERVER_ID;
+ *optptr++ = 4;
+ memcpy(optptr, sid, 4);
+ return optptr + 4;
+}
+
+static inline u8 *add_req_ipaddr(__be32 *ip, u8 *optptr)
+{
+ *optptr++ = DHCP_OPTION_REQ_IPADDR;
+ *optptr++ = 4;
+ memcpy(optptr, ip, 4);
+ return optptr + 4;
+}
+
+static inline u8 *add_end(u8 *optptr)
+{
+ *optptr++ = DHCP_OPTION_END;
+ return optptr;
+}
+
+static void
+libiscsi_process_dhcp_opts(struct dhcp_client_state *client, u8 *optptr,
+ int len)
+{
+ u8 *end = optptr + len;
+ u8 type = 0;
+
+ while (optptr < end) {
+ switch (*optptr) {
+ case DHCP_OPTION_SUBNET_MASK:
+ memcpy(&client->dinfo.netmask, optptr + 2, 4);
+ break;
+ case DHCP_OPTION_ROUTER:
+ memcpy(&client->dinfo.gipaddr, optptr + 2, 4);
+ break;
+ case DHCP_OPTION_DNS_SERVER:
+ memcpy(&client->dinfo.dnsaddr, optptr + 2, 4);
+ break;
+ case DHCP_OPTION_MSG_TYPE:
+ type = *(optptr + 2);
+ if (type == DHCPOFFER)
+ client->state = STATE_OFFER_REC;
+ else if (type == DHCPACK)
+ client->state = STATE_CONFIG_REC;
+ break;
+ case DHCP_OPTION_SERVER_ID:
+ memcpy(&client->dinfo.serverid, optptr + 2, 4);
+ break;
+ case DHCP_OPTION_LEASE_TIME:
+ memcpy(&client->dinfo.ltime, optptr + 2, 4);
+ break;
+ case DHCP_OPTION_END:
+ break;
+ }
+
+ optptr += optptr[1] + 2;
+ }
+}
+
+static void
+libiscsi_process_dhcp_pack(struct dhcp_client_state *client,
+ struct dhcp_pkt *pkt)
+{
+ u8 *start, *end;
+ int opt_len;
+
+ start = &pkt->options[4];
+ end = (u8 *) pkt + ntohs(pkt->iph.tot_len);
+ opt_len = end - start;
+
+ if (pkt->op == DHCP_REPLY &&
+ !memcmp(&pkt->xid, &client->xid, sizeof(client->xid)) &&
+ !memcmp(pkt->chwaddr, client->dinfo.mac_addr, pkt->hlen)) {
+ memcpy(&client->dinfo.ipaddr, &pkt->yipaddr, 4);
+ libiscsi_process_dhcp_opts(client, start, opt_len);
+ }
+}
+
+static int
+libiscsi_ipconfig_send(struct dhcp_client_state *client)
+{
+ int rc = 0;
+ struct sk_buff *lskb = client->skb;
+
+ lskb->dev = client->ndev;
+ lskb->protocol = htons(ETH_P_IP);
+
+ dev_hard_header(lskb, client->ndev, ntohs(lskb->protocol),
+ client->ndev->broadcast,
+ client->dinfo.mac_addr, lskb->len);
+ rc = dev_queue_xmit(lskb);
+
+ return rc;
+}
+
+int
+libiscsi_ipconfig_recv(struct net_device *ndev, struct sk_buff *skb)
+{
+ struct iphdr *iph;
+ struct udphdr *udph;
+ struct ethhdr *eh;
+ struct dhcp_pkt *pkt;
+ struct sk_buff *pskb;
+ int len, opts_len;
+ struct dhcp_client_state *client;
+ int rc = 0;
+
+
+ client = &client_state;
+
+ if (unlikely(client->state == STATE_INIT))
+ goto out;
+
+ if (skb->pkt_type != PACKET_OTHERHOST)
+ goto out;
+
+ pskb = skb_copy(skb, GFP_ATOMIC);
+ if (!pskb) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ eh = eth_hdr(pskb);
+ if (!is_valid_ether_addr(eh->h_dest))
+ goto drop;
+
+ if (compare_ether_addr(eh->h_dest, client->dinfo.mac_addr))
+ goto drop;
+
+ if (!pskb_may_pull(pskb, sizeof(struct iphdr) + sizeof(struct udphdr)))
+ goto drop;
+
+
+ skb_reset_network_header(pskb);
+ pkt = (struct dhcp_pkt *) skb_network_header(pskb);
+ iph = &pkt->iph;
+
+ if (iph->ihl != 5 || iph->version != 4 || iph->protocol != IPPROTO_UDP)
+ goto drop;
+
+ if (iph->frag_off & htons(IP_OFFSET | IP_MF))
+ goto drop;
+
+ if (skb->len < ntohs(iph->tot_len))
+ goto drop;
+
+ if (ip_fast_csum((u8 *)iph, iph->ihl))
+ goto drop;
+
+ udph = &pkt->udph;
+ if (udph->source != htons(67) || udph->dest != htons(68))
+ goto drop;
+
+ if (ntohs(iph->tot_len) < ntohs(udph->len) + sizeof(struct iphdr))
+ goto drop;
+
+ len = ntohs(udph->len) - sizeof(struct udphdr);
+ opts_len = len - (sizeof(*pkt) -
+ sizeof(struct iphdr) -
+ sizeof(struct udphdr) -
+ sizeof(pkt->options));
+ if (opts_len < 0)
+ goto drop;
+
+ if (memcmp(pkt->options, magic_cookie, 4))
+ goto drop;
+
+ spin_lock(&rcv_lock);
+
+ libiscsi_process_dhcp_pack(client, pkt);
+
+ spin_unlock(&rcv_lock);
+
+drop:
+ kfree(pskb);
+out:
+ return rc;
+}
+EXPORT_SYMBOL(libiscsi_ipconfig_recv);
+
+static int
+libiscsi_create_dhcp_msg(struct dhcp_client_state *client)
+{
+ struct iphdr *iph;
+ struct udphdr *udph;
+ struct sk_buff *skb;
+ struct dhcp_pkt *pkt;
+ int rc = 0;
+
+ skb = alloc_skb(sizeof(*pkt) +
+ LL_ALLOCATED_SPACE(client->ndev) + 15,
+ GFP_KERNEL);
+ if (!skb) {
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ client->skb = skb;
+ skb_reserve(skb, LL_RESERVED_SPACE(client->ndev));
+
+ pkt = (struct dhcp_pkt *) skb_put(skb, sizeof(*pkt));
+ BUG_ON(!pkt);
+ client->pkt = pkt;
+ memset(pkt, 0, sizeof(*pkt));
+
+ skb_reset_network_header(skb);
+
+ /* construct IP header */
+ iph = &pkt->iph;
+ iph->version = 4;
+ iph->ihl = 5;
+ iph->tot_len = htons(sizeof(struct dhcp_pkt));
+ iph->frag_off = htons(IP_DF);
+ iph->ttl = 64;
+ iph->protocol = IPPROTO_UDP;
+ iph->daddr = htonl(INADDR_BROADCAST);
+ iph->check = ip_fast_csum((u8 *) iph, iph->ihl);
+
+ /* Construct UDP header */
+ udph = &pkt->udph;
+ udph->source = htons(DHCPC_CLIENT_PORT);
+ udph->dest = htons(DHCPC_SERVER_PORT);
+ udph->len = htons(sizeof(struct dhcp_pkt) - sizeof(struct iphdr));
+
+ pkt->op = DHCP_REQUEST;
+ pkt->htype = DHCP_HTYPE_ETHERNET;
+ pkt->hlen = ETH_ALEN;
+
+ memcpy(pkt->chwaddr, client->dinfo.mac_addr, ETH_ALEN);
+ pkt->secs = htons(jiffies / HZ);
+ pkt->xid = client->xid;
+
+ memcpy(pkt->options, magic_cookie, sizeof(magic_cookie));
+
+ return rc;
+}
+
+static int libiscsi_send_dhcp_request(struct dhcp_client_state *client)
+{
+ int rc = 0;
+ u8 *end;
+
+ rc = libiscsi_create_dhcp_msg(client);
+ if (rc)
+ return rc;
+
+ end = add_msg_type(&client->pkt->options[4], DHCPREQUEST);
+ end = add_server_id(&client->dinfo.serverid, end);
+ end = add_req_ipaddr(&client->dinfo.ipaddr, end);
+ end = add_vendor_cid(end);
+ end = add_end(end);
+
+ rc = libiscsi_ipconfig_send(client);
+
+ return rc;
+}
+
+static int libiscsi_send_dhcp_discover(struct dhcp_client_state *client)
+{
+ int rc = 0;
+ u8 *end;
+
+ rc = libiscsi_create_dhcp_msg(client);
+ if (rc)
+ return rc;
+
+ end = add_msg_type(&client->pkt->options[4], DHCPDISCOVER);
+ end = add_req_options(end);
+ end = add_vendor_cid(end);
+ end = add_end(end);
+
+ client->state = STATE_SENDING;
+ rc = libiscsi_ipconfig_send(client);
+
+ return rc;
+}
+
+static void
+libiscsi_wait_for_pack(struct dhcp_client_state *client, u8 state)
+{
+ unsigned long tout, ntout;
+
+ get_random_bytes(&tout, sizeof(tout));
+ tout = (tout % (unsigned)HZ) + (HZ * 2);
+
+ ntout = jiffies + tout;
+ while (time_before(jiffies, ntout) && (client->state != state))
+ schedule_timeout_uninterruptible(1);
+}
+
+int libiscsi_do_ipconf(struct net_device *ndev, struct dhcp_info *dinfo)
+{
+ int rc = 0;
+ int retry;
+ struct dhcp_client_state *client;
+ retry = 2;
+
+ client = &client_state;
+ client->dinfo.mac_addr = dinfo->mac_addr;
+ client->ndev = ndev;
+ client->state = STATE_INIT;
+
+ /* show time */
+ for (;;) {
+ get_random_bytes(&client->xid, sizeof(__be32));
+ libiscsi_send_dhcp_discover(client);
+ libiscsi_wait_for_pack(client, STATE_OFFER_REC);
+
+ if (client->state == STATE_OFFER_REC) {
+ libiscsi_send_dhcp_request(client);
+ libiscsi_wait_for_pack(client, STATE_CONFIG_REC);
+ if (client->state == STATE_CONFIG_REC) {
+ dinfo->ipaddr = client->dinfo.ipaddr;
+ dinfo->netmask = client->dinfo.netmask;
+ dinfo->ltime = client->dinfo.ltime;
+ client->state = STATE_INIT;
+ break;
+ }
+ }
+
+ if (!--retry) {
+ rc = -ENETUNREACH;
+ break;
+ }
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(libiscsi_do_ipconf);
+
+MODULE_AUTHOR("Rakesh Ranjan");
+MODULE_DESCRIPTION("iSCSI ipconfig functions");
+MODULE_LICENSE("GPL");
--
1.6.0.6
WARNING: multiple messages have this Message-ID (diff)
From: Rakesh Ranjan <rakesh-ut6Up61K2wZBDgjK7y7TUQ@public.gmane.org>
To: Mike Christie <michaelc-hcNo3dDEHLuVc3sceRu5cw@public.gmane.org>
Cc: "open-iscsi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org"
<open-iscsi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org>,
netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
LKML <linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>,
David Miller <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>,
James Bottomley <James.Bottomley-l3A5Bk7waGM@public.gmane.org>,
Karen Xie <kxie-ut6Up61K2wZBDgjK7y7TUQ@public.gmane.org>,
Rakesh Ranjan <rakesh-ut6Up61K2wZBDgjK7y7TUQ@public.gmane.org>
Subject: [RFC-PATCH] libiscsi dhcp handler
Date: Mon, 16 Nov 2009 18:41:49 +0530 [thread overview]
Message-ID: <4B014F95.8000906@chelsio.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 748 bytes --]
Hi mike,
Herein attached patches to support dhcp based provisioning for iSCSI
offload capable cards. I have made dhcp code as generic as possible,
please go through the code. Based on the feedback I will submit final
version of these patches.
Regards
Rakesh Ranjan
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "open-iscsi" group.
To post to this group, send email to open-iscsi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
To unsubscribe from this group, send email to open-iscsi+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
For more options, visit this group at http://groups.google.com/group/open-iscsi
-~----------~----~----~----~------~----~------~--~---
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Required-changes-for-libiscsi-to-support-dhcp-based.patch --]
[-- Type: text/x-patch, Size: 4284 bytes --]
>From 472d53f2ca0122b7fbfeabbcbde2c9a15499042b Mon Sep 17 00:00:00 2001
From: Rakesh Ranjan <rakesh-ut6Up61K2wZBDgjK7y7TUQ@public.gmane.org>
Date: Mon, 16 Nov 2009 18:11:56 +0530
Subject: [PATCH 1/2] Required changes for libiscsi to support dhcp based provisioning for offload capable cards.
These changes adds ISCSI_UEVENT_REQ_IPCONF message to invoke dhcp handler.
Signed-off-by: Rakesh Ranjan <rakesh-ut6Up61K2wZBDgjK7y7TUQ@public.gmane.org>
---
Makefile | 3 ++-
include/scsi/iscsi_if.h | 4 ++++
include/scsi/libiscsi.h | 20 ++++++++++++++++++++
include/scsi/scsi_transport_iscsi.h | 1 +
scsi_transport_iscsi.c | 25 +++++++++++++++++++++++++
5 files changed, 52 insertions(+), 1 deletions(-)
diff --git a/Makefile b/Makefile
index 26d33e5..3e5d461 100644
--- a/Makefile
+++ b/Makefile
@@ -2,10 +2,11 @@
# libiscsi modules
#
ifneq ($(KERNELRELEASE),)
- obj-m += libiscsi.o
obj-m += libiscsi_tcp.o
obj-m += scsi_transport_iscsi.o
obj-m += iscsi_tcp.o
+ obj-m += libiscsi.o
+ obj-m += libiscsi_ipconfig.o
EXTRA_CFLAGS += -I$(src)/include
else
diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h
index d67dda2..135f229 100644
--- a/include/scsi/iscsi_if.h
+++ b/include/scsi/iscsi_if.h
@@ -59,6 +59,7 @@ enum iscsi_uevent_e {
ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST = UEVENT_BASE + 19,
ISCSI_UEVENT_PATH_UPDATE = UEVENT_BASE + 20,
+ ISCSI_UEVENT_REQ_IPCONF = UEVENT_BASE + 21,
/* up events */
ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1,
@@ -172,6 +173,9 @@ struct iscsi_uevent {
struct msg_set_path {
uint32_t host_no;
} set_path;
+ struct mag_req_ipconf {
+ uint32_t host_no;
+ } req_ipconf;
} u;
union {
/* messages k -> u */
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index a72edd4..a9af95e 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -330,6 +330,18 @@ struct iscsi_host {
char workq_name[20];
};
+/* libiscsi ipconfig */
+struct dhcp_info {
+ __be32 ltime;
+ __be32 serverid;
+ __be32 ipaddr;
+ __be32 netmask;
+ __be32 dnsaddr;
+ __be32 gipaddr;
+ __u8 *mac_addr;
+};
+
+
/*
* scsi host template
*/
@@ -427,6 +439,14 @@ extern void iscsi_pool_free(struct iscsi_pool *);
extern int iscsi_pool_init(struct iscsi_pool *, int, void ***, int);
/*
+ * libiscsi ipconfig helpers
+ */
+extern int libiscsi_do_ipconf(struct net_device *ndev,
+ struct dhcp_info *dinfo);
+extern int libiscsi_ipconfig_recv(struct net_device *ndev,
+ struct sk_buff *skb);
+
+/*
* inline functions to deal with padding.
*/
static inline unsigned int
diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
index 349c7f3..3e5fd96 100644
--- a/include/scsi/scsi_transport_iscsi.h
+++ b/include/scsi/scsi_transport_iscsi.h
@@ -134,6 +134,7 @@ struct iscsi_transport {
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);
+ int (*req_ipconf) (struct Scsi_Host *shost);
};
/*
diff --git a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c
index ad897df..4897a3f 100644
--- a/scsi_transport_iscsi.c
+++ b/scsi_transport_iscsi.c
@@ -1508,6 +1508,28 @@ iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev)
}
static int
+iscsi_req_ipconf(struct iscsi_transport *transport, struct iscsi_uevent *ev)
+{
+ struct Scsi_Host *shost;
+ int err;
+
+ if (!transport->req_ipconf)
+ return -ENOSYS;
+
+ shost = scsi_host_lookup(ev->u.req_ipconf.host_no);
+ if (!shost) {
+ printk(KERN_ERR "ipconf req could not find host no %u\n",
+ ev->u.req_ipconf.host_no);
+ return -ENODEV;
+ }
+
+ err = transport->req_ipconf(shost);
+
+ 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;
@@ -1627,6 +1649,9 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
case ISCSI_UEVENT_PATH_UPDATE:
err = iscsi_set_path(transport, ev);
break;
+ case ISCSI_UEVENT_REQ_IPCONF:
+ err = iscsi_req_ipconf(transport, ev);
+ break;
default:
err = -ENOSYS;
break;
--
1.6.0.6
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-dhcp-handler-for-libiscsi-to-support-dhcp-provisioni.patch --]
[-- Type: text/x-patch, Size: 11364 bytes --]
>From 8df0fd4d0a972bd76bb71d7fdac274b273cec50d Mon Sep 17 00:00:00 2001
From: Rakesh Ranjan <rakesh-ut6Up61K2wZBDgjK7y7TUQ@public.gmane.org>
Date: Mon, 16 Nov 2009 18:32:57 +0530
Subject: [PATCH 2/2] dhcp handler for libiscsi to support dhcp provisioning for offload capable cards.
This module contains a compact dhcp handler to support dhcp based provisioning for
offload capable cards.
Signed-off-by: Rakesh Ranjan <rakesh-ut6Up61K2wZBDgjK7y7TUQ@public.gmane.org>
---
libiscsi_ipconfig.c | 466 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 466 insertions(+), 0 deletions(-)
create mode 100644 libiscsi_ipconfig.c
diff --git a/libiscsi_ipconfig.c b/libiscsi_ipconfig.c
new file mode 100644
index 0000000..4057aae
--- /dev/null
+++ b/libiscsi_ipconfig.c
@@ -0,0 +1,466 @@
+/* libiscsi_ipconfig.c: libiscsi dhcp client.
+ *
+ * Copyright (c) 2009 Chelsio Communications, Inc.
+ *
+ * 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.
+ *
+ * Written by: Rakesh Ranjan (rakesh-ut6Up61K2wZBDgjK7y7TUQ@public.gmane.org)
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <linux/if_arp.h>
+#include <net/ip.h>
+#include <scsi/iscsi_if.h>
+#include <scsi/libiscsi.h>
+
+#define DHCP_REQUEST 1
+#define DHCP_REPLY 2
+#define DHCP_HTYPE_ETHERNET 1
+#define DHCP_HLEN_ETHERNET 6
+#define DHCP_MSG_LEN 236
+
+#define DHCPC_SERVER_PORT 67
+#define DHCPC_CLIENT_PORT 68
+
+/* DHCP message types */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+/* DHCP options */
+#define DHCP_OPTION_SUBNET_MASK 1
+#define DHCP_OPTION_ROUTER 3
+#define DHCP_OPTION_DNS_SERVER 6
+#define DHCP_OPTION_MTU 26
+#define DHCP_OPTION_REQ_IPADDR 50
+#define DHCP_OPTION_LEASE_TIME 51
+#define DHCP_OPTION_MSG_TYPE 53
+#define DHCP_OPTION_SERVER_ID 54
+#define DHCP_OPTION_REQ_LIST 55
+#define DHCP_OPTION_VCID 60
+#define DHCP_OPTION_END 255
+
+enum {
+ STATE_INIT = 0,
+ STATE_SENDING,
+ STATE_OFFER_REC,
+ STATE_CONFIG_REC,
+};
+
+struct dhcp_pkt {
+ struct iphdr iph;
+ struct udphdr udph;
+ u8 op;
+ u8 htype;
+ u8 hlen;
+ u8 hops;
+ __be32 xid;
+ __be16 secs;
+ __be16 flags;
+ __be32 cipaddr;
+ __be32 yipaddr;
+ __be32 sipaddr;
+ __be32 ripaddr;
+ u8 chwaddr[16];
+ u8 sname[64];
+ u8 bfile[128];
+ u8 options[312];
+};
+
+
+static struct dhcp_client_state {
+ struct sk_buff *skb;
+ struct dhcp_pkt *pkt;
+ struct net_device *ndev;
+ struct list_head list;
+ struct dhcp_info dinfo;
+
+ volatile __u8 state;
+ __be32 xid;
+
+} client_state;
+
+static DEFINE_SPINLOCK(rcv_lock);
+
+static const char *RFC2132_VENDOR_CLASS_ID = "libiscsi client";
+
+static const u8 magic_cookie[4] = { 99, 130, 83, 99 };
+
+static inline u8 *add_msg_type(u8 *optptr, u8 type)
+{
+ *optptr++ = DHCP_OPTION_MSG_TYPE;
+ *optptr++ = 1;
+ *optptr++ = type;
+ return optptr;
+}
+
+static inline u8 *add_req_options(u8 *optptr)
+{
+ *optptr++ = DHCP_OPTION_REQ_LIST;
+ *optptr++ = 4;
+ *optptr++ = DHCP_OPTION_SUBNET_MASK;
+ *optptr++ = DHCP_OPTION_ROUTER;
+ *optptr++ = DHCP_OPTION_DNS_SERVER;
+ *optptr++ = DHCP_OPTION_MTU;
+ return optptr;
+}
+
+static inline u8 *add_vendor_cid(u8 *optptr)
+{
+ u8 len = strlen(RFC2132_VENDOR_CLASS_ID);
+ *optptr++ = DHCP_OPTION_VCID;
+ *optptr++ = len;
+ memcpy(optptr, RFC2132_VENDOR_CLASS_ID, len);
+ optptr += len;
+ return optptr;
+}
+
+static inline u8 *add_server_id(__be32 *sid, u8 *optptr)
+{
+ *optptr++ = DHCP_OPTION_SERVER_ID;
+ *optptr++ = 4;
+ memcpy(optptr, sid, 4);
+ return optptr + 4;
+}
+
+static inline u8 *add_req_ipaddr(__be32 *ip, u8 *optptr)
+{
+ *optptr++ = DHCP_OPTION_REQ_IPADDR;
+ *optptr++ = 4;
+ memcpy(optptr, ip, 4);
+ return optptr + 4;
+}
+
+static inline u8 *add_end(u8 *optptr)
+{
+ *optptr++ = DHCP_OPTION_END;
+ return optptr;
+}
+
+static void
+libiscsi_process_dhcp_opts(struct dhcp_client_state *client, u8 *optptr,
+ int len)
+{
+ u8 *end = optptr + len;
+ u8 type = 0;
+
+ while (optptr < end) {
+ switch (*optptr) {
+ case DHCP_OPTION_SUBNET_MASK:
+ memcpy(&client->dinfo.netmask, optptr + 2, 4);
+ break;
+ case DHCP_OPTION_ROUTER:
+ memcpy(&client->dinfo.gipaddr, optptr + 2, 4);
+ break;
+ case DHCP_OPTION_DNS_SERVER:
+ memcpy(&client->dinfo.dnsaddr, optptr + 2, 4);
+ break;
+ case DHCP_OPTION_MSG_TYPE:
+ type = *(optptr + 2);
+ if (type == DHCPOFFER)
+ client->state = STATE_OFFER_REC;
+ else if (type == DHCPACK)
+ client->state = STATE_CONFIG_REC;
+ break;
+ case DHCP_OPTION_SERVER_ID:
+ memcpy(&client->dinfo.serverid, optptr + 2, 4);
+ break;
+ case DHCP_OPTION_LEASE_TIME:
+ memcpy(&client->dinfo.ltime, optptr + 2, 4);
+ break;
+ case DHCP_OPTION_END:
+ break;
+ }
+
+ optptr += optptr[1] + 2;
+ }
+}
+
+static void
+libiscsi_process_dhcp_pack(struct dhcp_client_state *client,
+ struct dhcp_pkt *pkt)
+{
+ u8 *start, *end;
+ int opt_len;
+
+ start = &pkt->options[4];
+ end = (u8 *) pkt + ntohs(pkt->iph.tot_len);
+ opt_len = end - start;
+
+ if (pkt->op == DHCP_REPLY &&
+ !memcmp(&pkt->xid, &client->xid, sizeof(client->xid)) &&
+ !memcmp(pkt->chwaddr, client->dinfo.mac_addr, pkt->hlen)) {
+ memcpy(&client->dinfo.ipaddr, &pkt->yipaddr, 4);
+ libiscsi_process_dhcp_opts(client, start, opt_len);
+ }
+}
+
+static int
+libiscsi_ipconfig_send(struct dhcp_client_state *client)
+{
+ int rc = 0;
+ struct sk_buff *lskb = client->skb;
+
+ lskb->dev = client->ndev;
+ lskb->protocol = htons(ETH_P_IP);
+
+ dev_hard_header(lskb, client->ndev, ntohs(lskb->protocol),
+ client->ndev->broadcast,
+ client->dinfo.mac_addr, lskb->len);
+ rc = dev_queue_xmit(lskb);
+
+ return rc;
+}
+
+int
+libiscsi_ipconfig_recv(struct net_device *ndev, struct sk_buff *skb)
+{
+ struct iphdr *iph;
+ struct udphdr *udph;
+ struct ethhdr *eh;
+ struct dhcp_pkt *pkt;
+ struct sk_buff *pskb;
+ int len, opts_len;
+ struct dhcp_client_state *client;
+ int rc = 0;
+
+
+ client = &client_state;
+
+ if (unlikely(client->state == STATE_INIT))
+ goto out;
+
+ if (skb->pkt_type != PACKET_OTHERHOST)
+ goto out;
+
+ pskb = skb_copy(skb, GFP_ATOMIC);
+ if (!pskb) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ eh = eth_hdr(pskb);
+ if (!is_valid_ether_addr(eh->h_dest))
+ goto drop;
+
+ if (compare_ether_addr(eh->h_dest, client->dinfo.mac_addr))
+ goto drop;
+
+ if (!pskb_may_pull(pskb, sizeof(struct iphdr) + sizeof(struct udphdr)))
+ goto drop;
+
+
+ skb_reset_network_header(pskb);
+ pkt = (struct dhcp_pkt *) skb_network_header(pskb);
+ iph = &pkt->iph;
+
+ if (iph->ihl != 5 || iph->version != 4 || iph->protocol != IPPROTO_UDP)
+ goto drop;
+
+ if (iph->frag_off & htons(IP_OFFSET | IP_MF))
+ goto drop;
+
+ if (skb->len < ntohs(iph->tot_len))
+ goto drop;
+
+ if (ip_fast_csum((u8 *)iph, iph->ihl))
+ goto drop;
+
+ udph = &pkt->udph;
+ if (udph->source != htons(67) || udph->dest != htons(68))
+ goto drop;
+
+ if (ntohs(iph->tot_len) < ntohs(udph->len) + sizeof(struct iphdr))
+ goto drop;
+
+ len = ntohs(udph->len) - sizeof(struct udphdr);
+ opts_len = len - (sizeof(*pkt) -
+ sizeof(struct iphdr) -
+ sizeof(struct udphdr) -
+ sizeof(pkt->options));
+ if (opts_len < 0)
+ goto drop;
+
+ if (memcmp(pkt->options, magic_cookie, 4))
+ goto drop;
+
+ spin_lock(&rcv_lock);
+
+ libiscsi_process_dhcp_pack(client, pkt);
+
+ spin_unlock(&rcv_lock);
+
+drop:
+ kfree(pskb);
+out:
+ return rc;
+}
+EXPORT_SYMBOL(libiscsi_ipconfig_recv);
+
+static int
+libiscsi_create_dhcp_msg(struct dhcp_client_state *client)
+{
+ struct iphdr *iph;
+ struct udphdr *udph;
+ struct sk_buff *skb;
+ struct dhcp_pkt *pkt;
+ int rc = 0;
+
+ skb = alloc_skb(sizeof(*pkt) +
+ LL_ALLOCATED_SPACE(client->ndev) + 15,
+ GFP_KERNEL);
+ if (!skb) {
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ client->skb = skb;
+ skb_reserve(skb, LL_RESERVED_SPACE(client->ndev));
+
+ pkt = (struct dhcp_pkt *) skb_put(skb, sizeof(*pkt));
+ BUG_ON(!pkt);
+ client->pkt = pkt;
+ memset(pkt, 0, sizeof(*pkt));
+
+ skb_reset_network_header(skb);
+
+ /* construct IP header */
+ iph = &pkt->iph;
+ iph->version = 4;
+ iph->ihl = 5;
+ iph->tot_len = htons(sizeof(struct dhcp_pkt));
+ iph->frag_off = htons(IP_DF);
+ iph->ttl = 64;
+ iph->protocol = IPPROTO_UDP;
+ iph->daddr = htonl(INADDR_BROADCAST);
+ iph->check = ip_fast_csum((u8 *) iph, iph->ihl);
+
+ /* Construct UDP header */
+ udph = &pkt->udph;
+ udph->source = htons(DHCPC_CLIENT_PORT);
+ udph->dest = htons(DHCPC_SERVER_PORT);
+ udph->len = htons(sizeof(struct dhcp_pkt) - sizeof(struct iphdr));
+
+ pkt->op = DHCP_REQUEST;
+ pkt->htype = DHCP_HTYPE_ETHERNET;
+ pkt->hlen = ETH_ALEN;
+
+ memcpy(pkt->chwaddr, client->dinfo.mac_addr, ETH_ALEN);
+ pkt->secs = htons(jiffies / HZ);
+ pkt->xid = client->xid;
+
+ memcpy(pkt->options, magic_cookie, sizeof(magic_cookie));
+
+ return rc;
+}
+
+static int libiscsi_send_dhcp_request(struct dhcp_client_state *client)
+{
+ int rc = 0;
+ u8 *end;
+
+ rc = libiscsi_create_dhcp_msg(client);
+ if (rc)
+ return rc;
+
+ end = add_msg_type(&client->pkt->options[4], DHCPREQUEST);
+ end = add_server_id(&client->dinfo.serverid, end);
+ end = add_req_ipaddr(&client->dinfo.ipaddr, end);
+ end = add_vendor_cid(end);
+ end = add_end(end);
+
+ rc = libiscsi_ipconfig_send(client);
+
+ return rc;
+}
+
+static int libiscsi_send_dhcp_discover(struct dhcp_client_state *client)
+{
+ int rc = 0;
+ u8 *end;
+
+ rc = libiscsi_create_dhcp_msg(client);
+ if (rc)
+ return rc;
+
+ end = add_msg_type(&client->pkt->options[4], DHCPDISCOVER);
+ end = add_req_options(end);
+ end = add_vendor_cid(end);
+ end = add_end(end);
+
+ client->state = STATE_SENDING;
+ rc = libiscsi_ipconfig_send(client);
+
+ return rc;
+}
+
+static void
+libiscsi_wait_for_pack(struct dhcp_client_state *client, u8 state)
+{
+ unsigned long tout, ntout;
+
+ get_random_bytes(&tout, sizeof(tout));
+ tout = (tout % (unsigned)HZ) + (HZ * 2);
+
+ ntout = jiffies + tout;
+ while (time_before(jiffies, ntout) && (client->state != state))
+ schedule_timeout_uninterruptible(1);
+}
+
+int libiscsi_do_ipconf(struct net_device *ndev, struct dhcp_info *dinfo)
+{
+ int rc = 0;
+ int retry;
+ struct dhcp_client_state *client;
+ retry = 2;
+
+ client = &client_state;
+ client->dinfo.mac_addr = dinfo->mac_addr;
+ client->ndev = ndev;
+ client->state = STATE_INIT;
+
+ /* show time */
+ for (;;) {
+ get_random_bytes(&client->xid, sizeof(__be32));
+ libiscsi_send_dhcp_discover(client);
+ libiscsi_wait_for_pack(client, STATE_OFFER_REC);
+
+ if (client->state == STATE_OFFER_REC) {
+ libiscsi_send_dhcp_request(client);
+ libiscsi_wait_for_pack(client, STATE_CONFIG_REC);
+ if (client->state == STATE_CONFIG_REC) {
+ dinfo->ipaddr = client->dinfo.ipaddr;
+ dinfo->netmask = client->dinfo.netmask;
+ dinfo->ltime = client->dinfo.ltime;
+ client->state = STATE_INIT;
+ break;
+ }
+ }
+
+ if (!--retry) {
+ rc = -ENETUNREACH;
+ break;
+ }
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(libiscsi_do_ipconf);
+
+MODULE_AUTHOR("Rakesh Ranjan");
+MODULE_DESCRIPTION("iSCSI ipconfig functions");
+MODULE_LICENSE("GPL");
--
1.6.0.6
next reply other threads:[~2009-11-16 13:12 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-11-16 13:11 Rakesh Ranjan [this message]
2009-11-16 13:11 ` [RFC-PATCH] libiscsi dhcp handler Rakesh Ranjan
2009-11-16 13:18 ` David Miller
2009-11-16 13:24 ` Rakesh Ranjan
2009-11-16 13:45 ` Rakesh Ranjan
2009-11-18 3:26 ` Rakesh Ranjan
2009-11-18 3:26 ` Rakesh Ranjan
2009-11-18 7:25 ` David Miller
2009-11-20 13:29 ` Dan Carpenter
2009-11-21 8:13 ` Dan Carpenter
2010-01-05 6:42 ` David Miller
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4B014F95.8000906@chelsio.com \
--to=rakesh@chelsio.com \
--cc=James.Bottomley@suse.de \
--cc=davem@davemloft.net \
--cc=kxie@chelsio.com \
--cc=linux-kernel@vger.kernel.org \
--cc=michaelc@cs.wisc.edu \
--cc=netdev@vger.kernel.org \
--cc=open-iscsi@googlegroups.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.