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 | ||
7 | #include <linux/hdreg.h> | |
8 | #include <linux/blkdev.h> | |
9 | #include <linux/netdevice.h> | |
03c41c43 | 10 | #include <linux/moduleparam.h> |
e730c155 | 11 | #include <net/net_namespace.h> |
43ecf529 | 12 | #include <asm/unaligned.h> |
1da177e4 LT |
13 | #include "aoe.h" |
14 | ||
15 | #define NECODES 5 | |
16 | ||
17 | static char *aoe_errlist[] = | |
18 | { | |
19 | "no such error", | |
20 | "unrecognized command code", | |
21 | "bad argument parameter", | |
22 | "device unavailable", | |
23 | "config string present", | |
24 | "unsupported version" | |
25 | }; | |
26 | ||
27 | enum { | |
28 | IFLISTSZ = 1024, | |
29 | }; | |
30 | ||
31 | static char aoe_iflist[IFLISTSZ]; | |
03c41c43 | 32 | module_param_string(aoe_iflist, aoe_iflist, IFLISTSZ, 0600); |
61a2d07d | 33 | MODULE_PARM_DESC(aoe_iflist, "aoe_iflist=\"dev1 [dev2 ...]\""); |
03c41c43 EC |
34 | |
35 | #ifndef MODULE | |
36 | static int __init aoe_iflist_setup(char *str) | |
37 | { | |
38 | strncpy(aoe_iflist, str, IFLISTSZ); | |
39 | aoe_iflist[IFLISTSZ - 1] = '\0'; | |
40 | return 1; | |
41 | } | |
42 | ||
43 | __setup("aoe_iflist=", aoe_iflist_setup); | |
44 | #endif | |
1da177e4 LT |
45 | |
46 | int | |
47 | is_aoe_netif(struct net_device *ifp) | |
48 | { | |
49 | register char *p, *q; | |
50 | register int len; | |
51 | ||
52 | if (aoe_iflist[0] == '\0') | |
53 | return 1; | |
54 | ||
03c41c43 EC |
55 | p = aoe_iflist + strspn(aoe_iflist, WHITESPACE); |
56 | for (; *p; p = q + strspn(q, WHITESPACE)) { | |
1da177e4 LT |
57 | q = p + strcspn(p, WHITESPACE); |
58 | if (q != p) | |
59 | len = q - p; | |
60 | else | |
61 | len = strlen(p); /* last token in aoe_iflist */ | |
62 | ||
63 | if (strlen(ifp->name) == len && !strncmp(ifp->name, p, len)) | |
64 | return 1; | |
65 | if (q == p) | |
66 | break; | |
67 | } | |
68 | ||
69 | return 0; | |
70 | } | |
71 | ||
72 | int | |
73 | set_aoe_iflist(const char __user *user_str, size_t size) | |
74 | { | |
75 | if (size >= IFLISTSZ) | |
76 | return -EINVAL; | |
77 | ||
78 | if (copy_from_user(aoe_iflist, user_str, size)) { | |
a12c93f0 | 79 | printk(KERN_INFO "aoe: copy from user failed\n"); |
1da177e4 LT |
80 | return -EFAULT; |
81 | } | |
82 | aoe_iflist[size] = 0x00; | |
83 | return 0; | |
84 | } | |
85 | ||
1da177e4 | 86 | void |
e9bb8fb0 | 87 | aoenet_xmit(struct sk_buff_head *queue) |
1da177e4 | 88 | { |
e9bb8fb0 | 89 | struct sk_buff *skb, *tmp; |
1da177e4 | 90 | |
d8779845 DM |
91 | skb_queue_walk_safe(queue, skb, tmp) { |
92 | __skb_unlink(skb, queue); | |
1da177e4 | 93 | dev_queue_xmit(skb); |
d8779845 | 94 | } |
1da177e4 LT |
95 | } |
96 | ||
97 | /* | |
98 | * (1) len doesn't include the header by default. I want this. | |
99 | */ | |
100 | static int | |
f2ccd8fa | 101 | aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, struct net_device *orig_dev) |
1da177e4 LT |
102 | { |
103 | struct aoe_hdr *h; | |
63e9cc5d | 104 | u32 n; |
1da177e4 | 105 | |
c346dca1 | 106 | if (dev_net(ifp) != &init_net) |
e730c155 EB |
107 | goto exit; |
108 | ||
5dc401ee EC |
109 | skb = skb_share_check(skb, GFP_ATOMIC); |
110 | if (skb == NULL) | |
1da177e4 | 111 | return 0; |
364c6bad | 112 | if (skb_linearize(skb)) |
5dc401ee | 113 | goto exit; |
1da177e4 LT |
114 | if (!is_aoe_netif(ifp)) |
115 | goto exit; | |
1da177e4 LT |
116 | skb_push(skb, ETH_HLEN); /* (1) */ |
117 | ||
abdbf94d | 118 | h = (struct aoe_hdr *) skb_mac_header(skb); |
f885f8d1 | 119 | n = get_unaligned_be32(&h->tag); |
1da177e4 LT |
120 | if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31)) |
121 | goto exit; | |
122 | ||
123 | if (h->verfl & AOEFL_ERR) { | |
124 | n = h->err; | |
125 | if (n > NECODES) | |
126 | n = 0; | |
127 | if (net_ratelimit()) | |
68e0d42f EC |
128 | printk(KERN_ERR |
129 | "%s%d.%d@%s; ecode=%d '%s'\n", | |
130 | "aoe: error packet from ", | |
f885f8d1 | 131 | get_unaligned_be16(&h->major), |
68e0d42f EC |
132 | h->minor, skb->dev->name, |
133 | h->err, aoe_errlist[n]); | |
1da177e4 LT |
134 | goto exit; |
135 | } | |
136 | ||
137 | switch (h->cmd) { | |
138 | case AOECMD_ATA: | |
139 | aoecmd_ata_rsp(skb); | |
140 | break; | |
141 | case AOECMD_CFG: | |
142 | aoecmd_cfg_rsp(skb); | |
143 | break; | |
144 | default: | |
a12c93f0 | 145 | printk(KERN_INFO "aoe: unknown cmd %d\n", h->cmd); |
1da177e4 LT |
146 | } |
147 | exit: | |
148 | dev_kfree_skb(skb); | |
149 | return 0; | |
150 | } | |
151 | ||
152 | static struct packet_type aoe_pt = { | |
153 | .type = __constant_htons(ETH_P_AOE), | |
154 | .func = aoenet_rcv, | |
155 | }; | |
156 | ||
157 | int __init | |
158 | aoenet_init(void) | |
159 | { | |
160 | dev_add_pack(&aoe_pt); | |
161 | return 0; | |
162 | } | |
163 | ||
164 | void | |
165 | aoenet_exit(void) | |
166 | { | |
167 | dev_remove_pack(&aoe_pt); | |
168 | } | |
169 |