]>
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; | |
63e9cc5d | 105 | u32 n; |
1da177e4 | 106 | |
c346dca1 | 107 | if (dev_net(ifp) != &init_net) |
e730c155 EB |
108 | goto exit; |
109 | ||
5dc401ee EC |
110 | skb = skb_share_check(skb, GFP_ATOMIC); |
111 | if (skb == NULL) | |
1da177e4 | 112 | return 0; |
364c6bad | 113 | if (skb_linearize(skb)) |
5dc401ee | 114 | goto exit; |
1da177e4 LT |
115 | if (!is_aoe_netif(ifp)) |
116 | goto exit; | |
1da177e4 LT |
117 | skb_push(skb, ETH_HLEN); /* (1) */ |
118 | ||
abdbf94d | 119 | h = (struct aoe_hdr *) skb_mac_header(skb); |
f885f8d1 | 120 | n = get_unaligned_be32(&h->tag); |
1da177e4 LT |
121 | if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31)) |
122 | goto exit; | |
123 | ||
124 | if (h->verfl & AOEFL_ERR) { | |
125 | n = h->err; | |
126 | if (n > NECODES) | |
127 | n = 0; | |
128 | if (net_ratelimit()) | |
68e0d42f EC |
129 | printk(KERN_ERR |
130 | "%s%d.%d@%s; ecode=%d '%s'\n", | |
131 | "aoe: error packet from ", | |
f885f8d1 | 132 | get_unaligned_be16(&h->major), |
68e0d42f EC |
133 | h->minor, skb->dev->name, |
134 | h->err, aoe_errlist[n]); | |
1da177e4 LT |
135 | goto exit; |
136 | } | |
137 | ||
138 | switch (h->cmd) { | |
139 | case AOECMD_ATA: | |
140 | aoecmd_ata_rsp(skb); | |
141 | break; | |
142 | case AOECMD_CFG: | |
143 | aoecmd_cfg_rsp(skb); | |
144 | break; | |
145 | default: | |
b6d6c517 EC |
146 | if (h->cmd >= AOECMD_VEND_MIN) |
147 | break; /* don't complain about vendor commands */ | |
a12c93f0 | 148 | printk(KERN_INFO "aoe: unknown cmd %d\n", h->cmd); |
1da177e4 LT |
149 | } |
150 | exit: | |
151 | dev_kfree_skb(skb); | |
152 | return 0; | |
153 | } | |
154 | ||
7546dd97 | 155 | static struct packet_type aoe_pt __read_mostly = { |
1da177e4 LT |
156 | .type = __constant_htons(ETH_P_AOE), |
157 | .func = aoenet_rcv, | |
158 | }; | |
159 | ||
160 | int __init | |
161 | aoenet_init(void) | |
162 | { | |
163 | dev_add_pack(&aoe_pt); | |
164 | return 0; | |
165 | } | |
166 | ||
167 | void | |
168 | aoenet_exit(void) | |
169 | { | |
170 | dev_remove_pack(&aoe_pt); | |
171 | } | |
172 |