aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/rionet.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/rionet.c')
-rw-r--r--drivers/net/rionet.c287
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);