This patch adds the code to iSCSI to make use of the dmaengine. If
CONFIG_ISCSI_DMA is enabled then we offload copying skb bits to
dma_skb_copy_bits. The implementation of this patch is fairly simple
and should be easy to follow.
Signed-off-by: Supreeth Venkataraman <[EMAIL PROTECTED]>
---
drivers/scsi/iscsi_tcp.c | 72 ++++++++++++++++++++++++++++-
drivers/scsi/libiscsi.c | 115 ++++++++++++++++++++++++++++++++++++++
++++++++
include/scsi/libiscsi.h | 14 ++++++
3 files changed, 200 insertions(+), 1 deletions(-)
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 57ce225..d0d546f 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -33,6 +33,7 @@
#include <linux/blkdev.h>
#include <linux/crypto.h>
#include <linux/delay.h>
+#include <linux/smp.h>
#include <linux/kfifo.h>
#include <linux/scatterlist.h>
#include <net/tcp.h>
@@ -42,6 +43,10 @@
#include <scsi/scsi.h>
#include <scsi/scsi_transport_iscsi.h>
+#ifdef CONFIG_ISCSI_DMA
+#include <net/netdma.h>
+#endif /* CONFIG_ISCSI_DMA */
+
#include "iscsi_tcp.h"
MODULE_AUTHOR("Dmitry Yusupov <[EMAIL PROTECTED]>, "
@@ -587,7 +592,7 @@ copy_hdr:
* ctask->data_count left to copy from in progress Data-In
* buf_left left to copy from in progress buffer
**/
-static inline int
+int
iscsi_ctask_copy(struct iscsi_tcp_conn *tcp_conn, struct
iscsi_cmd_task *ctask,
void *buf, int buf_size, int offset)
{
@@ -596,6 +601,10 @@ iscsi_ctask_copy(struct iscsi_tcp_conn *tcp_conn,
struct iscsi_cmd_task *ctask,
unsigned size = min(tcp_conn->in.copy, buf_left);
int rc;
+#ifdef CONFIG_ISCSI_DMA
+ dma_cookie_t cookie;
+#endif /* CONFIG_ISCSI_DMA */
+
size = min(size, ctask->data_count);
debug_tcp("ctask_copy %d bytes at offset %d copied %d\n",
@@ -604,8 +613,22 @@ iscsi_ctask_copy(struct iscsi_tcp_conn *tcp_conn,
struct iscsi_cmd_task *ctask,
BUG_ON(size <= 0);
BUG_ON(tcp_ctask->sent + size > scsi_bufflen(ctask->sc));
+#ifdef CONFIG_ISCSI_DMA
+ if (tcp_conn->iscsi_conn->dma_chan) {
+ rc = dma_skb_copy_bits(tcp_conn->iscsi_conn->dma_chan, tcp_conn-
>in.skb, tcp_conn->in.offset,
+ (char*)buf + (offset +
tcp_conn->data_copied), size,
+ &cookie);
+ if (0 < cookie)
+ tcp_conn->iscsi_conn->dma_cookie = cookie;
+
+ } else {
+ rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset,
+ (char*)buf + (offset +
tcp_conn->data_copied), size);
+ }
+#else
rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset,
(char*)buf + (offset + tcp_conn->data_copied), size);
+#endif /* CONFIG_ISCSI_DMA */
/* must fit into skb->len */
BUG_ON(rc);
@@ -756,6 +779,42 @@ static int iscsi_scsi_data_in(struct iscsi_conn
*conn)
/* check for non-exceptional status */
if (tcp_conn->in.hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+#ifdef CONFIG_ISCSI_DMA
+ if (0 < tcp_conn->iscsi_conn->dma_cookie) {
+ struct sk_buff *skb;
+ struct sock *sk;
+ dma_cookie_t done, used, skb_cookie = 0;
+
+ sk = tcp_conn->sock->sk;
+
+
dma_async_memcpy_issue_pending(tcp_conn->iscsi_conn->dma_chan);
+
+ while (dma_async_memcpy_complete(tcp_conn-
>iscsi_conn->dma_chan,
+ tcp_conn-
>iscsi_conn->dma_cookie, &done,
+ &used) ==
DMA_IN_PROGRESS) {
+ /* do partial cleanup of
sk_async_wait_queue up to done*/
+ do
+ {
+ skb =
skb_peek(&sk->sk_async_wait_queue);
+
+ if (NULL == skb || done <
skb->dma_cookie)
+ break;
+ else {
+ skb_cookie = skb->dma_cookie;
+
__skb_dequeue(&sk->sk_async_wait_queue);
+ kfree_skb(skb);
+ }
+ } while (skb_cookie != done);
+ }
+
+ /* Safe to free early-copied skbs now */
+ __skb_queue_purge(&sk->sk_async_wait_queue);
+ tcp_conn->iscsi_conn->dma_cookie = 0;
+ }
+
+ if ( tcp_conn->iscsi_conn->dma_chan)
+ dma_chan_put(tcp_conn->iscsi_conn->dma_chan);
+#endif /* CONFIG_ISCSI_DMA */
debug_scsi("done [sc %lx res %d itt 0x%x flags 0x%x]\n",
(long)sc, sc->result, ctask->itt,
tcp_conn->in.hdr->flags);
@@ -1955,6 +2014,10 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session
*cls_session,
sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
sk->sk_allocation = GFP_ATOMIC;
+#ifdef CONFIG_ISCSI_DMA
+ skb_queue_head_init(&sk->sk_async_wait_queue);
+#endif /* CONFIG_ISCSI_DMA */
+
/* FIXME: disable Nagle's algorithm */
/*
@@ -2305,6 +2368,10 @@ iscsi_tcp_init(void)
if (!iscsi_register_transport(&iscsi_tcp_transport))
return -ENODEV;
+#ifdef CONFIG_ISCSI_DMA
+ iscsi_register_dma();
+#endif /* CONFIG_ISCSI_DMA */
+
return 0;
}
@@ -2312,6 +2379,9 @@ static void __exit
iscsi_tcp_exit(void)
{
iscsi_unregister_transport(&iscsi_tcp_transport);
+#ifdef CONFIG_ISCSI_DMA
+ iscsi_unregister_dma();
+#endif /* CONFIG_ISCSI_DMA */
}
module_init(iscsi_tcp_init);
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 8b57af5..2d7cc2c 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -23,7 +23,11 @@
*/
#include <linux/types.h>
#include <linux/kfifo.h>
+#include <linux/kernel.h>
#include <linux/delay.h>
+#include <linux/rcupdate.h>
+#include <linux/smp.h>
+#include <linux/threads.h>
#include <asm/unaligned.h>
#include <net/tcp.h>
#include <scsi/scsi_cmnd.h>
@@ -37,6 +41,28 @@
#include <scsi/scsi_transport_iscsi.h>
#include <scsi/libiscsi.h>
+#ifdef CONFIG_ISCSI_DMA
+#include <linux/dmaengine.h>
+
+struct iscsi_dma {
+ struct dma_client client;
+ spinlock_t lock;
+ cpumask_t channel_mask;
+ struct dma_chan *channel[NR_CPUS];
+};
+
+static enum dma_state_client
+iscsi_dma_event(struct dma_client *client, struct dma_chan *chan,
+ enum dma_state state);
+
+static struct iscsi_dma iscsi_dma = {
+ .client = {
+ .event_callback = iscsi_dma_event,
+ },
+};
+#endif /* CONFIG_ISCSI_DMA */
+
+
struct iscsi_session *
class_to_transport_session(struct iscsi_cls_session *cls_session)
{
@@ -291,6 +317,9 @@ invalid_datalen:
min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
}
+ if (sc->sc_data_direction == DMA_TO_DEVICE)
+ goto out;
+
if (rhdr->flags & ISCSI_FLAG_CMD_UNDERFLOW) {
int res_count = be32_to_cpu(rhdr->residual_count);
@@ -1534,6 +1563,7 @@ iscsi_conn_setup(struct iscsi_cls_session
*cls_session, uint32_t conn_idx)
struct iscsi_conn *conn;
struct iscsi_cls_conn *cls_conn;
char *data;
+ int id = 0;
cls_conn = iscsi_create_conn(cls_session, conn_idx);
if (!cls_conn)
@@ -1546,6 +1576,17 @@ iscsi_conn_setup(struct iscsi_cls_session
*cls_session, uint32_t conn_idx)
conn->c_stage = ISCSI_CONN_INITIAL_STAGE;
conn->id = conn_idx;
conn->exp_statsn = 0;
+
+#ifdef CONFIG_ISCSI_DMA
+ id = smp_processor_id();
+ if (iscsi_dma.channel[id]) {
+ printk(KERN_INFO "iscsi_conn_setup: %d \n",id);
+ dma_chan_get(iscsi_dma.channel[id]);
+ conn->dma_chan = iscsi_dma.channel[id];
+ }
+ conn->dma_cookie = 0;
+#endif /*CONFIG_ISCSI_DMA*/
+
conn->tmabort_state = TMABORT_INITIAL;
INIT_LIST_HEAD(&conn->run_list);
INIT_LIST_HEAD(&conn->mgmt_run_list);
@@ -2135,6 +2176,80 @@ int iscsi_host_set_param(struct Scsi_Host
*shost, enum iscsi_host_param param,
}
EXPORT_SYMBOL_GPL(iscsi_host_set_param);
+#ifdef CONFIG_ISCSI_DMA
+/**
+ * iscsi_dma_event - event callback for the iscsi_dma_client
+ * @client: should always be iscsi_dma_client
+ * @chan: DMA channel for the event
+ * @event: event type
+ */
+static enum dma_state_client iscsi_dma_event(struct dma_client
*client,
+ struct dma_chan *chan, enum dma_state event)
+{
+ int i, found = 0, pos = -1;
+ struct iscsi_dma *iscsi_dma =
+ container_of(client, struct iscsi_dma, client);
+ enum dma_state_client ack = DMA_DUP; /* default: take no action */
+
+ spin_lock(&iscsi_dma->lock);
+ switch (event) {
+ case DMA_RESOURCE_AVAILABLE:
+ for (i = 0; i < NR_CPUS; i++)
+ if (iscsi_dma->channel[i] == chan) {
+ found = 1;
+ break;
+ } else if (iscsi_dma->channel[i] == NULL && pos < 0)
+ pos = i;
+
+ if (!found && pos >= 0) {
+ ack = DMA_ACK;
+ iscsi_dma->channel[pos] = chan;
+ cpu_set(pos, iscsi_dma->channel_mask);
+ //iscsi_dma_rebalance(iscsi_dma);
+ }
+ break;
+ case DMA_RESOURCE_REMOVED:
+ for (i = 0; i < NR_CPUS; i++)
+ if (iscsi_dma->channel[i] == chan) {
+ found = 1;
+ pos = i;
+ break;
+ }
+
+ if (found) {
+ ack = DMA_ACK;
+ cpu_clear(pos, iscsi_dma->channel_mask);
+ iscsi_dma->channel[i] = NULL;
+ //iscsi_dma_rebalance(net_dma);
+ }
+ break;
+ default:
+ break;
+ }
+ spin_unlock(&iscsi_dma->lock);
+
+ return ack;
+}
+
+EXPORT_SYMBOL_GPL(iscsi_dma_event);
+
+int iscsi_register_dma(void)
+{
+ spin_lock_init(&iscsi_dma.lock);
+ dma_cap_set(DMA_MEMCPY, iscsi_dma.client.cap_mask);
+ dma_async_client_register(&iscsi_dma.client);
+ dma_async_client_chan_request(&iscsi_dma.client);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_register_dma);
+
+void iscsi_unregister_dma(void)
+{
+ dma_async_client_unregister(&iscsi_dma.client);
+}
+EXPORT_SYMBOL_GPL(iscsi_unregister_dma);
+#endif /* CONFIG_ISCSI_DMA */
+
MODULE_AUTHOR("Mike Christie");
MODULE_DESCRIPTION("iSCSI library functions");
MODULE_LICENSE("GPL");
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index b4b3113..b443282 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -27,8 +27,13 @@
#include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
+#include <linux/cpumask.h>
#include <scsi/iscsi_proto.h>
#include <scsi/iscsi_if.h>
+#include <scsi/iscsi_if.h>
+#ifdef CONFIG_ISCSI_DMA
+#include <linux/dmaengine.h>
+#endif /* CONFIG_ISCSI_DMA */
struct scsi_transport_template;
struct scsi_device;
@@ -196,6 +201,10 @@ struct iscsi_conn {
/* custom statistics */
uint32_t eh_abort_cnt;
+#ifdef CONFIG_ISCSI_DMA
+ struct dma_chan *dma_chan; /* DMA channel for copies
*/
+ dma_cookie_t dma_cookie; /* last DMA cookie returned
for this ctask */
+#endif /* CONFIG_ISCSI_DMA */
};
struct iscsi_queue {
@@ -333,4 +342,9 @@ extern int iscsi_verify_itt(struct iscsi_conn *,
struct iscsi_hdr *,
extern void iscsi_pool_free(struct iscsi_queue *, void **);
extern int iscsi_pool_init(struct iscsi_queue *, int, void ***, int);
+/*
+ * DMA functions
+ */
+extern int iscsi_register_dma(void);
+extern void iscsi_unregister_dma(void);
#endif
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"open-iscsi" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at http://groups.google.com/group/open-iscsi
-~----------~----~----~----~------~----~------~--~---