]>
Commit | Line | Data |
---|---|---|
52e112b3 | 1 | /* Copyright (c) 2007 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 EC |
35 | |
36 | #ifndef MODULE | |
37 | static int __init aoe_iflist_setup(char *str) | |
38 | { | |
39 | strncpy(aoe_iflist, str, IFLISTSZ); | |
40 | aoe_iflist[IFLISTSZ - 1] = '\0'; | |
41 | return 1; | |
42 | } | |
43 | ||
44 | __setup("aoe_iflist=", aoe_iflist_setup); | |
45 | #endif | |
1da177e4 LT |
46 | |
47 | int | |
48 | is_aoe_netif(struct net_device *ifp) | |
49 | { | |
50 | register char *p, *q; | |
51 | register int len; | |
52 | ||
53 | if (aoe_iflist[0] == '\0') | |
54 | return 1; | |
55 | ||
03c41c43 EC |
56 | p = aoe_iflist + strspn(aoe_iflist, WHITESPACE); |
57 | for (; *p; p = q + strspn(q, WHITESPACE)) { | |
1da177e4 LT |
58 | q = p + strcspn(p, WHITESPACE); |
59 | if (q != p) | |
60 | len = q - p; | |
61 | else | |
62 | len = strlen(p); /* last token in aoe_iflist */ | |
63 | ||
64 | if (strlen(ifp->name) == len && !strncmp(ifp->name, p, len)) | |
65 | return 1; | |
66 | if (q == p) | |
67 | break; | |
68 | } | |
69 | ||
70 | return 0; | |
71 | } | |
72 | ||
73 | int | |
74 | set_aoe_iflist(const char __user *user_str, size_t size) | |
75 | { | |
76 | if (size >= IFLISTSZ) | |
77 | return -EINVAL; | |
78 | ||
79 | if (copy_from_user(aoe_iflist, user_str, size)) { | |
a12c93f0 | 80 | printk(KERN_INFO "aoe: copy from user failed\n"); |
1da177e4 LT |
81 | return -EFAULT; |
82 | } | |
83 | aoe_iflist[size] = 0x00; | |
84 | return 0; | |
85 | } | |
86 | ||
1da177e4 | 87 | void |
e9bb8fb0 | 88 | aoenet_xmit(struct sk_buff_head *queue) |
1da177e4 | 89 | { |
e9bb8fb0 | 90 | struct sk_buff *skb, *tmp; |
1da177e4 | 91 | |
d8779845 DM |
92 | skb_queue_walk_safe(queue, skb, tmp) { |
93 | __skb_unlink(skb, queue); | |
1da177e4 | 94 | dev_queue_xmit(skb); |
d8779845 | 95 | } |
1da177e4 LT |
96 | } |
97 | ||
98 | /* | |
99 | * (1) len doesn't include the header by default. I want this. | |
100 | */ | |
101 | static int | |
f2ccd8fa | 102 | aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, struct net_device *orig_dev) |
1da177e4 LT |
103 | { |
104 | struct aoe_hdr *h; | |
3d5b0605 | 105 | struct aoe_atahdr *ah; |
63e9cc5d | 106 | u32 n; |
3d5b0605 | 107 | int sn; |
1da177e4 | 108 | |
c346dca1 | 109 | if (dev_net(ifp) != &init_net) |
e730c155 EB |
110 | goto exit; |
111 | ||
5dc401ee EC |
112 | skb = skb_share_check(skb, GFP_ATOMIC); |
113 | if (skb == NULL) | |
1da177e4 | 114 | return 0; |
1da177e4 LT |
115 | if (!is_aoe_netif(ifp)) |
116 | goto exit; | |
1da177e4 | 117 | skb_push(skb, ETH_HLEN); /* (1) */ |
3d5b0605 EC |
118 | sn = sizeof(*h) + sizeof(*ah); |
119 | if (skb->len >= sn) { | |
120 | sn -= skb_headlen(skb); | |
121 | if (sn > 0 && !__pskb_pull_tail(skb, sn)) | |
122 | goto exit; | |
123 | } | |
124 | h = (struct aoe_hdr *) skb->data; | |
f885f8d1 | 125 | n = get_unaligned_be32(&h->tag); |
1da177e4 LT |
126 | if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31)) |
127 | goto exit; | |
128 | ||
129 | if (h->verfl & AOEFL_ERR) { | |
130 | n = h->err; | |
131 | if (n > NECODES) | |
132 | n = 0; | |
133 | if (net_ratelimit()) | |
68e0d42f EC |
134 | printk(KERN_ERR |
135 | "%s%d.%d@%s; ecode=%d '%s'\n", | |
136 | "aoe: error packet from ", | |
f885f8d1 | 137 | get_unaligned_be16(&h->major), |
68e0d42f EC |
138 | h->minor, skb->dev->name, |
139 | h->err, aoe_errlist[n]); | |
1da177e4 LT |
140 | goto exit; |
141 | } | |
142 | ||
143 | switch (h->cmd) { | |
144 | case AOECMD_ATA: | |
896831f5 EC |
145 | /* ata_rsp may keep skb for later processing or give it back */ |
146 | skb = aoecmd_ata_rsp(skb); | |
1da177e4 LT |
147 | break; |
148 | case AOECMD_CFG: | |
149 | aoecmd_cfg_rsp(skb); | |
150 | break; | |
151 | default: | |
b6d6c517 EC |
152 | if (h->cmd >= AOECMD_VEND_MIN) |
153 | break; /* don't complain about vendor commands */ | |
a12c93f0 | 154 | printk(KERN_INFO "aoe: unknown cmd %d\n", h->cmd); |
1da177e4 | 155 | } |
896831f5 EC |
156 | |
157 | if (!skb) | |
158 | return 0; | |
1da177e4 LT |
159 | exit: |
160 | dev_kfree_skb(skb); | |
161 | return 0; | |
162 | } | |
163 | ||
7546dd97 | 164 | static struct packet_type aoe_pt __read_mostly = { |
1da177e4 LT |
165 | .type = __constant_htons(ETH_P_AOE), |
166 | .func = aoenet_rcv, | |
167 | }; | |
168 | ||
169 | int __init | |
170 | aoenet_init(void) | |
171 | { | |
172 | dev_add_pack(&aoe_pt); | |
173 | return 0; | |
174 | } | |
175 | ||
176 | void | |
177 | aoenet_exit(void) | |
178 | { | |
179 | dev_remove_pack(&aoe_pt); | |
180 | } | |
181 |