152e112b3ab6b2b35a144565c8ea3bdda1e2845f2Ed L. Cashin/* Copyright (c) 2007 Coraid, Inc.  See COPYING for GPL terms. */
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * aoenet.c
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Ethernet portion of AoE driver
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
75a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h>
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/hdreg.h>
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/blkdev.h>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
1103c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin#include <linux/moduleparam.h>
12e730c15519d09ea528b4d2f1103681fa5937c0e6Eric W. Biederman#include <net/net_namespace.h>
1343ecf5295b622e9ec93f5b932949acf1c6e4150cDavid S. Miller#include <asm/unaligned.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "aoe.h"
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NECODES 5
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char *aoe_errlist[] =
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"no such error",
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"unrecognized command code",
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"bad argument parameter",
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"device unavailable",
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"config string present",
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"unsupported version"
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsenum {
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	IFLISTSZ = 1024,
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char aoe_iflist[IFLISTSZ];
3303c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashinmodule_param_string(aoe_iflist, aoe_iflist, IFLISTSZ, 0600);
3461a2d07d3fb1ac34d142b9b62d4cd60a0f8c229eNiels de VosMODULE_PARM_DESC(aoe_iflist, "aoe_iflist=\"dev1 [dev2 ...]\"");
3503c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin
3603c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin#ifndef MODULE
3703c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashinstatic int __init aoe_iflist_setup(char *str)
3803c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin{
3903c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin	strncpy(aoe_iflist, str, IFLISTSZ);
4003c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin	aoe_iflist[IFLISTSZ - 1] = '\0';
4103c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin	return 1;
4203c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin}
4303c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin
4403c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin__setup("aoe_iflist=", aoe_iflist_setup);
4503c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin#endif
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsis_aoe_netif(struct net_device *ifp)
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	register char *p, *q;
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	register int len;
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (aoe_iflist[0] == '\0')
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 1;
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5603c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin	p = aoe_iflist + strspn(aoe_iflist, WHITESPACE);
5703c41c434775c52092d17a5031ad8ebaaf555bc4Ed L. Cashin	for (; *p; p = q + strspn(q, WHITESPACE)) {
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		q = p + strcspn(p, WHITESPACE);
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (q != p)
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			len = q - p;
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			len = strlen(p); /* last token in aoe_iflist */
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (strlen(ifp->name) == len && !strncmp(ifp->name, p, len))
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 1;
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (q == p)
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsset_aoe_iflist(const char __user *user_str, size_t size)
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (size >= IFLISTSZ)
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (copy_from_user(aoe_iflist, user_str, size)) {
80a12c93f08b8fc83b7fcdabaf92b1adcea7489f5eEd L. Cashin		printk(KERN_INFO "aoe: copy from user failed\n");
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	aoe_iflist[size] = 0x00;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
88e9bb8fb0b6d61a822201537b25206a0ca34b9d1dDavid S. Milleraoenet_xmit(struct sk_buff_head *queue)
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
90e9bb8fb0b6d61a822201537b25206a0ca34b9d1dDavid S. Miller	struct sk_buff *skb, *tmp;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
92d87798450a7635ab1bcc80271a13ce4a53b016a9David S. Miller	skb_queue_walk_safe(queue, skb, tmp) {
93d87798450a7635ab1bcc80271a13ce4a53b016a9David S. Miller		__skb_unlink(skb, queue);
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_queue_xmit(skb);
95d87798450a7635ab1bcc80271a13ce4a53b016a9David S. Miller	}
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (1) len doesn't include the header by default.  I want this.
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
102f2ccd8fa06c8e302116e71df372f5c1f83432e03David S. Milleraoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, struct net_device *orig_dev)
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct aoe_hdr *h;
10563e9cc5d6fbe8b58ea1ee96439d356cbf726fbc0Ed L. Cashin	u32 n;
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
107c346dca10840a874240c78efe3f39acf4312a1f2YOSHIFUJI Hideaki	if (dev_net(ifp) != &init_net)
108e730c15519d09ea528b4d2f1103681fa5937c0e6Eric W. Biederman		goto exit;
109e730c15519d09ea528b4d2f1103681fa5937c0e6Eric W. Biederman
1105dc401ee74c5d6a24867acd8302c55da9ae4f0ceEd L. Cashin	skb = skb_share_check(skb, GFP_ATOMIC);
1115dc401ee74c5d6a24867acd8302c55da9ae4f0ceEd L. Cashin	if (skb == NULL)
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
113364c6badde0dd62a0a38e5ed67f85d87d6665780Herbert Xu	if (skb_linearize(skb))
1145dc401ee74c5d6a24867acd8302c55da9ae4f0ceEd L. Cashin		goto exit;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!is_aoe_netif(ifp))
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	skb_push(skb, ETH_HLEN);	/* (1) */
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
119abdbf94d7c6f1fcb2931d5cb7562a6159323b704Ed L. Cashin	h = (struct aoe_hdr *) skb_mac_header(skb);
120f885f8d127665e784a8071755243bd4e18f594d5Harvey Harrison	n = get_unaligned_be32(&h->tag);
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31))
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (h->verfl & AOEFL_ERR) {
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		n = h->err;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (n > NECODES)
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			n = 0;
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (net_ratelimit())
12968e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin			printk(KERN_ERR
13068e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin				"%s%d.%d@%s; ecode=%d '%s'\n",
13168e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin				"aoe: error packet from ",
132f885f8d127665e784a8071755243bd4e18f594d5Harvey Harrison				get_unaligned_be16(&h->major),
13368e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin				h->minor, skb->dev->name,
13468e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin				h->err, aoe_errlist[n]);
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (h->cmd) {
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case AOECMD_ATA:
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		aoecmd_ata_rsp(skb);
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case AOECMD_CFG:
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		aoecmd_cfg_rsp(skb);
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
146b6d6c5175809934e04a606d9193ef04924a7a7d9Ed L. Cashin		if (h->cmd >= AOECMD_VEND_MIN)
147b6d6c5175809934e04a606d9193ef04924a7a7d9Ed L. Cashin			break;	/* don't complain about vendor commands */
148a12c93f08b8fc83b7fcdabaf92b1adcea7489f5eEd L. Cashin		printk(KERN_INFO "aoe: unknown cmd %d\n", h->cmd);
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_kfree_skb(skb);
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1557546dd97d27306d939c13e03318aae695badaa88Stephen Hemmingerstatic struct packet_type aoe_pt __read_mostly = {
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.type = __constant_htons(ETH_P_AOE),
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.func = aoenet_rcv,
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint __init
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsaoenet_init(void)
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_add_pack(&aoe_pt);
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsaoenet_exit(void)
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_remove_pack(&aoe_pt);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
173