]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | Subject: cxgb3 - manage private iSCSI IP addresses |
2 | From: Karen Xie <kxie@chelsio.com> | |
3 | References: FATE#304154,bnc#433500 | |
4 | ||
5 | The accelerated iSCSI traffic uses a private IP address unknown to the OS. | |
6 | Create a per port sysfs entry to pass an IP address to the NIC driver, | |
7 | and a control call for the iSCSI driver to grab it. | |
8 | The IP address is required in both drivers to manage ARP requests and connection set up. | |
9 | ||
10 | Signed-off-by: Karen Xie <kxie@chelsio.com> | |
11 | Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com> | |
12 | Signed-off-by: Hannes Reinecke <hare@suse.de> | |
13 | ||
14 | --- | |
15 | --- | |
16 | drivers/net/cxgb3/adapter.h | 1 | |
17 | drivers/net/cxgb3/cxgb3_ctl_defs.h | 9 ++++ | |
18 | drivers/net/cxgb3/cxgb3_main.c | 46 +++++++++++++++++++++++++ | |
19 | drivers/net/cxgb3/cxgb3_offload.c | 45 +++++++++++++++++------- | |
20 | drivers/net/cxgb3/sge.c | 68 ++++++++++++++++++++++++++++++++++--- | |
21 | 5 files changed, 152 insertions(+), 17 deletions(-) | |
22 | ||
23 | --- a/drivers/net/cxgb3/adapter.h | |
24 | +++ b/drivers/net/cxgb3/adapter.h | |
25 | @@ -64,6 +64,7 @@ struct port_info { | |
26 | struct link_config link_config; | |
27 | struct net_device_stats netstats; | |
28 | int activity; | |
29 | + __be32 iscsi_ipv4addr; | |
30 | }; | |
31 | ||
32 | enum { /* adapter flags */ | |
33 | --- a/drivers/net/cxgb3/cxgb3_ctl_defs.h | |
34 | +++ b/drivers/net/cxgb3/cxgb3_ctl_defs.h | |
35 | @@ -57,6 +57,9 @@ enum { | |
36 | RDMA_GET_MIB = 19, | |
37 | ||
38 | GET_RX_PAGE_INFO = 50, | |
39 | + | |
40 | + GET_ISCSI_IPV4ADDR = 51, | |
41 | + SET_ISCSI_IPV4ADDR = 52, | |
42 | }; | |
43 | ||
44 | /* | |
45 | @@ -86,6 +89,12 @@ struct iff_mac { | |
46 | u16 vlan_tag; | |
47 | }; | |
48 | ||
49 | +/* Structure used to request a port's iSCSI IPv4 address */ | |
50 | +struct iscsi_ipv4addr { | |
51 | + struct net_device *dev; /* the net_device */ | |
52 | + __be32 ipv4addr; /* the return iSCSI IPv4 address */ | |
53 | +}; | |
54 | + | |
55 | struct pci_dev; | |
56 | ||
57 | /* | |
58 | --- a/drivers/net/cxgb3/cxgb3_main.c | |
59 | +++ b/drivers/net/cxgb3/cxgb3_main.c | |
60 | @@ -44,6 +44,7 @@ | |
61 | #include <linux/rtnetlink.h> | |
62 | #include <linux/firmware.h> | |
63 | #include <linux/log2.h> | |
64 | +#include <linux/inet.h> | |
65 | #include <asm/uaccess.h> | |
66 | ||
67 | #include "common.h" | |
68 | @@ -688,6 +689,47 @@ static struct attribute *offload_attrs[] | |
69 | ||
70 | static struct attribute_group offload_attr_group = {.attrs = offload_attrs }; | |
71 | ||
72 | +static ssize_t iscsi_ipv4addr_attr_show(struct device *d, char *buf) | |
73 | +{ | |
74 | + struct port_info *pi = netdev_priv(to_net_dev(d)); | |
75 | + | |
76 | + __be32 a = pi->iscsi_ipv4addr; | |
77 | + return sprintf(buf, NIPQUAD_FMT "\n", NIPQUAD(a)); | |
78 | +} | |
79 | + | |
80 | +static ssize_t iscsi_ipv4addr_attr_store(struct device *d, | |
81 | + const char *buf, size_t len) | |
82 | +{ | |
83 | + struct port_info *pi = netdev_priv(to_net_dev(d)); | |
84 | + | |
85 | + pi->iscsi_ipv4addr = in_aton(buf); | |
86 | + return len; | |
87 | +} | |
88 | + | |
89 | +#define ISCSI_IPADDR_ATTR(name) \ | |
90 | +static ssize_t show_##name(struct device *d, struct device_attribute *attr, \ | |
91 | + char *buf) \ | |
92 | +{ \ | |
93 | + return iscsi_ipv4addr_attr_show(d, buf); \ | |
94 | +} \ | |
95 | +static ssize_t store_##name(struct device *d, struct device_attribute *attr, \ | |
96 | + const char *buf, size_t len) \ | |
97 | +{ \ | |
98 | + return iscsi_ipv4addr_attr_store(d, buf, len); \ | |
99 | +} \ | |
100 | +static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_##name, store_##name) | |
101 | + | |
102 | +ISCSI_IPADDR_ATTR(iscsi_ipv4addr); | |
103 | + | |
104 | +static struct attribute *iscsi_offload_attrs[] = { | |
105 | + &dev_attr_iscsi_ipv4addr.attr, | |
106 | + NULL | |
107 | +}; | |
108 | + | |
109 | +static struct attribute_group iscsi_offload_attr_group = { | |
110 | + .attrs = iscsi_offload_attrs | |
111 | +}; | |
112 | + | |
113 | /* | |
114 | * Sends an sk_buff to an offload queue driver | |
115 | * after dealing with any active network taps. | |
116 | @@ -1079,6 +1121,7 @@ static int cxgb_open(struct net_device * | |
117 | if (err) | |
118 | printk(KERN_WARNING | |
119 | "Could not initialize offload capabilities\n"); | |
120 | + sysfs_create_group(&dev->dev.kobj, &iscsi_offload_attr_group); | |
121 | } | |
122 | ||
123 | link_start(dev); | |
124 | @@ -1101,6 +1144,9 @@ static int cxgb_close(struct net_device | |
125 | netif_carrier_off(dev); | |
126 | t3_mac_disable(&pi->mac, MAC_DIRECTION_TX | MAC_DIRECTION_RX); | |
127 | ||
128 | + if (is_offload(adapter) && !ofld_disable) | |
129 | + sysfs_remove_group(&dev->dev.kobj, &iscsi_offload_attr_group); | |
130 | + | |
131 | spin_lock(&adapter->work_lock); /* sync with update task */ | |
132 | clear_bit(pi->port_id, &adapter->open_device_map); | |
133 | spin_unlock(&adapter->work_lock); | |
134 | --- a/drivers/net/cxgb3/cxgb3_offload.c | |
135 | +++ b/drivers/net/cxgb3/cxgb3_offload.c | |
136 | @@ -182,7 +182,9 @@ static struct net_device *get_iff_from_m | |
137 | static int cxgb_ulp_iscsi_ctl(struct adapter *adapter, unsigned int req, | |
138 | void *data) | |
139 | { | |
140 | + int i; | |
141 | int ret = 0; | |
142 | + unsigned int val = 0; | |
143 | struct ulp_iscsi_info *uiip = data; | |
144 | ||
145 | switch (req) { | |
146 | @@ -191,31 +193,36 @@ static int cxgb_ulp_iscsi_ctl(struct ada | |
147 | uiip->llimit = t3_read_reg(adapter, A_ULPRX_ISCSI_LLIMIT); | |
148 | uiip->ulimit = t3_read_reg(adapter, A_ULPRX_ISCSI_ULIMIT); | |
149 | uiip->tagmask = t3_read_reg(adapter, A_ULPRX_ISCSI_TAGMASK); | |
150 | + val = t3_read_reg(adapter, A_ULPRX_ISCSI_PSZ); | |
151 | + for (i = 0; i < 4; i++, val >>= 8) | |
152 | + uiip->pgsz_factor[i] = val & 0xFF; | |
153 | /* | |
154 | * On tx, the iscsi pdu has to be <= tx page size and has to | |
155 | * fit into the Tx PM FIFO. | |
156 | */ | |
157 | uiip->max_txsz = min(adapter->params.tp.tx_pg_size, | |
158 | t3_read_reg(adapter, A_PM1_TX_CFG) >> 17); | |
159 | - /* on rx, the iscsi pdu has to be < rx page size and the | |
160 | - whole pdu + cpl headers has to fit into one sge buffer */ | |
161 | - uiip->max_rxsz = min_t(unsigned int, | |
162 | - adapter->params.tp.rx_pg_size, | |
163 | - (adapter->sge.qs[0].fl[1].buf_size - | |
164 | - sizeof(struct cpl_rx_data) * 2 - | |
165 | - sizeof(struct cpl_rx_data_ddp))); | |
166 | + /* | |
167 | + * on rx, the iscsi pdu has to be < rx page size and the | |
168 | + * the max rx data length programmed in TP | |
169 | + */ | |
170 | + uiip->max_rxsz = min(adapter->params.tp.rx_pg_size, | |
171 | + ((t3_read_reg(adapter, A_TP_PARA_REG2)) | |
172 | + >> S_MAXRXDATA) & M_MAXRXDATA); | |
173 | break; | |
174 | case ULP_ISCSI_SET_PARAMS: | |
175 | t3_write_reg(adapter, A_ULPRX_ISCSI_TAGMASK, uiip->tagmask); | |
176 | /* set MaxRxData and MaxCoalesceSize to 16224 */ | |
177 | t3_write_reg(adapter, A_TP_PARA_REG2, 0x3f603f60); | |
178 | /* program the ddp page sizes */ | |
179 | - { | |
180 | - int i; | |
181 | - unsigned int val = 0; | |
182 | - for (i = 0; i < 4; i++) | |
183 | - val |= (uiip->pgsz_factor[i] & 0xF) << (8 * i); | |
184 | - if (val) | |
185 | + for (val = 0, i = 0; i < 4; i++) | |
186 | + val |= (uiip->pgsz_factor[i] & 0xF) << (8 * i); | |
187 | + if (val && (val != t3_read_reg(adapter, A_ULPRX_ISCSI_PSZ))) { | |
188 | + printk(KERN_INFO | |
189 | + "%s, setting iscsi pgsz 0x%x, %u,%u,%u,%u.\n", | |
190 | + adapter->name, val, uiip->pgsz_factor[0], | |
191 | + uiip->pgsz_factor[1], uiip->pgsz_factor[2], | |
192 | + uiip->pgsz_factor[3]); | |
193 | t3_write_reg(adapter, A_ULPRX_ISCSI_PSZ, val); | |
194 | } | |
195 | break; | |
196 | @@ -407,6 +414,18 @@ static int cxgb_offload_ctl(struct t3cde | |
197 | rx_page_info->page_size = tp->rx_pg_size; | |
198 | rx_page_info->num = tp->rx_num_pgs; | |
199 | break; | |
200 | + case GET_ISCSI_IPV4ADDR: { | |
201 | + struct iscsi_ipv4addr *p = data; | |
202 | + struct port_info *pi = netdev_priv(p->dev); | |
203 | + p->ipv4addr = pi->iscsi_ipv4addr; | |
204 | + break; | |
205 | + } | |
206 | + case SET_ISCSI_IPV4ADDR: { | |
207 | + struct iscsi_ipv4addr *p = data; | |
208 | + struct port_info *pi = netdev_priv(p->dev); | |
209 | + pi->iscsi_ipv4addr = p->ipv4addr; | |
210 | + break; | |
211 | + } | |
212 | default: | |
213 | return -EOPNOTSUPP; | |
214 | } | |
215 | --- a/drivers/net/cxgb3/sge.c | |
216 | +++ b/drivers/net/cxgb3/sge.c | |
217 | @@ -36,6 +36,7 @@ | |
218 | #include <linux/ip.h> | |
219 | #include <linux/tcp.h> | |
220 | #include <linux/dma-mapping.h> | |
221 | +#include <net/arp.h> | |
222 | #include "common.h" | |
223 | #include "regs.h" | |
224 | #include "sge_defs.h" | |
225 | @@ -1856,6 +1857,53 @@ static void restart_tx(struct sge_qset * | |
226 | } | |
227 | ||
228 | /** | |
229 | + * cxgb3_arp_process - process an ARP request probing a private IP address | |
230 | + * @adapter: the adapter | |
231 | + * @skb: the skbuff containing the ARP request | |
232 | + * | |
233 | + * Check if the ARP request is probing the private IP address | |
234 | + * dedicated to iSCSI, generate an ARP reply if so. | |
235 | + */ | |
236 | +static void cxgb3_arp_process(struct adapter *adapter, struct sk_buff *skb) | |
237 | +{ | |
238 | + struct net_device *dev = skb->dev; | |
239 | + struct port_info *pi; | |
240 | + struct arphdr *arp; | |
241 | + unsigned char *arp_ptr; | |
242 | + unsigned char *sha; | |
243 | + __be32 sip, tip; | |
244 | + | |
245 | + if (!dev) | |
246 | + return; | |
247 | + | |
248 | + skb_reset_network_header(skb); | |
249 | + arp = arp_hdr(skb); | |
250 | + | |
251 | + if (arp->ar_op != htons(ARPOP_REQUEST)) | |
252 | + return; | |
253 | + | |
254 | + arp_ptr = (unsigned char *)(arp + 1); | |
255 | + sha = arp_ptr; | |
256 | + arp_ptr += dev->addr_len; | |
257 | + memcpy(&sip, arp_ptr, sizeof(sip)); | |
258 | + arp_ptr += sizeof(sip); | |
259 | + arp_ptr += dev->addr_len; | |
260 | + memcpy(&tip, arp_ptr, sizeof(tip)); | |
261 | + | |
262 | + pi = netdev_priv(dev); | |
263 | + if (tip != pi->iscsi_ipv4addr) | |
264 | + return; | |
265 | + | |
266 | + arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha, | |
267 | + dev->dev_addr, sha); | |
268 | +} | |
269 | + | |
270 | +static inline int is_arp(struct sk_buff *skb) | |
271 | +{ | |
272 | + return skb->protocol == htons(ETH_P_ARP); | |
273 | +} | |
274 | + | |
275 | +/** | |
276 | * rx_eth - process an ingress ethernet packet | |
277 | * @adap: the adapter | |
278 | * @rq: the response queue that received the packet | |
279 | @@ -1879,7 +1927,7 @@ static void rx_eth(struct adapter *adap, | |
280 | pi = netdev_priv(skb->dev); | |
281 | if (pi->rx_csum_offload && p->csum_valid && p->csum == htons(0xffff) && | |
282 | !p->fragment) { | |
283 | - rspq_to_qset(rq)->port_stats[SGE_PSTAT_RX_CSUM_GOOD]++; | |
284 | + qs->port_stats[SGE_PSTAT_RX_CSUM_GOOD]++; | |
285 | skb->ip_summed = CHECKSUM_UNNECESSARY; | |
286 | } else | |
287 | skb->ip_summed = CHECKSUM_NONE; | |
288 | @@ -1894,16 +1942,28 @@ static void rx_eth(struct adapter *adap, | |
289 | grp, | |
290 | ntohs(p->vlan), | |
291 | p); | |
292 | - else | |
293 | + else { | |
294 | + if (unlikely(pi->iscsi_ipv4addr && | |
295 | + is_arp(skb))) { | |
296 | + unsigned short vtag = ntohs(p->vlan) & | |
297 | + VLAN_VID_MASK; | |
298 | + skb->dev = vlan_group_get_device(grp, | |
299 | + vtag); | |
300 | + cxgb3_arp_process(adap, skb); | |
301 | + } | |
302 | __vlan_hwaccel_rx(skb, grp, ntohs(p->vlan), | |
303 | - rq->polling); | |
304 | + rq->polling); | |
305 | + } | |
306 | else | |
307 | dev_kfree_skb_any(skb); | |
308 | } else if (rq->polling) { | |
309 | if (lro) | |
310 | lro_receive_skb(&qs->lro_mgr, skb, p); | |
311 | - else | |
312 | + else { | |
313 | + if (unlikely(pi->iscsi_ipv4addr && is_arp(skb))) | |
314 | + cxgb3_arp_process(adap, skb); | |
315 | netif_receive_skb(skb); | |
316 | + } | |
317 | } else | |
318 | netif_rx(skb); | |
319 | } |