]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | From: Karen Xie <kxie@chelsio.com> |
2 | Subject: cxgb3i - added pdu size check and fixed ddp page setup | |
3 | References: bnc#449519 | |
4 | ||
5 | - Added documentation for cxgb3i driver, decribing how to use it to offload | |
6 | open-iscsi initaitor. | |
7 | - limit the transmit pdu size to be 4K aligned. | |
8 | - added check for pdu size, if the negotiated pdu size if too big, print error | |
9 | message to dmesg, and close the connection. | |
10 | - fixed ddp page size setup when both header and data digests are off. | |
11 | ||
12 | Signed-off-by: Karen Xie <kxie@chelsio.com> | |
13 | Acked-by: John Jolly <jjolly@novell.com> | |
14 | --- | |
15 | ||
16 | diff -uNr linux-2.6.27.7-4.orig/Documentation/scsi/cxgb3i.txt linux-2.6.27.7-4.new/Documentation/scsi/cxgb3i.txt | |
17 | --- linux-2.6.27.7-4.orig/Documentation/scsi/cxgb3i.txt 1969-12-31 18:00:00.000000000 -0600 | |
18 | +++ linux-2.6.27.7-4.new/Documentation/scsi/cxgb3i.txt 2008-12-01 18:32:21.000000000 -0600 | |
19 | @@ -0,0 +1,51 @@ | |
20 | +Chelsio S3 iSCSI Driver for Linux | |
21 | + | |
22 | +Introduction | |
23 | +============ | |
24 | + | |
25 | +The Chelsio T3 ASIC based Adapters (S310, S320, S302, S304, Mezz cards, etc. | |
26 | +series of products) supports iSCSI acceleration and iSCSI Direct Data Placement | |
27 | +(DDP) where the hardware handles the expensive byte touching operations, such | |
28 | +as CRC computation and verification, and direct DMA to the final host memory | |
29 | +destination. | |
30 | + | |
31 | +The cxgb3i driver interfaces with open-iscsi initiator and provides the iSCSI | |
32 | +acceleration through Chelsio hardware wherever applicable. | |
33 | + | |
34 | +Using the cxgb3i Driver | |
35 | +======================= | |
36 | + | |
37 | +The following steps need to be taken to accelerates the open-iscsi initiator: | |
38 | + | |
39 | +1. a new transport class "cxgb3i" need to be specified. This is done by | |
40 | + creating a new interface file in /etc/iscsi/ifaces. | |
41 | + | |
42 | + The content of the file should be in the following format: | |
43 | + iface.transport_name = cxgb3i | |
44 | + iface.net_ifacename = <ethX> | |
45 | + iface.ipaddress = <iscsi ip address> | |
46 | + | |
47 | + * iface.ipaddress is optional, <iscsi ip address> can be either the same as | |
48 | + the ethX's ip address or an address on the same subnet. Make sure the | |
49 | + address is unique in the network. | |
50 | + | |
51 | +2. edit /etc/iscsi/iscsid.conf | |
52 | + The default setting for MaxRecvDataSegmentLength (131072) is too big, search | |
53 | + and replace all occurances of "xxx.iscsi.MaxRecvDataSegmentLength" to be a | |
54 | + smaller value (8192 is recommended): | |
55 | + | |
56 | + discovery.sendtargets.iscsi.MaxRecvDataSegmentLength = 8192 | |
57 | + node.conn[0].iscsi.MaxRecvDataSegmentLength = 8192 | |
58 | + | |
59 | +3. Load the cxgb3i driver: "modprobe cxgb3i" | |
60 | + | |
61 | + * in the case of recompiling the kernel, the cxgb3i selection is located at | |
62 | + Device Drivers | |
63 | + SCSI device support ---> | |
64 | + [*] SCSI low-level drivers ---> | |
65 | + <M> Chelsio S3xx iSCSI support | |
66 | + | |
67 | +4. To direct open-iscsi traffic to go through cxgb3i's accelerated path, | |
68 | + "-I <iface file name>" option needs to be specified with most of the | |
69 | + iscsiadm command. <iface file name> is the transport interface file created | |
70 | + in step 1. | |
71 | diff -uNr linux-2.6.27.7-4.orig/drivers/scsi/cxgb3i/cxgb3i_iscsi.c linux-2.6.27.7-4.new/drivers/scsi/cxgb3i/cxgb3i_iscsi.c | |
72 | --- linux-2.6.27.7-4.orig/drivers/scsi/cxgb3i/cxgb3i_iscsi.c 2008-11-25 09:16:10.000000000 -0600 | |
73 | +++ linux-2.6.27.7-4.new/drivers/scsi/cxgb3i/cxgb3i_iscsi.c 2008-12-01 18:32:55.000000000 -0600 | |
74 | @@ -22,6 +22,12 @@ | |
75 | #include <scsi/scsi_transport_iscsi.h> | |
76 | ||
77 | #include "cxgb3i.h" | |
78 | +#include "cxgb3i_ulp2.h" | |
79 | + | |
80 | +#define align_to_4k_boundary(n) \ | |
81 | + do { \ | |
82 | + n = (n) & ~((1 << 12) - 1); \ | |
83 | + } while(0) | |
84 | ||
85 | static struct scsi_transport_template *cxgb3i_scsi_transport; | |
86 | static struct scsi_host_template cxgb3i_host_template; | |
87 | @@ -370,36 +376,59 @@ | |
88 | * | |
89 | * Creates a new iSCSI connection instance for a given session | |
90 | */ | |
91 | -static inline void cxgb3i_conn_max_xmit_dlength(struct iscsi_conn *conn) | |
92 | +static inline int cxgb3i_conn_max_xmit_dlength(struct iscsi_conn *conn) | |
93 | { | |
94 | struct cxgb3i_conn *cconn = conn->dd_data; | |
95 | + unsigned int max = min_t(unsigned int, ULP2_MAX_PDU_SIZE, | |
96 | + cconn->hba->snic->tx_max_size - | |
97 | + ISCSI_PDU_HEADER_MAX); | |
98 | + | |
99 | + cxgb3i_log_debug("conn 0x%p, max xmit %u.\n", | |
100 | + conn, conn->max_xmit_dlength); | |
101 | ||
102 | if (conn->max_xmit_dlength) | |
103 | conn->max_xmit_dlength = min_t(unsigned int, | |
104 | - conn->max_xmit_dlength, | |
105 | - cconn->hba->snic->tx_max_size - | |
106 | - ISCSI_PDU_HEADER_MAX); | |
107 | + conn->max_xmit_dlength, max); | |
108 | else | |
109 | - conn->max_xmit_dlength = cconn->hba->snic->tx_max_size - | |
110 | - ISCSI_PDU_HEADER_MAX; | |
111 | - cxgb3i_log_debug("conn 0x%p, max xmit %u.\n", | |
112 | + conn->max_xmit_dlength = max; | |
113 | + | |
114 | + align_to_4k_boundary(conn->max_xmit_dlength); | |
115 | + | |
116 | + cxgb3i_log_debug("conn 0x%p, set max xmit %u.\n", | |
117 | conn, conn->max_xmit_dlength); | |
118 | + | |
119 | + return 0; | |
120 | } | |
121 | ||
122 | -static inline void cxgb3i_conn_max_recv_dlength(struct iscsi_conn *conn) | |
123 | +static inline int cxgb3i_conn_max_recv_dlength(struct iscsi_conn *conn) | |
124 | { | |
125 | struct cxgb3i_conn *cconn = conn->dd_data; | |
126 | + unsigned int max = min_t(unsigned int, ULP2_MAX_PDU_SIZE, | |
127 | + cconn->hba->snic->rx_max_size - | |
128 | + ISCSI_PDU_HEADER_MAX); | |
129 | ||
130 | - if (conn->max_recv_dlength) | |
131 | - conn->max_recv_dlength = min_t(unsigned int, | |
132 | - conn->max_recv_dlength, | |
133 | - cconn->hba->snic->rx_max_size - | |
134 | - ISCSI_PDU_HEADER_MAX); | |
135 | - else | |
136 | - conn->max_recv_dlength = cconn->hba->snic->rx_max_size - | |
137 | - ISCSI_PDU_HEADER_MAX; | |
138 | cxgb3i_log_debug("conn 0x%p, max recv %u.\n", | |
139 | conn, conn->max_recv_dlength); | |
140 | + | |
141 | + align_to_4k_boundary(max); | |
142 | + | |
143 | + if (conn->max_recv_dlength) { | |
144 | + if (conn->max_recv_dlength > max) { | |
145 | + cxgb3i_log_error("MaxRecvDataSegmentLength %u, not supported." | |
146 | + "Need to be <= %u.\n", | |
147 | + conn->max_recv_dlength, max); | |
148 | + return -EINVAL; | |
149 | + } | |
150 | + conn->max_recv_dlength = min_t(unsigned int, | |
151 | + conn->max_recv_dlength, max); | |
152 | + align_to_4k_boundary(conn->max_recv_dlength); | |
153 | + } else | |
154 | + conn->max_recv_dlength = max; | |
155 | + | |
156 | + cxgb3i_log_debug("conn 0x%p, set max recv %u.\n", | |
157 | + conn, conn->max_recv_dlength); | |
158 | + | |
159 | + return 0; | |
160 | } | |
161 | ||
162 | static struct iscsi_cls_conn *cxgb3i_conn_create(struct iscsi_cls_session | |
163 | @@ -546,13 +575,13 @@ | |
164 | switch (param) { | |
165 | case ISCSI_PARAM_HDRDGST_EN: | |
166 | err = iscsi_set_param(cls_conn, param, buf, buflen); | |
167 | - if (!err && conn->hdrdgst_en) | |
168 | + if (!err) | |
169 | cxgb3i_conn_ulp_setup(cconn, conn->hdrdgst_en, | |
170 | conn->datadgst_en); | |
171 | break; | |
172 | case ISCSI_PARAM_DATADGST_EN: | |
173 | err = iscsi_set_param(cls_conn, param, buf, buflen); | |
174 | - if (!err && conn->datadgst_en) | |
175 | + if (!err) | |
176 | cxgb3i_conn_ulp_setup(cconn, conn->hdrdgst_en, | |
177 | conn->datadgst_en); | |
178 | break; | |
179 | @@ -568,11 +597,11 @@ | |
180 | return -ENOMEM; | |
181 | case ISCSI_PARAM_MAX_RECV_DLENGTH: | |
182 | err = iscsi_set_param(cls_conn, param, buf, buflen); | |
183 | - cxgb3i_conn_max_recv_dlength(conn); | |
184 | + err = cxgb3i_conn_max_recv_dlength(conn); | |
185 | break; | |
186 | case ISCSI_PARAM_MAX_XMIT_DLENGTH: | |
187 | err = iscsi_set_param(cls_conn, param, buf, buflen); | |
188 | - cxgb3i_conn_max_xmit_dlength(conn); | |
189 | + err = cxgb3i_conn_max_xmit_dlength(conn); | |
190 | break; | |
191 | default: | |
192 | return iscsi_set_param(cls_conn, param, buf, buflen); | |
193 | diff -uNr linux-2.6.27.7-4.orig/drivers/scsi/cxgb3i/cxgb3i_offload.c linux-2.6.27.7-4.new/drivers/scsi/cxgb3i/cxgb3i_offload.c | |
194 | --- linux-2.6.27.7-4.orig/drivers/scsi/cxgb3i/cxgb3i_offload.c 2008-11-25 09:16:10.000000000 -0600 | |
195 | +++ linux-2.6.27.7-4.new/drivers/scsi/cxgb3i/cxgb3i_offload.c 2008-12-01 18:32:55.000000000 -0600 | |
196 | @@ -45,6 +45,12 @@ | |
197 | #define c3cn_tx_debug(fmt...) | |
198 | #endif | |
199 | ||
200 | +#ifdef __DEBUG_C3CN_RX__ | |
201 | +#define c3cn_rx_debug cxgb3i_log_debug | |
202 | +#else | |
203 | +#define c3cn_rx_debug(fmt...) | |
204 | +#endif | |
205 | + | |
206 | /* connection flags */ | |
207 | static inline void c3cn_set_flag(struct s3_conn *c3cn, enum c3cn_flags flag) | |
208 | { | |
209 | @@ -1577,6 +1583,9 @@ | |
210 | skb_ulp_ddigest(skb) = ntohl(ddp_cpl.ulp_crc); | |
211 | status = ntohl(ddp_cpl.ddp_status); | |
212 | ||
213 | + c3cn_rx_debug("skb 0x%p, len %u, pdulen %u, ddp status 0x%x.\n", | |
214 | + skb, skb->len, skb_ulp_pdulen(skb), status); | |
215 | + | |
216 | if (status & (1 << RX_DDP_STATUS_HCRC_SHIFT)) | |
217 | skb_ulp_mode(skb) |= ULP2_FLAG_HCRC_ERROR; | |
218 | if (status & (1 << RX_DDP_STATUS_DCRC_SHIFT)) | |
219 | diff -uNr linux-2.6.27.7-4.orig/drivers/scsi/cxgb3i/cxgb3i_ulp2.c linux-2.6.27.7-4.new/drivers/scsi/cxgb3i/cxgb3i_ulp2.c | |
220 | --- linux-2.6.27.7-4.orig/drivers/scsi/cxgb3i/cxgb3i_ulp2.c 2008-11-25 09:16:10.000000000 -0600 | |
221 | +++ linux-2.6.27.7-4.new/drivers/scsi/cxgb3i/cxgb3i_ulp2.c 2008-12-01 18:32:55.000000000 -0600 | |
222 | @@ -145,6 +145,9 @@ | |
223 | PAGE_SHIFT; | |
224 | int i = 1, j = 0; | |
225 | ||
226 | + if (sgoffset) | |
227 | + return NULL; | |
228 | + | |
229 | gl = kzalloc(sizeof(struct cxgb3i_gather_list) + | |
230 | npages * (sizeof(dma_addr_t) + sizeof(struct page *)), | |
231 | gfp); | |
232 | @@ -173,7 +176,7 @@ | |
233 | goto error_out; | |
234 | ||
235 | j++; | |
236 | - if (j == gl->nelem) | |
237 | + if (j == gl->nelem || sg->offset) | |
238 | goto error_out; | |
239 | gl->pages[j] = page; | |
240 | sglen = sg->length; | |
241 | @@ -303,7 +306,8 @@ | |
242 | int idx = -1, idx_max; | |
243 | u32 tag; | |
244 | ||
245 | - if (page_idx || !ddp || !sgcnt || xferlen < ULP2_DDP_THRESHOLD) { | |
246 | + if (page_idx >= ULP2_PGIDX_MAX || !ddp || !sgcnt || | |
247 | + xferlen < ULP2_DDP_THRESHOLD) { | |
248 | cxgb3i_tag_debug("pgidx %u, sgcnt %u, xfer %u/%u, NO ddp.\n", | |
249 | page_idx, sgcnt, xferlen, ULP2_DDP_THRESHOLD); | |
250 | return RESERVED_ITT; | |
251 | @@ -330,10 +334,9 @@ | |
252 | npods, gl); | |
253 | } | |
254 | if (idx < 0) { | |
255 | - kfree(gl); | |
256 | cxgb3i_tag_debug("sgcnt %u, xferlen %u, npods %u NO DDP.\n", | |
257 | sgcnt, xferlen, npods); | |
258 | - return RESERVED_ITT; | |
259 | + goto free_gl; | |
260 | } | |
261 | ||
262 | if (ddp_alloc_gl_skb(ddp, idx, npods, gfp) < 0) | |
263 | @@ -341,7 +344,7 @@ | |
264 | ||
265 | if (ddp_gl_map(snic->pdev, gl) < 0) | |
266 | goto unmap_sgl; | |
267 | - | |
268 | + | |
269 | tag = sw_tag | (idx << snic->tag_format.rsvd_shift); | |
270 | ||
271 | hdr.rsvd = 0; | |
272 | @@ -363,6 +366,8 @@ | |
273 | ddp_free_gl_skb(ddp, idx, npods); | |
274 | unmark_entries: | |
275 | ddp_unmark_entries(ddp, idx, npods); | |
276 | +free_gl: | |
277 | + kfree(gl); | |
278 | return RESERVED_ITT; | |
279 | } | |
280 | ||
281 | @@ -393,8 +398,10 @@ | |
282 | struct sk_buff *skb = alloc_skb(sizeof(struct cpl_set_tcb_field), | |
283 | GFP_KERNEL | __GFP_NOFAIL); | |
284 | struct cpl_set_tcb_field *req; | |
285 | - u32 submode = (hcrc ? 1 : 0) | (dcrc ? 2 : 0); | |
286 | - u32 pgcode = page_idx < ULP2_PGIDX_MAX ? page_idx : 0; | |
287 | + u64 val = (hcrc ? 1 : 0) | (dcrc ? 2 : 0); | |
288 | + | |
289 | + if (page_idx < ULP2_PGIDX_MAX) | |
290 | + val |= page_idx << 4; | |
291 | ||
292 | /* set up ulp submode and page size */ | |
293 | req = (struct cpl_set_tcb_field *)skb_put(skb, sizeof(*req)); | |
294 | @@ -404,7 +411,7 @@ | |
295 | req->cpu_idx = 0; | |
296 | req->word = htons(31); | |
297 | req->mask = cpu_to_be64(0xFF000000); | |
298 | - req->val = cpu_to_be64(((pgcode << 4) | submode) << 24); | |
299 | + req->val = cpu_to_be64(val << 24); | |
300 | skb->priority = CPL_PRIORITY_CONTROL; | |
301 | ||
302 | cxgb3_ofld_send(c3cn->cdev, skb); | |
303 | @@ -461,12 +468,16 @@ | |
304 | segment->status = (conn->datadgst_en && | |
305 | (skb_ulp_mode(skb) & ULP2_FLAG_DCRC_ERROR)) ? | |
306 | ISCSI_SEGMENT_DGST_ERR : 0; | |
307 | - if (skb_ulp_mode(skb) & ULP2_FLAG_DATA_DDPED) | |
308 | + if (skb_ulp_mode(skb) & ULP2_FLAG_DATA_DDPED) { | |
309 | + cxgb3i_ddp_debug("skb 0x%p, opcode 0x%x, data %u, ddp'ed, " | |
310 | + "itt 0x%x.\n", | |
311 | + skb, hdr->opcode & ISCSI_OPCODE_MASK, | |
312 | + tcp_conn->in.datalen, hdr->itt); | |
313 | segment->total_copied = segment->total_size; | |
314 | - else { | |
315 | - cxgb3i_ddp_debug("opcode 0x%x, data %u, NOT ddp'ed, " | |
316 | + } else { | |
317 | + cxgb3i_ddp_debug("skb 0x%p, opcode 0x%x, data %u, not ddp'ed, " | |
318 | "itt 0x%x.\n", | |
319 | - hdr->opcode & ISCSI_OPCODE_MASK, | |
320 | + skb, hdr->opcode & ISCSI_OPCODE_MASK, | |
321 | tcp_conn->in.datalen, hdr->itt); | |
322 | offset += sizeof(struct cpl_iscsi_hdr_norss); | |
323 | } | |
324 | diff -uNr linux-2.6.27.7-4.orig/drivers/scsi/cxgb3i/cxgb3i_ulp2.h linux-2.6.27.7-4.new/drivers/scsi/cxgb3i/cxgb3i_ulp2.h | |
325 | --- linux-2.6.27.7-4.orig/drivers/scsi/cxgb3i/cxgb3i_ulp2.h 2008-11-25 09:16:10.000000000 -0600 | |
326 | +++ linux-2.6.27.7-4.new/drivers/scsi/cxgb3i/cxgb3i_ulp2.h 2008-12-01 18:32:55.000000000 -0600 | |
327 | @@ -101,6 +101,7 @@ | |
328 | #define ULP2_FLAG_PAD_ERROR 0x40 | |
329 | ||
330 | #define ULP2_MAX_PKT_SIZE 16224 | |
331 | +#define ULP2_MAX_PDU_SIZE 8192 | |
332 | ||
333 | void cxgb3i_conn_closing(struct s3_conn *); | |
334 | void cxgb3i_conn_pdu_ready(struct s3_conn *c3cn); |