Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* Copyright (c) 2004 Coraid, Inc. See COPYING for GPL terms. */ |
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> | |
10 | #include "aoe.h" | |
11 | ||
12 | #define NECODES 5 | |
13 | ||
14 | static char *aoe_errlist[] = | |
15 | { | |
16 | "no such error", | |
17 | "unrecognized command code", | |
18 | "bad argument parameter", | |
19 | "device unavailable", | |
20 | "config string present", | |
21 | "unsupported version" | |
22 | }; | |
23 | ||
24 | enum { | |
25 | IFLISTSZ = 1024, | |
26 | }; | |
27 | ||
28 | static char aoe_iflist[IFLISTSZ]; | |
29 | ||
30 | int | |
31 | is_aoe_netif(struct net_device *ifp) | |
32 | { | |
33 | register char *p, *q; | |
34 | register int len; | |
35 | ||
36 | if (aoe_iflist[0] == '\0') | |
37 | return 1; | |
38 | ||
39 | for (p = aoe_iflist; *p; p = q + strspn(q, WHITESPACE)) { | |
40 | q = p + strcspn(p, WHITESPACE); | |
41 | if (q != p) | |
42 | len = q - p; | |
43 | else | |
44 | len = strlen(p); /* last token in aoe_iflist */ | |
45 | ||
46 | if (strlen(ifp->name) == len && !strncmp(ifp->name, p, len)) | |
47 | return 1; | |
48 | if (q == p) | |
49 | break; | |
50 | } | |
51 | ||
52 | return 0; | |
53 | } | |
54 | ||
55 | int | |
56 | set_aoe_iflist(const char __user *user_str, size_t size) | |
57 | { | |
58 | if (size >= IFLISTSZ) | |
59 | return -EINVAL; | |
60 | ||
61 | if (copy_from_user(aoe_iflist, user_str, size)) { | |
62 | printk(KERN_INFO "aoe: %s: copy from user failed\n", __FUNCTION__); | |
63 | return -EFAULT; | |
64 | } | |
65 | aoe_iflist[size] = 0x00; | |
66 | return 0; | |
67 | } | |
68 | ||
69 | u64 | |
70 | mac_addr(char addr[6]) | |
71 | { | |
72 | u64 n = 0; | |
73 | char *p = (char *) &n; | |
74 | ||
75 | memcpy(p + 2, addr, 6); /* (sizeof addr != 6) */ | |
76 | ||
77 | return __be64_to_cpu(n); | |
78 | } | |
79 | ||
80 | static struct sk_buff * | |
81 | skb_check(struct sk_buff *skb) | |
82 | { | |
83 | if (skb_is_nonlinear(skb)) | |
84 | if ((skb = skb_share_check(skb, GFP_ATOMIC))) | |
85 | if (skb_linearize(skb, GFP_ATOMIC) < 0) { | |
86 | dev_kfree_skb(skb); | |
87 | return NULL; | |
88 | } | |
89 | return skb; | |
90 | } | |
91 | ||
92 | void | |
93 | aoenet_xmit(struct sk_buff *sl) | |
94 | { | |
95 | struct sk_buff *skb; | |
96 | ||
97 | while ((skb = sl)) { | |
98 | sl = sl->next; | |
99 | skb->next = skb->prev = NULL; | |
100 | dev_queue_xmit(skb); | |
101 | } | |
102 | } | |
103 | ||
104 | /* | |
105 | * (1) len doesn't include the header by default. I want this. | |
106 | */ | |
107 | static int | |
108 | aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt) | |
109 | { | |
110 | struct aoe_hdr *h; | |
111 | ulong n; | |
112 | ||
113 | skb = skb_check(skb); | |
114 | if (!skb) | |
115 | return 0; | |
116 | ||
117 | if (!is_aoe_netif(ifp)) | |
118 | goto exit; | |
119 | ||
120 | //skb->len += ETH_HLEN; /* (1) */ | |
121 | skb_push(skb, ETH_HLEN); /* (1) */ | |
122 | ||
123 | h = (struct aoe_hdr *) skb->mac.raw; | |
124 | n = __be32_to_cpu(*((u32 *) h->tag)); | |
125 | if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31)) | |
126 | goto exit; | |
127 | ||
128 | if (h->verfl & AOEFL_ERR) { | |
129 | n = h->err; | |
130 | if (n > NECODES) | |
131 | n = 0; | |
132 | if (net_ratelimit()) | |
133 | printk(KERN_ERR "aoe: aoenet_rcv: error packet from %d.%d; " | |
134 | "ecode=%d '%s'\n", | |
135 | __be16_to_cpu(*((u16 *) h->major)), h->minor, | |
136 | h->err, aoe_errlist[n]); | |
137 | goto exit; | |
138 | } | |
139 | ||
140 | switch (h->cmd) { | |
141 | case AOECMD_ATA: | |
142 | aoecmd_ata_rsp(skb); | |
143 | break; | |
144 | case AOECMD_CFG: | |
145 | aoecmd_cfg_rsp(skb); | |
146 | break; | |
147 | default: | |
148 | printk(KERN_INFO "aoe: aoenet_rcv: unknown cmd %d\n", h->cmd); | |
149 | } | |
150 | exit: | |
151 | dev_kfree_skb(skb); | |
152 | return 0; | |
153 | } | |
154 | ||
155 | static struct packet_type aoe_pt = { | |
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 |