Commit | Line | Data |
---|---|---|
fea05a26 | 1 | /* Copyright (c) 2012 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); |
61a2d07d | 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 | |
55 | tx(void) | |
56 | { | |
57 | struct sk_buff *skb; | |
58 | ||
59 | while ((skb = skb_dequeue(&skbtxq))) { | |
60 | spin_unlock_irq(&txlock); | |
61 | dev_queue_xmit(skb); | |
62 | spin_lock_irq(&txlock); | |
63 | } | |
64 | return 0; | |
65 | } | |
66 | ||
1da177e4 LT |
67 | int |
68 | is_aoe_netif(struct net_device *ifp) | |
69 | { | |
70 | register char *p, *q; | |
71 | register int len; | |
72 | ||
73 | if (aoe_iflist[0] == '\0') | |
74 | return 1; | |
75 | ||
03c41c43 EC |
76 | p = aoe_iflist + strspn(aoe_iflist, WHITESPACE); |
77 | for (; *p; p = q + strspn(q, WHITESPACE)) { | |
1da177e4 LT |
78 | q = p + strcspn(p, WHITESPACE); |
79 | if (q != p) | |
80 | len = q - p; | |
81 | else | |
82 | len = strlen(p); /* last token in aoe_iflist */ | |
83 | ||
84 | if (strlen(ifp->name) == len && !strncmp(ifp->name, p, len)) | |
85 | return 1; | |
86 | if (q == p) | |
87 | break; | |
88 | } | |
89 | ||
90 | return 0; | |
91 | } | |
92 | ||
93 | int | |
94 | set_aoe_iflist(const char __user *user_str, size_t size) | |
95 | { | |
96 | if (size >= IFLISTSZ) | |
97 | return -EINVAL; | |
98 | ||
99 | if (copy_from_user(aoe_iflist, user_str, size)) { | |
a12c93f0 | 100 | printk(KERN_INFO "aoe: copy from user failed\n"); |
1da177e4 LT |
101 | return -EFAULT; |
102 | } | |
103 | aoe_iflist[size] = 0x00; | |
104 | return 0; | |
105 | } | |
106 | ||
1da177e4 | 107 | void |
e9bb8fb0 | 108 | aoenet_xmit(struct sk_buff_head *queue) |
1da177e4 | 109 | { |
e9bb8fb0 | 110 | struct sk_buff *skb, *tmp; |
eb086ec5 | 111 | ulong flags; |
1da177e4 | 112 | |
d8779845 DM |
113 | skb_queue_walk_safe(queue, skb, tmp) { |
114 | __skb_unlink(skb, queue); | |
eb086ec5 EC |
115 | spin_lock_irqsave(&txlock, flags); |
116 | skb_queue_tail(&skbtxq, skb); | |
117 | spin_unlock_irqrestore(&txlock, flags); | |
118 | wake_up(&txwq); | |
d8779845 | 119 | } |
1da177e4 LT |
120 | } |
121 | ||
122 | /* | |
123 | * (1) len doesn't include the header by default. I want this. | |
124 | */ | |
125 | static int | |
f2ccd8fa | 126 | aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, struct net_device *orig_dev) |
1da177e4 LT |
127 | { |
128 | struct aoe_hdr *h; | |
3d5b0605 | 129 | struct aoe_atahdr *ah; |
63e9cc5d | 130 | u32 n; |
3d5b0605 | 131 | int sn; |
1da177e4 | 132 | |
c346dca1 | 133 | if (dev_net(ifp) != &init_net) |
e730c155 EB |
134 | goto exit; |
135 | ||
5dc401ee EC |
136 | skb = skb_share_check(skb, GFP_ATOMIC); |
137 | if (skb == NULL) | |
1da177e4 | 138 | return 0; |
1da177e4 LT |
139 | if (!is_aoe_netif(ifp)) |
140 | goto exit; | |
1da177e4 | 141 | skb_push(skb, ETH_HLEN); /* (1) */ |
3d5b0605 EC |
142 | sn = sizeof(*h) + sizeof(*ah); |
143 | if (skb->len >= sn) { | |
144 | sn -= skb_headlen(skb); | |
145 | if (sn > 0 && !__pskb_pull_tail(skb, sn)) | |
146 | goto exit; | |
147 | } | |
148 | h = (struct aoe_hdr *) skb->data; | |
f885f8d1 | 149 | n = get_unaligned_be32(&h->tag); |
1da177e4 LT |
150 | if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31)) |
151 | goto exit; | |
152 | ||
153 | if (h->verfl & AOEFL_ERR) { | |
154 | n = h->err; | |
155 | if (n > NECODES) | |
156 | n = 0; | |
157 | if (net_ratelimit()) | |
68e0d42f EC |
158 | printk(KERN_ERR |
159 | "%s%d.%d@%s; ecode=%d '%s'\n", | |
160 | "aoe: error packet from ", | |
f885f8d1 | 161 | get_unaligned_be16(&h->major), |
68e0d42f EC |
162 | h->minor, skb->dev->name, |
163 | h->err, aoe_errlist[n]); | |
1da177e4 LT |
164 | goto exit; |
165 | } | |
166 | ||
167 | switch (h->cmd) { | |
168 | case AOECMD_ATA: | |
896831f5 EC |
169 | /* ata_rsp may keep skb for later processing or give it back */ |
170 | skb = aoecmd_ata_rsp(skb); | |
1da177e4 LT |
171 | break; |
172 | case AOECMD_CFG: | |
173 | aoecmd_cfg_rsp(skb); | |
174 | break; | |
175 | default: | |
b6d6c517 EC |
176 | if (h->cmd >= AOECMD_VEND_MIN) |
177 | break; /* don't complain about vendor commands */ | |
b21faa25 EC |
178 | pr_info("aoe: unknown AoE command type 0x%02x\n", h->cmd); |
179 | break; | |
1da177e4 | 180 | } |
896831f5 EC |
181 | |
182 | if (!skb) | |
183 | return 0; | |
1da177e4 LT |
184 | exit: |
185 | dev_kfree_skb(skb); | |
186 | return 0; | |
187 | } | |
188 | ||
7546dd97 | 189 | static struct packet_type aoe_pt __read_mostly = { |
1da177e4 LT |
190 | .type = __constant_htons(ETH_P_AOE), |
191 | .func = aoenet_rcv, | |
192 | }; | |
193 | ||
194 | int __init | |
195 | aoenet_init(void) | |
196 | { | |
eb086ec5 EC |
197 | skb_queue_head_init(&skbtxq); |
198 | init_waitqueue_head(&txwq); | |
199 | spin_lock_init(&txlock); | |
200 | kts.lock = &txlock; | |
201 | kts.fn = tx; | |
202 | kts.waitq = &txwq; | |
203 | kts.name = "aoe_tx"; | |
204 | if (aoe_ktstart(&kts)) | |
205 | return -EAGAIN; | |
1da177e4 LT |
206 | dev_add_pack(&aoe_pt); |
207 | return 0; | |
208 | } | |
209 | ||
210 | void | |
211 | aoenet_exit(void) | |
212 | { | |
eb086ec5 EC |
213 | aoe_ktstop(&kts); |
214 | skb_queue_purge(&skbtxq); | |
1da177e4 LT |
215 | dev_remove_pack(&aoe_pt); |
216 | } | |
217 |