diff options
Diffstat (limited to 'drivers/net/rionet.c')
| -rw-r--r-- | drivers/net/rionet.c | 287 | 
1 files changed, 178 insertions, 109 deletions
diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index 44150f2f7bf..dac7a0d9bb4 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -26,7 +26,7 @@  #include <linux/ethtool.h>  #define DRV_NAME        "rionet" -#define DRV_VERSION     "0.2" +#define DRV_VERSION     "0.3"  #define DRV_AUTHOR      "Matt Porter <mporter@kernel.crashing.org>"  #define DRV_DESC        "Ethernet over RapidIO" @@ -47,8 +47,7 @@ MODULE_LICENSE("GPL");  #define RIONET_TX_RING_SIZE	CONFIG_RIONET_TX_SIZE  #define RIONET_RX_RING_SIZE	CONFIG_RIONET_RX_SIZE - -static LIST_HEAD(rionet_peers); +#define RIONET_MAX_NETS		8  struct rionet_private {  	struct rio_mport *mport; @@ -69,27 +68,25 @@ struct rionet_peer {  	struct resource *res;  }; -static int rionet_check = 0; -static int rionet_capable = 1; +struct rionet_net { +	struct net_device *ndev; +	struct list_head peers; +	struct rio_dev **active; +	int nact;	/* number of active peers */ +}; -/* - * This is a fast lookup table for translating TX - * Ethernet packets into a destination RIO device. It - * could be made into a hash table to save memory depending - * on system trade-offs. - */ -static struct rio_dev **rionet_active; +static struct rionet_net nets[RIONET_MAX_NETS]; -#define is_rionet_capable(pef, src_ops, dst_ops)		\ -			((pef & RIO_PEF_INB_MBOX) &&		\ -			 (pef & RIO_PEF_INB_DOORBELL) &&	\ +#define is_rionet_capable(src_ops, dst_ops)			\ +			((src_ops & RIO_SRC_OPS_DATA_MSG) &&	\ +			 (dst_ops & RIO_DST_OPS_DATA_MSG) &&	\  			 (src_ops & RIO_SRC_OPS_DOORBELL) &&	\  			 (dst_ops & RIO_DST_OPS_DOORBELL))  #define dev_rionet_capable(dev) \ -	is_rionet_capable(dev->pef, dev->src_ops, dev->dst_ops) +	is_rionet_capable(dev->src_ops, dev->dst_ops) -#define RIONET_MAC_MATCH(x)	(*(u32 *)x == 0x00010001) -#define RIONET_GET_DESTID(x)	(*(u16 *)(x + 4)) +#define RIONET_MAC_MATCH(x)	(!memcmp((x), "\00\01\00\01", 4)) +#define RIONET_GET_DESTID(x)	((*((u8 *)x + 4) << 8) | *((u8 *)x + 5))  static int rionet_rx_clean(struct net_device *ndev)  { @@ -162,8 +159,8 @@ static int rionet_queue_tx_msg(struct sk_buff *skb, struct net_device *ndev,  	rnet->tx_slot &= (RIONET_TX_RING_SIZE - 1);  	if (netif_msg_tx_queued(rnet)) -		printk(KERN_INFO "%s: queued skb %8.8x len %8.8x\n", DRV_NAME, -		       (u32) skb, skb->len); +		printk(KERN_INFO "%s: queued skb len %8.8x\n", DRV_NAME, +		       skb->len);  	return 0;  } @@ -175,6 +172,7 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev)  	struct ethhdr *eth = (struct ethhdr *)skb->data;  	u16 destid;  	unsigned long flags; +	int add_num = 1;  	local_irq_save(flags);  	if (!spin_trylock(&rnet->tx_lock)) { @@ -182,7 +180,10 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev)  		return NETDEV_TX_LOCKED;  	} -	if ((rnet->tx_cnt + 1) > RIONET_TX_RING_SIZE) { +	if (is_multicast_ether_addr(eth->h_dest)) +		add_num = nets[rnet->mport->id].nact; + +	if ((rnet->tx_cnt + add_num) > RIONET_TX_RING_SIZE) {  		netif_stop_queue(ndev);  		spin_unlock_irqrestore(&rnet->tx_lock, flags);  		printk(KERN_ERR "%s: BUG! Tx Ring full when queue awake!\n", @@ -190,16 +191,34 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev)  		return NETDEV_TX_BUSY;  	} -	if (eth->h_dest[0] & 0x01) { +	if (is_multicast_ether_addr(eth->h_dest)) { +		int count = 0; +  		for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size);  				i++) -			if (rionet_active[i]) +			if (nets[rnet->mport->id].active[i]) {  				rionet_queue_tx_msg(skb, ndev, -						    rionet_active[i]); +					nets[rnet->mport->id].active[i]); +				if (count) +					atomic_inc(&skb->users); +				count++; +			}  	} else if (RIONET_MAC_MATCH(eth->h_dest)) {  		destid = RIONET_GET_DESTID(eth->h_dest); -		if (rionet_active[destid]) -			rionet_queue_tx_msg(skb, ndev, rionet_active[destid]); +		if (nets[rnet->mport->id].active[destid]) +			rionet_queue_tx_msg(skb, ndev, +					nets[rnet->mport->id].active[destid]); +		else { +			/* +			 * If the target device was removed from the list of +			 * active peers but we still have TX packets targeting +			 * it just report sending a packet to the target +			 * (without actual packet transfer). +			 */ +			dev_kfree_skb_any(skb); +			ndev->stats.tx_packets++; +			ndev->stats.tx_bytes += skb->len; +		}  	}  	spin_unlock_irqrestore(&rnet->tx_lock, flags); @@ -218,16 +237,21 @@ static void rionet_dbell_event(struct rio_mport *mport, void *dev_id, u16 sid, u  		printk(KERN_INFO "%s: doorbell sid %4.4x tid %4.4x info %4.4x",  		       DRV_NAME, sid, tid, info);  	if (info == RIONET_DOORBELL_JOIN) { -		if (!rionet_active[sid]) { -			list_for_each_entry(peer, &rionet_peers, node) { -				if (peer->rdev->destid == sid) -					rionet_active[sid] = peer->rdev; +		if (!nets[rnet->mport->id].active[sid]) { +			list_for_each_entry(peer, +					   &nets[rnet->mport->id].peers, node) { +				if (peer->rdev->destid == sid) { +					nets[rnet->mport->id].active[sid] = +								peer->rdev; +					nets[rnet->mport->id].nact++; +				}  			}  			rio_mport_send_doorbell(mport, sid,  						RIONET_DOORBELL_JOIN);  		}  	} else if (info == RIONET_DOORBELL_LEAVE) { -		rionet_active[sid] = NULL; +		nets[rnet->mport->id].active[sid] = NULL; +		nets[rnet->mport->id].nact--;  	} else {  		if (netif_msg_intr(rnet))  			printk(KERN_WARNING "%s: unhandled doorbell\n", @@ -282,7 +306,6 @@ static int rionet_open(struct net_device *ndev)  {  	int i, rc = 0;  	struct rionet_peer *peer, *tmp; -	u32 pwdcsr;  	struct rionet_private *rnet = netdev_priv(ndev);  	if (netif_msg_ifup(rnet)) @@ -322,7 +345,8 @@ static int rionet_open(struct net_device *ndev)  	netif_carrier_on(ndev);  	netif_start_queue(ndev); -	list_for_each_entry_safe(peer, tmp, &rionet_peers, node) { +	list_for_each_entry_safe(peer, tmp, +				 &nets[rnet->mport->id].peers, node) {  		if (!(peer->res = rio_request_outb_dbell(peer->rdev,  							 RIONET_DOORBELL_JOIN,  							 RIONET_DOORBELL_LEAVE))) @@ -332,13 +356,8 @@ static int rionet_open(struct net_device *ndev)  			continue;  		} -		/* -		 * If device has initialized inbound doorbells, -		 * send a join message -		 */ -		rio_read_config_32(peer->rdev, RIO_WRITE_PORT_CSR, &pwdcsr); -		if (pwdcsr & RIO_DOORBELL_AVAIL) -			rio_send_doorbell(peer->rdev, RIONET_DOORBELL_JOIN); +		/* Send a join message */ +		rio_send_doorbell(peer->rdev, RIONET_DOORBELL_JOIN);  	}        out: @@ -352,7 +371,7 @@ static int rionet_close(struct net_device *ndev)  	int i;  	if (netif_msg_ifup(rnet)) -		printk(KERN_INFO "%s: close\n", DRV_NAME); +		printk(KERN_INFO "%s: close %s\n", DRV_NAME, ndev->name);  	netif_stop_queue(ndev);  	netif_carrier_off(ndev); @@ -360,10 +379,11 @@ static int rionet_close(struct net_device *ndev)  	for (i = 0; i < RIONET_RX_RING_SIZE; i++)  		kfree_skb(rnet->rx_skb[i]); -	list_for_each_entry_safe(peer, tmp, &rionet_peers, node) { -		if (rionet_active[peer->rdev->destid]) { +	list_for_each_entry_safe(peer, tmp, +				 &nets[rnet->mport->id].peers, node) { +		if (nets[rnet->mport->id].active[peer->rdev->destid]) {  			rio_send_doorbell(peer->rdev, RIONET_DOORBELL_LEAVE); -			rionet_active[peer->rdev->destid] = NULL; +			nets[rnet->mport->id].active[peer->rdev->destid] = NULL;  		}  		rio_release_outb_dbell(peer->rdev, peer->res);  	} @@ -376,20 +396,28 @@ static int rionet_close(struct net_device *ndev)  	return 0;  } -static void rionet_remove(struct rio_dev *rdev) +static int rionet_remove_dev(struct device *dev, struct subsys_interface *sif)  { -	struct net_device *ndev = NULL; +	struct rio_dev *rdev = to_rio_dev(dev); +	unsigned char netid = rdev->net->hport->id;  	struct rionet_peer *peer, *tmp; -	free_pages((unsigned long)rionet_active, rdev->net->hport->sys_size ? -					__ilog2(sizeof(void *)) + 4 : 0); -	unregister_netdev(ndev); -	free_netdev(ndev); - -	list_for_each_entry_safe(peer, tmp, &rionet_peers, node) { -		list_del(&peer->node); -		kfree(peer); +	if (dev_rionet_capable(rdev)) { +		list_for_each_entry_safe(peer, tmp, &nets[netid].peers, node) { +			if (peer->rdev == rdev) { +				if (nets[netid].active[rdev->destid]) { +					nets[netid].active[rdev->destid] = NULL; +					nets[netid].nact--; +				} + +				list_del(&peer->node); +				kfree(peer); +				break; +			} +		}  	} + +	return 0;  }  static void rionet_get_drvinfo(struct net_device *ndev, @@ -397,10 +425,10 @@ static void rionet_get_drvinfo(struct net_device *ndev,  {  	struct rionet_private *rnet = netdev_priv(ndev); -	strcpy(info->driver, DRV_NAME); -	strcpy(info->version, DRV_VERSION); -	strcpy(info->fw_version, "n/a"); -	strcpy(info->bus_info, rnet->mport->name); +	strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); +	strlcpy(info->version, DRV_VERSION, sizeof(info->version)); +	strlcpy(info->fw_version, "n/a", sizeof(info->fw_version)); +	strlcpy(info->bus_info, rnet->mport->name, sizeof(info->bus_info));  }  static u32 rionet_get_msglevel(struct net_device *ndev) @@ -433,30 +461,21 @@ static const struct net_device_ops rionet_netdev_ops = {  	.ndo_set_mac_address	= eth_mac_addr,  }; -static int rionet_setup_netdev(struct rio_mport *mport) +static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev)  {  	int rc = 0; -	struct net_device *ndev = NULL;  	struct rionet_private *rnet;  	u16 device_id; +	const size_t rionet_active_bytes = sizeof(void *) * +				RIO_MAX_ROUTE_ENTRIES(mport->sys_size); -	/* Allocate our net_device structure */ -	ndev = alloc_etherdev(sizeof(struct rionet_private)); -	if (ndev == NULL) { -		printk(KERN_INFO "%s: could not allocate ethernet device.\n", -		       DRV_NAME); -		rc = -ENOMEM; -		goto out; -	} - -	rionet_active = (struct rio_dev **)__get_free_pages(GFP_KERNEL, -			mport->sys_size ? __ilog2(sizeof(void *)) + 4 : 0); -	if (!rionet_active) { +	nets[mport->id].active = (struct rio_dev **)__get_free_pages(GFP_KERNEL, +						get_order(rionet_active_bytes)); +	if (!nets[mport->id].active) {  		rc = -ENOMEM;  		goto out;  	} -	memset((void *)rionet_active, 0, sizeof(void *) * -				RIO_MAX_ROUTE_ENTRIES(mport->sys_size)); +	memset((void *)nets[mport->id].active, 0, rionet_active_bytes);  	/* Set up private area */  	rnet = netdev_priv(ndev); @@ -474,7 +493,8 @@ static int rionet_setup_netdev(struct rio_mport *mport)  	ndev->netdev_ops = &rionet_netdev_ops;  	ndev->mtu = RIO_MAX_MSG_SIZE - 14;  	ndev->features = NETIF_F_LLTX; -	SET_ETHTOOL_OPS(ndev, &rionet_ethtool_ops); +	SET_NETDEV_DEV(ndev, &mport->dev); +	ndev->ethtool_ops = &rionet_ethtool_ops;  	spin_lock_init(&rnet->lock);  	spin_lock_init(&rnet->tx_lock); @@ -485,53 +505,70 @@ static int rionet_setup_netdev(struct rio_mport *mport)  	if (rc != 0)  		goto out; -	printk("%s: %s %s Version %s, MAC %pM\n", +	printk(KERN_INFO "%s: %s %s Version %s, MAC %pM, %s\n",  	       ndev->name,  	       DRV_NAME,  	       DRV_DESC,  	       DRV_VERSION, -	       ndev->dev_addr); +	       ndev->dev_addr, +	       mport->name);        out:  	return rc;  } -/* - * XXX Make multi-net safe - */ -static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id) +static unsigned long net_table[RIONET_MAX_NETS/sizeof(unsigned long) + 1]; + +static int rionet_add_dev(struct device *dev, struct subsys_interface *sif)  {  	int rc = -ENODEV; -	u32 lpef, lsrc_ops, ldst_ops; +	u32 lsrc_ops, ldst_ops;  	struct rionet_peer *peer; +	struct net_device *ndev = NULL; +	struct rio_dev *rdev = to_rio_dev(dev); +	unsigned char netid = rdev->net->hport->id; +	int oldnet; -	/* If local device is not rionet capable, give up quickly */ -	if (!rionet_capable) -		goto out; +	if (netid >= RIONET_MAX_NETS) +		return rc; + +	oldnet = test_and_set_bit(netid, net_table);  	/* -	 * First time through, make sure local device is rionet -	 * capable, setup netdev,  and set flags so this is skipped -	 * on later probes +	 * If first time through this net, make sure local device is rionet +	 * capable and setup netdev (this step will be skipped in later probes +	 * on the same net).  	 */ -	if (!rionet_check) { -		rio_local_read_config_32(rdev->net->hport, RIO_PEF_CAR, &lpef); +	if (!oldnet) {  		rio_local_read_config_32(rdev->net->hport, RIO_SRC_OPS_CAR,  					 &lsrc_ops);  		rio_local_read_config_32(rdev->net->hport, RIO_DST_OPS_CAR,  					 &ldst_ops); -		if (!is_rionet_capable(lpef, lsrc_ops, ldst_ops)) { +		if (!is_rionet_capable(lsrc_ops, ldst_ops)) {  			printk(KERN_ERR -			       "%s: local device is not network capable\n", -			       DRV_NAME); -			rionet_check = 1; -			rionet_capable = 0; +			       "%s: local device %s is not network capable\n", +			       DRV_NAME, rdev->net->hport->name);  			goto out;  		} -		rc = rionet_setup_netdev(rdev->net->hport); -		rionet_check = 1; -	} +		/* Allocate our net_device structure */ +		ndev = alloc_etherdev(sizeof(struct rionet_private)); +		if (ndev == NULL) { +			rc = -ENOMEM; +			goto out; +		} +		nets[netid].ndev = ndev; +		rc = rionet_setup_netdev(rdev->net->hport, ndev); +		if (rc) { +			printk(KERN_ERR "%s: failed to setup netdev (rc=%d)\n", +			       DRV_NAME, rc); +			goto out; +		} + +		INIT_LIST_HEAD(&nets[netid].peers); +		nets[netid].nact = 0; +	} else if (nets[netid].ndev == NULL) +		goto out;  	/*  	 * If the remote device has mailbox/doorbell capabilities, @@ -543,33 +580,65 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id)  			goto out;  		}  		peer->rdev = rdev; -		list_add_tail(&peer->node, &rionet_peers); +		list_add_tail(&peer->node, &nets[netid].peers);  	} -      out: +	return 0; +out:  	return rc;  } +#ifdef MODULE  static struct rio_device_id rionet_id_table[] = { -	{RIO_DEVICE(RIO_ANY_ID, RIO_ANY_ID)} +	{RIO_DEVICE(RIO_ANY_ID, RIO_ANY_ID)}, +	{ 0, }	/* terminate list */  }; -static struct rio_driver rionet_driver = { -	.name = "rionet", -	.id_table = rionet_id_table, -	.probe = rionet_probe, -	.remove = rionet_remove, +MODULE_DEVICE_TABLE(rapidio, rionet_id_table); +#endif + +static struct subsys_interface rionet_interface = { +	.name		= "rionet", +	.subsys		= &rio_bus_type, +	.add_dev	= rionet_add_dev, +	.remove_dev	= rionet_remove_dev,  };  static int __init rionet_init(void)  { -	return rio_register_driver(&rionet_driver); +	return subsys_interface_register(&rionet_interface);  }  static void __exit rionet_exit(void)  { -	rio_unregister_driver(&rionet_driver); +	struct rionet_private *rnet; +	struct net_device *ndev; +	struct rionet_peer *peer, *tmp; +	int i; + +	for (i = 0; i < RIONET_MAX_NETS; i++) { +		if (nets[i].ndev != NULL) { +			ndev = nets[i].ndev; +			rnet = netdev_priv(ndev); +			unregister_netdev(ndev); + +			list_for_each_entry_safe(peer, +						 tmp, &nets[i].peers, node) { +				list_del(&peer->node); +				kfree(peer); +			} + +			free_pages((unsigned long)nets[i].active, +				 get_order(sizeof(void *) * +				 RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size))); +			nets[i].active = NULL; + +			free_netdev(ndev); +		} +	} + +	subsys_interface_unregister(&rionet_interface);  } -module_init(rionet_init); +late_initcall(rionet_init);  module_exit(rionet_exit);  | 
