]>
Commit | Line | Data |
---|---|---|
ca47bbd9 | 1 | /* Copyright (c) 2013 Coraid, Inc. See COPYING for GPL terms. */ |
1da177e4 LT |
2 | /* |
3 | * aoenet.c | |
4 | * Ethernet portion of AoE driver | |
5 | */ | |
6 | ||
5a0e3ad6 | 7 | #include <linux/gfp.h> |
1da177e4 LT |
8 | #include <linux/hdreg.h> |
9 | #include <linux/blkdev.h> | |
10 | #include <linux/netdevice.h> | |
03c41c43 | 11 | #include <linux/moduleparam.h> |
e730c155 | 12 | #include <net/net_namespace.h> |
43ecf529 | 13 | #include <asm/unaligned.h> |
1da177e4 LT |
14 | #include "aoe.h" |
15 | ||
16 | #define NECODES 5 | |
17 | ||
18 | static char *aoe_errlist[] = | |
19 | { | |
20 | "no such error", | |
21 | "unrecognized command code", | |
22 | "bad argument parameter", | |
23 | "device unavailable", | |
24 | "config string present", | |
25 | "unsupported version" | |
26 | }; | |
27 | ||
28 | enum { | |
29 | IFLISTSZ = 1024, | |
30 | }; | |
31 | ||
32 | static char aoe_iflist[IFLISTSZ]; | |
03c41c43 | 33 | module_param_string(aoe_iflist, aoe_iflist, IFLISTSZ, 0600); |
4a6c9ee9 | 34 | MODULE_PARM_DESC(aoe_iflist, "aoe_iflist=dev1[,dev2...]"); |
03c41c43 | 35 | |
eb086ec5 EC |
36 | static wait_queue_head_t txwq; |
37 | static struct ktstate kts; | |
38 | ||
03c41c43 EC |
39 | #ifndef MODULE |
40 | static int __init aoe_iflist_setup(char *str) | |
41 | { | |
42 | strncpy(aoe_iflist, str, IFLISTSZ); | |
43 | aoe_iflist[IFLISTSZ - 1] = '\0'; | |
44 | return 1; | |
45 | } | |
46 | ||
47 | __setup("aoe_iflist=", aoe_iflist_setup); | |
48 | #endif | |
1da177e4 | 49 | |
eb086ec5 EC |
50 | static spinlock_t txlock; |
51 | static struct sk_buff_head skbtxq; | |
52 | ||
53 | /* enters with txlock held */ | |
54 | static int | |
8030d343 | 55 | tx(int id) __must_hold(&txlock) |
eb086ec5 EC |
56 | { |
57 | struct sk_buff *skb; | |
4e78dd14 | 58 | struct net_device *ifp; |
eb086ec5 EC |
59 | |
60 | while ((skb = skb_dequeue(&skbtxq))) { | |
61 | spin_unlock_irq(&txlock); | |
4e78dd14 EC |
62 | ifp = skb->dev; |
63 | if (dev_queue_xmit(skb) == NET_XMIT_DROP && net_ratelimit()) | |
64 | pr_warn("aoe: packet could not be sent on %s. %s\n", | |
65 | ifp ? ifp->name : "netif", | |
66 | "consider increasing tx_queue_len"); | |
eb086ec5 EC |
67 | spin_lock_irq(&txlock); |
68 | } | |
69 | return 0; | |
70 | } | |
71 | ||
1da177e4 LT |
72 | int |
73 | is_aoe_netif(struct net_device *ifp) | |
74 | { | |
75 | register char *p, *q; | |
76 | register int len; | |
77 | ||
78 | if (aoe_iflist[0] == '\0') | |
79 | return 1; | |
80 | ||
03c41c43 EC |
81 | p = aoe_iflist + strspn(aoe_iflist, WHITESPACE); |
82 | for (; *p; p = q + strspn(q, WHITESPACE)) { | |
1da177e4 LT |
83 | q = p + strcspn(p, WHITESPACE); |
84 | if (q != p) | |
85 | len = q - p; | |
86 | else | |
87 | len = strlen(p); /* last token in aoe_iflist */ | |
88 | ||
89 | if (strlen(ifp->name) == len && !strncmp(ifp->name, p, len)) | |
90 | return 1; | |
91 | if (q == p) | |
92 | break; | |
93 | } | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
98 | int | |
99 | set_aoe_iflist(const char __user *user_str, size_t size) | |
100 | { | |
101 | if (size >= IFLISTSZ) | |
102 | return -EINVAL; | |
103 | ||
104 | if (copy_from_user(aoe_iflist, user_str, size)) { | |
a12c93f0 | 105 | printk(KERN_INFO "aoe: copy from user failed\n"); |
1da177e4 LT |
106 | return -EFAULT; |
107 | } | |
108 | aoe_iflist[size] = 0x00; | |
109 | return 0; | |
110 | } | |
111 | ||
1da177e4 | 112 | void |
e9bb8fb0 | 113 | aoenet_xmit(struct sk_buff_head *queue) |
1da177e4 | 114 | { |
e9bb8fb0 | 115 | struct sk_buff *skb, *tmp; |
eb086ec5 | 116 | ulong flags; |
1da177e4 | 117 | |
d8779845 DM |
118 | skb_queue_walk_safe(queue, skb, tmp) { |
119 | __skb_unlink(skb, queue); | |
eb086ec5 EC |
120 | spin_lock_irqsave(&txlock, flags); |
121 | skb_queue_tail(&skbtxq, skb); | |
122 | spin_unlock_irqrestore(&txlock, flags); | |
123 | wake_up(&txwq); | |
d8779845 | 124 | } |
1da177e4 LT |
125 | } |
126 | ||
a04b41cd EC |
127 | /* |
128 | * (1) len doesn't include the header by default. I want this. | |
1da177e4 LT |
129 | */ |
130 | static int | |
f2ccd8fa | 131 | aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, struct net_device *orig_dev) |
1da177e4 LT |
132 | { |
133 | struct aoe_hdr *h; | |
3d5b0605 | 134 | struct aoe_atahdr *ah; |
63e9cc5d | 135 | u32 n; |
3d5b0605 | 136 | int sn; |
1da177e4 | 137 | |
c346dca1 | 138 | if (dev_net(ifp) != &init_net) |
e730c155 EB |
139 | goto exit; |
140 | ||
5dc401ee EC |
141 | skb = skb_share_check(skb, GFP_ATOMIC); |
142 | if (skb == NULL) | |
1da177e4 | 143 | return 0; |
1da177e4 LT |
144 | if (!is_aoe_netif(ifp)) |
145 | goto exit; | |
1da177e4 | 146 | skb_push(skb, ETH_HLEN); /* (1) */ |
3d5b0605 EC |
147 | sn = sizeof(*h) + sizeof(*ah); |
148 | if (skb->len >= sn) { | |
149 | sn -= skb_headlen(skb); | |
150 | if (sn > 0 && !__pskb_pull_tail(skb, sn)) | |
151 | goto exit; | |
152 | } | |
153 | h = (struct aoe_hdr *) skb->data; | |
f885f8d1 | 154 | n = get_unaligned_be32(&h->tag); |
1da177e4 LT |
155 | if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31)) |
156 | goto exit; | |
157 | ||
158 | if (h->verfl & AOEFL_ERR) { | |
159 | n = h->err; | |
160 | if (n > NECODES) | |
161 | n = 0; | |
162 | if (net_ratelimit()) | |
68e0d42f EC |
163 | printk(KERN_ERR |
164 | "%s%d.%d@%s; ecode=%d '%s'\n", | |
165 | "aoe: error packet from ", | |
f885f8d1 | 166 | get_unaligned_be16(&h->major), |
68e0d42f EC |
167 | h->minor, skb->dev->name, |
168 | h->err, aoe_errlist[n]); | |
1da177e4 LT |
169 | goto exit; |
170 | } | |
171 | ||
172 | switch (h->cmd) { | |
173 | case AOECMD_ATA: | |
896831f5 EC |
174 | /* ata_rsp may keep skb for later processing or give it back */ |
175 | skb = aoecmd_ata_rsp(skb); | |
1da177e4 LT |
176 | break; |
177 | case AOECMD_CFG: | |
178 | aoecmd_cfg_rsp(skb); | |
179 | break; | |
180 | default: | |
b6d6c517 EC |
181 | if (h->cmd >= AOECMD_VEND_MIN) |
182 | break; /* don't complain about vendor commands */ | |
b21faa25 EC |
183 | pr_info("aoe: unknown AoE command type 0x%02x\n", h->cmd); |
184 | break; | |
1da177e4 | 185 | } |
896831f5 EC |
186 | |
187 | if (!skb) | |
188 | return 0; | |
1da177e4 LT |
189 | exit: |
190 | dev_kfree_skb(skb); | |
191 | return 0; | |
192 | } | |
193 | ||
7546dd97 | 194 | static struct packet_type aoe_pt __read_mostly = { |
1da177e4 LT |
195 | .type = __constant_htons(ETH_P_AOE), |
196 | .func = aoenet_rcv, | |
197 | }; | |
198 | ||
199 | int __init | |
200 | aoenet_init(void) | |
201 | { | |
eb086ec5 EC |
202 | skb_queue_head_init(&skbtxq); |
203 | init_waitqueue_head(&txwq); | |
204 | spin_lock_init(&txlock); | |
205 | kts.lock = &txlock; | |
206 | kts.fn = tx; | |
207 | kts.waitq = &txwq; | |
8030d343 EC |
208 | kts.id = 0; |
209 | snprintf(kts.name, sizeof(kts.name), "aoe_tx%d", kts.id); | |
eb086ec5 EC |
210 | if (aoe_ktstart(&kts)) |
211 | return -EAGAIN; | |
1da177e4 LT |
212 | dev_add_pack(&aoe_pt); |
213 | return 0; | |
214 | } | |
215 | ||
216 | void | |
217 | aoenet_exit(void) | |
218 | { | |
eb086ec5 EC |
219 | aoe_ktstop(&kts); |
220 | skb_queue_purge(&skbtxq); | |
1da177e4 LT |
221 | dev_remove_pack(&aoe_pt); |
222 | } | |
223 |