diff options
author | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2011-05-12 23:04:46 -0700 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2011-08-11 02:33:43 -0700 |
commit | e689cf4a042772f727450035b102579b0c01bdc7 (patch) | |
tree | f2b17aa21b8358a8f7589fed46fa08688b439464 /drivers/net/sunvnet.c | |
parent | 8efc91254fda97ee78e2e0b8e016120e664131de (diff) |
cassini/niu/sun*: Move the Sun drivers
Moves the Sun drivers into drivers/net/ethernet/sun/ and make
the necessary Kconfig and Makefile changes.
Oliver Hartkopp <socketcan@hartkopp.net> suggested removing the
sun* prefix on the driver names. This type of change I will
leave up to the driver maintainers.
CC: Sam Creasey <sammy@sammy.net>
CC: Adrian Sun <asun@darksunrising.com>
CC: Benjamin Herrenscmidt <benh@kernel.crashing.org>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/sunvnet.c')
-rw-r--r-- | drivers/net/sunvnet.c | 1284 |
1 files changed, 0 insertions, 1284 deletions
diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c deleted file mode 100644 index bf3c762de620..000000000000 --- a/drivers/net/sunvnet.c +++ /dev/null @@ -1,1284 +0,0 @@ -/* sunvnet.c: Sun LDOM Virtual Network Driver. - * - * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/netdevice.h> -#include <linux/ethtool.h> -#include <linux/etherdevice.h> -#include <linux/mutex.h> - -#include <asm/vio.h> -#include <asm/ldc.h> - -#include "sunvnet.h" - -#define DRV_MODULE_NAME "sunvnet" -#define DRV_MODULE_VERSION "1.0" -#define DRV_MODULE_RELDATE "June 25, 2007" - -static char version[] __devinitdata = - DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; -MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); -MODULE_DESCRIPTION("Sun LDOM virtual network driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_MODULE_VERSION); - -/* Ordered from largest major to lowest */ -static struct vio_version vnet_versions[] = { - { .major = 1, .minor = 0 }, -}; - -static inline u32 vnet_tx_dring_avail(struct vio_dring_state *dr) -{ - return vio_dring_avail(dr, VNET_TX_RING_SIZE); -} - -static int vnet_handle_unknown(struct vnet_port *port, void *arg) -{ - struct vio_msg_tag *pkt = arg; - - pr_err("Received unknown msg [%02x:%02x:%04x:%08x]\n", - pkt->type, pkt->stype, pkt->stype_env, pkt->sid); - pr_err("Resetting connection\n"); - - ldc_disconnect(port->vio.lp); - - return -ECONNRESET; -} - -static int vnet_send_attr(struct vio_driver_state *vio) -{ - struct vnet_port *port = to_vnet_port(vio); - struct net_device *dev = port->vp->dev; - struct vio_net_attr_info pkt; - int i; - - memset(&pkt, 0, sizeof(pkt)); - pkt.tag.type = VIO_TYPE_CTRL; - pkt.tag.stype = VIO_SUBTYPE_INFO; - pkt.tag.stype_env = VIO_ATTR_INFO; - pkt.tag.sid = vio_send_sid(vio); - pkt.xfer_mode = VIO_DRING_MODE; - pkt.addr_type = VNET_ADDR_ETHERMAC; - pkt.ack_freq = 0; - for (i = 0; i < 6; i++) - pkt.addr |= (u64)dev->dev_addr[i] << ((5 - i) * 8); - pkt.mtu = ETH_FRAME_LEN; - - viodbg(HS, "SEND NET ATTR xmode[0x%x] atype[0x%x] addr[%llx] " - "ackfreq[%u] mtu[%llu]\n", - pkt.xfer_mode, pkt.addr_type, - (unsigned long long) pkt.addr, - pkt.ack_freq, - (unsigned long long) pkt.mtu); - - return vio_ldc_send(vio, &pkt, sizeof(pkt)); -} - -static int handle_attr_info(struct vio_driver_state *vio, - struct vio_net_attr_info *pkt) -{ - viodbg(HS, "GOT NET ATTR INFO xmode[0x%x] atype[0x%x] addr[%llx] " - "ackfreq[%u] mtu[%llu]\n", - pkt->xfer_mode, pkt->addr_type, - (unsigned long long) pkt->addr, - pkt->ack_freq, - (unsigned long long) pkt->mtu); - - pkt->tag.sid = vio_send_sid(vio); - - if (pkt->xfer_mode != VIO_DRING_MODE || - pkt->addr_type != VNET_ADDR_ETHERMAC || - pkt->mtu != ETH_FRAME_LEN) { - viodbg(HS, "SEND NET ATTR NACK\n"); - - pkt->tag.stype = VIO_SUBTYPE_NACK; - - (void) vio_ldc_send(vio, pkt, sizeof(*pkt)); - - return -ECONNRESET; - } else { - viodbg(HS, "SEND NET ATTR ACK\n"); - - pkt->tag.stype = VIO_SUBTYPE_ACK; - - return vio_ldc_send(vio, pkt, sizeof(*pkt)); - } - -} - -static int handle_attr_ack(struct vio_driver_state *vio, - struct vio_net_attr_info *pkt) -{ - viodbg(HS, "GOT NET ATTR ACK\n"); - - return 0; -} - -static int handle_attr_nack(struct vio_driver_state *vio, - struct vio_net_attr_info *pkt) -{ - viodbg(HS, "GOT NET ATTR NACK\n"); - - return -ECONNRESET; -} - -static int vnet_handle_attr(struct vio_driver_state *vio, void *arg) -{ - struct vio_net_attr_info *pkt = arg; - - switch (pkt->tag.stype) { - case VIO_SUBTYPE_INFO: - return handle_attr_info(vio, pkt); - - case VIO_SUBTYPE_ACK: - return handle_attr_ack(vio, pkt); - - case VIO_SUBTYPE_NACK: - return handle_attr_nack(vio, pkt); - - default: - return -ECONNRESET; - } -} - -static void vnet_handshake_complete(struct vio_driver_state *vio) -{ - struct vio_dring_state *dr; - - dr = &vio->drings[VIO_DRIVER_RX_RING]; - dr->snd_nxt = dr->rcv_nxt = 1; - - dr = &vio->drings[VIO_DRIVER_TX_RING]; - dr->snd_nxt = dr->rcv_nxt = 1; -} - -/* The hypervisor interface that implements copying to/from imported - * memory from another domain requires that copies are done to 8-byte - * aligned buffers, and that the lengths of such copies are also 8-byte - * multiples. - * - * So we align skb->data to an 8-byte multiple and pad-out the data - * area so we can round the copy length up to the next multiple of - * 8 for the copy. - * - * The transmitter puts the actual start of the packet 6 bytes into - * the buffer it sends over, so that the IP headers after the ethernet - * header are aligned properly. These 6 bytes are not in the descriptor - * length, they are simply implied. This offset is represented using - * the VNET_PACKET_SKIP macro. - */ -static struct sk_buff *alloc_and_align_skb(struct net_device *dev, - unsigned int len) -{ - struct sk_buff *skb = netdev_alloc_skb(dev, len+VNET_PACKET_SKIP+8+8); - unsigned long addr, off; - - if (unlikely(!skb)) - return NULL; - - addr = (unsigned long) skb->data; - off = ((addr + 7UL) & ~7UL) - addr; - if (off) - skb_reserve(skb, off); - - return skb; -} - -static int vnet_rx_one(struct vnet_port *port, unsigned int len, - struct ldc_trans_cookie *cookies, int ncookies) -{ - struct net_device *dev = port->vp->dev; - unsigned int copy_len; - struct sk_buff *skb; - int err; - - err = -EMSGSIZE; - if (unlikely(len < ETH_ZLEN || len > ETH_FRAME_LEN)) { - dev->stats.rx_length_errors++; - goto out_dropped; - } - - skb = alloc_and_align_skb(dev, len); - err = -ENOMEM; - if (unlikely(!skb)) { - dev->stats.rx_missed_errors++; - goto out_dropped; - } - - copy_len = (len + VNET_PACKET_SKIP + 7U) & ~7U; - skb_put(skb, copy_len); - err = ldc_copy(port->vio.lp, LDC_COPY_IN, - skb->data, copy_len, 0, - cookies, ncookies); - if (unlikely(err < 0)) { - dev->stats.rx_frame_errors++; - goto out_free_skb; - } - - skb_pull(skb, VNET_PACKET_SKIP); - skb_trim(skb, len); - skb->protocol = eth_type_trans(skb, dev); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; - - netif_rx(skb); - - return 0; - -out_free_skb: - kfree_skb(skb); - -out_dropped: - dev->stats.rx_dropped++; - return err; -} - -static int vnet_send_ack(struct vnet_port *port, struct vio_dring_state *dr, - u32 start, u32 end, u8 vio_dring_state) -{ - struct vio_dring_data hdr = { - .tag = { - .type = VIO_TYPE_DATA, - .stype = VIO_SUBTYPE_ACK, - .stype_env = VIO_DRING_DATA, - .sid = vio_send_sid(&port->vio), - }, - .dring_ident = dr->ident, - .start_idx = start, - .end_idx = end, - .state = vio_dring_state, - }; - int err, delay; - - hdr.seq = dr->snd_nxt; - delay = 1; - do { - err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr)); - if (err > 0) { - dr->snd_nxt++; - break; - } - udelay(delay); - if ((delay <<= 1) > 128) - delay = 128; - } while (err == -EAGAIN); - - return err; -} - -static u32 next_idx(u32 idx, struct vio_dring_state *dr) -{ - if (++idx == dr->num_entries) - idx = 0; - return idx; -} - -static u32 prev_idx(u32 idx, struct vio_dring_state *dr) -{ - if (idx == 0) - idx = dr->num_entries - 1; - else - idx--; - - return idx; -} - -static struct vio_net_desc *get_rx_desc(struct vnet_port *port, - struct vio_dring_state *dr, - u32 index) -{ - struct vio_net_desc *desc = port->vio.desc_buf; - int err; - - err = ldc_get_dring_entry(port->vio.lp, desc, dr->entry_size, - (index * dr->entry_size), - dr->cookies, dr->ncookies); - if (err < 0) - return ERR_PTR(err); - - return desc; -} - -static int put_rx_desc(struct vnet_port *port, - struct vio_dring_state *dr, - struct vio_net_desc *desc, - u32 index) -{ - int err; - - err = ldc_put_dring_entry(port->vio.lp, desc, dr->entry_size, - (index * dr->entry_size), - dr->cookies, dr->ncookies); - if (err < 0) - return err; - - return 0; -} - -static int vnet_walk_rx_one(struct vnet_port *port, - struct vio_dring_state *dr, - u32 index, int *needs_ack) -{ - struct vio_net_desc *desc = get_rx_desc(port, dr, index); - struct vio_driver_state *vio = &port->vio; - int err; - - if (IS_ERR(desc)) - return PTR_ERR(desc); - - viodbg(DATA, "vio_walk_rx_one desc[%02x:%02x:%08x:%08x:%llx:%llx]\n", - desc->hdr.state, desc->hdr.ack, - desc->size, desc->ncookies, - desc->cookies[0].cookie_addr, - desc->cookies[0].cookie_size); - - if (desc->hdr.state != VIO_DESC_READY) - return 1; - err = vnet_rx_one(port, desc->size, desc->cookies, desc->ncookies); - if (err == -ECONNRESET) - return err; - desc->hdr.state = VIO_DESC_DONE; - err = put_rx_desc(port, dr, desc, index); - if (err < 0) - return err; - *needs_ack = desc->hdr.ack; - return 0; -} - -static int vnet_walk_rx(struct vnet_port *port, struct vio_dring_state *dr, - u32 start, u32 end) -{ - struct vio_driver_state *vio = &port->vio; - int ack_start = -1, ack_end = -1; - - end = (end == (u32) -1) ? prev_idx(start, dr) : next_idx(end, dr); - - viodbg(DATA, "vnet_walk_rx start[%08x] end[%08x]\n", start, end); - - while (start != end) { - int ack = 0, err = vnet_walk_rx_one(port, dr, start, &ack); - if (err == -ECONNRESET) - return err; - if (err != 0) - break; - if (ack_start == -1) - ack_start = start; - ack_end = start; - start = next_idx(start, dr); - if (ack && start != end) { - err = vnet_send_ack(port, dr, ack_start, ack_end, - VIO_DRING_ACTIVE); - if (err == -ECONNRESET) - return err; - ack_start = -1; - } - } - if (unlikely(ack_start == -1)) - ack_start = ack_end = prev_idx(start, dr); - return vnet_send_ack(port, dr, ack_start, ack_end, VIO_DRING_STOPPED); -} - -static int vnet_rx(struct vnet_port *port, void *msgbuf) -{ - struct vio_dring_data *pkt = msgbuf; - struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_RX_RING]; - struct vio_driver_state *vio = &port->vio; - - viodbg(DATA, "vnet_rx stype_env[%04x] seq[%016llx] rcv_nxt[%016llx]\n", - pkt->tag.stype_env, pkt->seq, dr->rcv_nxt); - - if (unlikely(pkt->tag.stype_env != VIO_DRING_DATA)) - return 0; - if (unlikely(pkt->seq != dr->rcv_nxt)) { - pr_err("RX out of sequence seq[0x%llx] rcv_nxt[0x%llx]\n", - pkt->seq, dr->rcv_nxt); - return 0; - } - - dr->rcv_nxt++; - - /* XXX Validate pkt->start_idx and pkt->end_idx XXX */ - - return vnet_walk_rx(port, dr, pkt->start_idx, pkt->end_idx); -} - -static int idx_is_pending(struct vio_dring_state *dr, u32 end) -{ - u32 idx = dr->cons; - int found = 0; - - while (idx != dr->prod) { - if (idx == end) { - found = 1; - break; - } - idx = next_idx(idx, dr); - } - return found; -} - -static int vnet_ack(struct vnet_port *port, void *msgbuf) -{ - struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; - struct vio_dring_data *pkt = msgbuf; - struct net_device *dev; - struct vnet *vp; - u32 end; - - if (unlikely(pkt->tag.stype_env != VIO_DRING_DATA)) - return 0; - - end = pkt->end_idx; - if (unlikely(!idx_is_pending(dr, end))) - return 0; - - dr->cons = next_idx(end, dr); - - vp = port->vp; - dev = vp->dev; - if (unlikely(netif_queue_stopped(dev) && - vnet_tx_dring_avail(dr) >= VNET_TX_WAKEUP_THRESH(dr))) - return 1; - - return 0; -} - -static int vnet_nack(struct vnet_port *port, void *msgbuf) -{ - /* XXX just reset or similar XXX */ - return 0; -} - -static int handle_mcast(struct vnet_port *port, void *msgbuf) -{ - struct vio_net_mcast_info *pkt = msgbuf; - - if (pkt->tag.stype != VIO_SUBTYPE_ACK) - pr_err("%s: Got unexpected MCAST reply [%02x:%02x:%04x:%08x]\n", - port->vp->dev->name, - pkt->tag.type, - pkt->tag.stype, - pkt->tag.stype_env, - pkt->tag.sid); - - return 0; -} - -static void maybe_tx_wakeup(struct vnet *vp) -{ - struct net_device *dev = vp->dev; - - netif_tx_lock(dev); - if (likely(netif_queue_stopped(dev))) { - struct vnet_port *port; - int wake = 1; - - list_for_each_entry(port, &vp->port_list, list) { - struct vio_dring_state *dr; - - dr = &port->vio.drings[VIO_DRIVER_TX_RING]; - if (vnet_tx_dring_avail(dr) < - VNET_TX_WAKEUP_THRESH(dr)) { - wake = 0; - break; - } - } - if (wake) - netif_wake_queue(dev); - } - netif_tx_unlock(dev); -} - -static void vnet_event(void *arg, int event) -{ - struct vnet_port *port = arg; - struct vio_driver_state *vio = &port->vio; - unsigned long flags; - int tx_wakeup, err; - - spin_lock_irqsave(&vio->lock, flags); - - if (unlikely(event == LDC_EVENT_RESET || - event == LDC_EVENT_UP)) { - vio_link_state_change(vio, event); - spin_unlock_irqrestore(&vio->lock, flags); - - if (event == LDC_EVENT_RESET) - vio_port_up(vio); - return; - } - - if (unlikely(event != LDC_EVENT_DATA_READY)) { - pr_warning("Unexpected LDC event %d\n", event); - spin_unlock_irqrestore(&vio->lock, flags); - return; - } - - tx_wakeup = err = 0; - while (1) { - union { - struct vio_msg_tag tag; - u64 raw[8]; - } msgbuf; - - err = ldc_read(vio->lp, &msgbuf, sizeof(msgbuf)); - if (unlikely(err < 0)) { - if (err == -ECONNRESET) - vio_conn_reset(vio); - break; - } - if (err == 0) - break; - viodbg(DATA, "TAG [%02x:%02x:%04x:%08x]\n", - msgbuf.tag.type, - msgbuf.tag.stype, - msgbuf.tag.stype_env, - msgbuf.tag.sid); - err = vio_validate_sid(vio, &msgbuf.tag); - if (err < 0) - break; - - if (likely(msgbuf.tag.type == VIO_TYPE_DATA)) { - if (msgbuf.tag.stype == VIO_SUBTYPE_INFO) { - err = vnet_rx(port, &msgbuf); - } else if (msgbuf.tag.stype == VIO_SUBTYPE_ACK) { - err = vnet_ack(port, &msgbuf); - if (err > 0) - tx_wakeup |= err; - } else if (msgbuf.tag.stype == VIO_SUBTYPE_NACK) { - err = vnet_nack(port, &msgbuf); - } - } else if (msgbuf.tag.type == VIO_TYPE_CTRL) { - if (msgbuf.tag.stype_env == VNET_MCAST_INFO) - err = handle_mcast(port, &msgbuf); - else - err = vio_control_pkt_engine(vio, &msgbuf); - if (err) - break; - } else { - err = vnet_handle_unknown(port, &msgbuf); - } - if (err == -ECONNRESET) - break; - } - spin_unlock(&vio->lock); - if (unlikely(tx_wakeup && err != -ECONNRESET)) - maybe_tx_wakeup(port->vp); - local_irq_restore(flags); -} - -static int __vnet_tx_trigger(struct vnet_port *port) -{ - struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; - struct vio_dring_data hdr = { - .tag = { - .type = VIO_TYPE_DATA, - .stype = VIO_SUBTYPE_INFO, - .stype_env = VIO_DRING_DATA, - .sid = vio_send_sid(&port->vio), - }, - .dring_ident = dr->ident, - .start_idx = dr->prod, - .end_idx = (u32) -1, - }; - int err, delay; - - hdr.seq = dr->snd_nxt; - delay = 1; - do { - err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr)); - if (err > 0) { - dr->snd_nxt++; - break; - } - udelay(delay); - if ((delay <<= 1) > 128) - delay = 128; - } while (err == -EAGAIN); - - return err; -} - -struct vnet_port *__tx_port_find(struct vnet *vp, struct sk_buff *skb) -{ - unsigned int hash = vnet_hashfn(skb->data); - struct hlist_head *hp = &vp->port_hash[hash]; - struct hlist_node *n; - struct vnet_port *port; - - hlist_for_each_entry(port, n, hp, hash) { - if (!compare_ether_addr(port->raddr, skb->data)) - return port; - } - port = NULL; - if (!list_empty(&vp->port_list)) - port = list_entry(vp->port_list.next, struct vnet_port, list); - - return port; -} - -struct vnet_port *tx_port_find(struct vnet *vp, struct sk_buff *skb) -{ - struct vnet_port *ret; - unsigned long flags; - - spin_lock_irqsave(&vp->lock, flags); - ret = __tx_port_find(vp, skb); - spin_unlock_irqrestore(&vp->lock, flags); - - return ret; -} - -static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct vnet *vp = netdev_priv(dev); - struct vnet_port *port = tx_port_find(vp, skb); - struct vio_dring_state *dr; - struct vio_net_desc *d; - unsigned long flags; - unsigned int len; - void *tx_buf; - int i, err; - - if (unlikely(!port)) - goto out_dropped; - - spin_lock_irqsave(&port->vio.lock, flags); - - dr = &port->vio.drings[VIO_DRIVER_TX_RING]; - if (unlikely(vnet_tx_dring_avail(dr) < 2)) { - if (!netif_queue_stopped(dev)) { - netif_stop_queue(dev); - - /* This is a hard error, log it. */ - netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); - dev->stats.tx_errors++; - } - spin_unlock_irqrestore(&port->vio.lock, flags); - return NETDEV_TX_BUSY; - } - - d = vio_dring_cur(dr); - - tx_buf = port->tx_bufs[dr->prod].buf; - skb_copy_from_linear_data(skb, tx_buf + VNET_PACKET_SKIP, skb->len); - - len = skb->len; - if (len < ETH_ZLEN) { - len = ETH_ZLEN; - memset(tx_buf+VNET_PACKET_SKIP+skb->len, 0, len - skb->len); - } - - d->hdr.ack = VIO_ACK_ENABLE; - d->size = len; - d->ncookies = port->tx_bufs[dr->prod].ncookies; - for (i = 0; i < d->ncookies; i++) - d->cookies[i] = port->tx_bufs[dr->prod].cookies[i]; - - /* This has to be a non-SMP write barrier because we are writing - * to memory which is shared with the peer LDOM. - */ - wmb(); - - d->hdr.state = VIO_DESC_READY; - - err = __vnet_tx_trigger(port); - if (unlikely(err < 0)) { - netdev_info(dev, "TX trigger error %d\n", err); - d->hdr.state = VIO_DESC_FREE; - dev->stats.tx_carrier_errors++; - goto out_dropped_unlock; - } - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1); - if (unlikely(vnet_tx_dring_avail(dr) < 2)) { - netif_stop_queue(dev); - if (vnet_tx_dring_avail(dr) > VNET_TX_WAKEUP_THRESH(dr)) - netif_wake_queue(dev); - } - - spin_unlock_irqrestore(&port->vio.lock, flags); - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; - -out_dropped_unlock: - spin_unlock_irqrestore(&port->vio.lock, flags); - -out_dropped: - dev_kfree_skb(skb); - dev->stats.tx_dropped++; - return NETDEV_TX_OK; -} - -static void vnet_tx_timeout(struct net_device *dev) -{ - /* XXX Implement me XXX */ -} - -static int vnet_open(struct net_device *dev) -{ - netif_carrier_on(dev); - netif_start_queue(dev); - - return 0; -} - -static int vnet_close(struct net_device *dev) -{ - netif_stop_queue(dev); - netif_carrier_off(dev); - - return 0; -} - -static struct vnet_mcast_entry *__vnet_mc_find(struct vnet *vp, u8 *addr) -{ - struct vnet_mcast_entry *m; - - for (m = vp->mcast_list; m; m = m->next) { - if (!memcmp(m->addr, addr, ETH_ALEN)) - return m; - } - return NULL; -} - -static void __update_mc_list(struct vnet *vp, struct net_device *dev) -{ - struct netdev_hw_addr *ha; - - netdev_for_each_mc_addr(ha, dev) { - struct vnet_mcast_entry *m; - - m = __vnet_mc_find(vp, ha->addr); - if (m) { - m->hit = 1; - continue; - } - - if (!m) { - m = kzalloc(sizeof(*m), GFP_ATOMIC); - if (!m) - continue; - memcpy(m->addr, ha->addr, ETH_ALEN); - m->hit = 1; - - m->next = vp->mcast_list; - vp->mcast_list = m; - } - } -} - -static void __send_mc_list(struct vnet *vp, struct vnet_port *port) -{ - struct vio_net_mcast_info info; - struct vnet_mcast_entry *m, **pp; - int n_addrs; - - memset(&info, 0, sizeof(info)); - - info.tag.type = VIO_TYPE_CTRL; - info.tag.stype = VIO_SUBTYPE_INFO; - info.tag.stype_env = VNET_MCAST_INFO; - info.tag.sid = vio_send_sid(&port->vio); - info.set = 1; - - n_addrs = 0; - for (m = vp->mcast_list; m; m = m->next) { - if (m->sent) - continue; - m->sent = 1; - memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], - m->addr, ETH_ALEN); - if (++n_addrs == VNET_NUM_MCAST) { - info.count = n_addrs; - - (void) vio_ldc_send(&port->vio, &info, - sizeof(info)); - n_addrs = 0; - } - } - if (n_addrs) { - info.count = n_addrs; - (void) vio_ldc_send(&port->vio, &info, sizeof(info)); - } - - info.set = 0; - - n_addrs = 0; - pp = &vp->mcast_list; - while ((m = *pp) != NULL) { - if (m->hit) { - m->hit = 0; - pp = &m->next; - continue; - } - - memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], - m->addr, ETH_ALEN); - if (++n_addrs == VNET_NUM_MCAST) { - info.count = n_addrs; - (void) vio_ldc_send(&port->vio, &info, - sizeof(info)); - n_addrs = 0; - } - - *pp = m->next; - kfree(m); - } - if (n_addrs) { - info.count = n_addrs; - (void) vio_ldc_send(&port->vio, &info, sizeof(info)); - } -} - -static void vnet_set_rx_mode(struct net_device *dev) -{ - struct vnet *vp = netdev_priv(dev); - struct vnet_port *port; - unsigned long flags; - - spin_lock_irqsave(&vp->lock, flags); - if (!list_empty(&vp->port_list)) { - port = list_entry(vp->port_list.next, struct vnet_port, list); - - if (port->switch_port) { - __update_mc_list(vp, dev); - __send_mc_list(vp, port); - } - } - spin_unlock_irqrestore(&vp->lock, flags); -} - -static int vnet_change_mtu(struct net_device *dev, int new_mtu) -{ - if (new_mtu != ETH_DATA_LEN) - return -EINVAL; - - dev->mtu = new_mtu; - return 0; -} - -static int vnet_set_mac_addr(struct net_device *dev, void *p) -{ - return -EINVAL; -} - -static void vnet_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strcpy(info->driver, DRV_MODULE_NAME); - strcpy(info->version, DRV_MODULE_VERSION); -} - -static u32 vnet_get_msglevel(struct net_device *dev) -{ - struct vnet *vp = netdev_priv(dev); - return vp->msg_enable; -} - -static void vnet_set_msglevel(struct net_device *dev, u32 value) -{ - struct vnet *vp = netdev_priv(dev); - vp->msg_enable = value; -} - -static const struct ethtool_ops vnet_ethtool_ops = { - .get_drvinfo = vnet_get_drvinfo, - .get_msglevel = vnet_get_msglevel, - .set_msglevel = vnet_set_msglevel, - .get_link = ethtool_op_get_link, -}; - -static void vnet_port_free_tx_bufs(struct vnet_port *port) -{ - struct vio_dring_state *dr; - int i; - - dr = &port->vio.drings[VIO_DRIVER_TX_RING]; - if (dr->base) { - ldc_free_exp_dring(port->vio.lp, dr->base, - (dr->entry_size * dr->num_entries), - dr->cookies, dr->ncookies); - dr->base = NULL; - dr->entry_size = 0; - dr->num_entries = 0; - dr->pending = 0; - dr->ncookies = 0; - } - - for (i = 0; i < VNET_TX_RING_SIZE; i++) { - void *buf = port->tx_bufs[i].buf; - - if (!buf) - continue; - - ldc_unmap(port->vio.lp, - port->tx_bufs[i].cookies, - port->tx_bufs[i].ncookies); - - kfree(buf); - port->tx_bufs[i].buf = NULL; - } -} - -static int __devinit vnet_port_alloc_tx_bufs(struct vnet_port *port) -{ - struct vio_dring_state *dr; - unsigned long len; - int i, err, ncookies; - void *dring; - - for (i = 0; i < VNET_TX_RING_SIZE; i++) { - void *buf = kzalloc(ETH_FRAME_LEN + 8, GFP_KERNEL); - int map_len = (ETH_FRAME_LEN + 7) & ~7; - - err = -ENOMEM; - if (!buf) { - pr_err("TX buffer allocation failure\n"); - goto err_out; - } - err = -EFAULT; - if ((unsigned long)buf & (8UL - 1)) { - pr_err("TX buffer misaligned\n"); - kfree(buf); - goto err_out; - } - - err = ldc_map_single(port->vio.lp, buf, map_len, - port->tx_bufs[i].cookies, 2, - (LDC_MAP_SHADOW | - LDC_MAP_DIRECT | - LDC_MAP_RW)); - if (err < 0) { - kfree(buf); - goto err_out; - } - port->tx_bufs[i].buf = buf; - port->tx_bufs[i].ncookies = err; - } - - dr = &port->vio.drings[VIO_DRIVER_TX_RING]; - - len = (VNET_TX_RING_SIZE * - (sizeof(struct vio_net_desc) + - (sizeof(struct ldc_trans_cookie) * 2))); - - ncookies = VIO_MAX_RING_COOKIES; - dring = ldc_alloc_exp_dring(port->vio.lp, len, - dr->cookies, &ncookies, - (LDC_MAP_SHADOW | - LDC_MAP_DIRECT | - LDC_MAP_RW)); - if (IS_ERR(dring)) { - err = PTR_ERR(dring); - goto err_out; - } - - dr->base = dring; - dr->entry_size = (sizeof(struct vio_net_desc) + - (sizeof(struct ldc_trans_cookie) * 2)); - dr->num_entries = VNET_TX_RING_SIZE; - dr->prod = dr->cons = 0; - dr->pending = VNET_TX_RING_SIZE; - dr->ncookies = ncookies; - - return 0; - -err_out: - vnet_port_free_tx_bufs(port); - - return err; -} - -static LIST_HEAD(vnet_list); -static DEFINE_MUTEX(vnet_list_mutex); - -static const struct net_device_ops vnet_ops = { - .ndo_open = vnet_open, - .ndo_stop = vnet_close, - .ndo_set_multicast_list = vnet_set_rx_mode, - .ndo_set_mac_address = vnet_set_mac_addr, - .ndo_validate_addr = eth_validate_addr, - .ndo_tx_timeout = vnet_tx_timeout, - .ndo_change_mtu = vnet_change_mtu, - .ndo_start_xmit = vnet_start_xmit, -}; - -static struct vnet * __devinit vnet_new(const u64 *local_mac) -{ - struct net_device *dev; - struct vnet *vp; - int err, i; - - dev = alloc_etherdev(sizeof(*vp)); - if (!dev) { - pr_err("Etherdev alloc failed, aborting\n"); - return ERR_PTR(-ENOMEM); - } - - for (i = 0; i < ETH_ALEN; i++) - dev->dev_addr[i] = (*local_mac >> (5 - i) * 8) & 0xff; - - memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); - - vp = netdev_priv(dev); - - spin_lock_init(&vp->lock); - vp->dev = dev; - - INIT_LIST_HEAD(&vp->port_list); - for (i = 0; i < VNET_PORT_HASH_SIZE; i++) - INIT_HLIST_HEAD(&vp->port_hash[i]); - INIT_LIST_HEAD(&vp->list); - vp->local_mac = *local_mac; - - dev->netdev_ops = &vnet_ops; - dev->ethtool_ops = &vnet_ethtool_ops; - dev->watchdog_timeo = VNET_TX_TIMEOUT; - - err = register_netdev(dev); - if (err) { - pr_err("Cannot register net device, aborting\n"); - goto err_out_free_dev; - } - - netdev_info(dev, "Sun LDOM vnet %pM\n", dev->dev_addr); - - list_add(&vp->list, &vnet_list); - - return vp; - -err_out_free_dev: - free_netdev(dev); - - return ERR_PTR(err); -} - -static struct vnet * __devinit vnet_find_or_create(const u64 *local_mac) -{ - struct vnet *iter, *vp; - - mutex_lock(&vnet_list_mutex); - vp = NULL; - list_for_each_entry(iter, &vnet_list, list) { - if (iter->local_mac == *local_mac) { - vp = iter; - break; - } - } - if (!vp) - vp = vnet_new(local_mac); - mutex_unlock(&vnet_list_mutex); - - return vp; -} - -static const char *local_mac_prop = "local-mac-address"; - -static struct vnet * __devinit vnet_find_parent(struct mdesc_handle *hp, - u64 port_node) -{ - const u64 *local_mac = NULL; - u64 a; - - mdesc_for_each_arc(a, hp, port_node, MDESC_ARC_TYPE_BACK) { - u64 target = mdesc_arc_target(hp, a); - const char *name; - - name = mdesc_get_property(hp, target, "name", NULL); - if (!name || strcmp(name, "network")) - continue; - - local_mac = mdesc_get_property(hp, target, - local_mac_prop, NULL); - if (local_mac) - break; - } - if (!local_mac) - return ERR_PTR(-ENODEV); - - return vnet_find_or_create(local_mac); -} - -static struct ldc_channel_config vnet_ldc_cfg = { - .event = vnet_event, - .mtu = 64, - .mode = LDC_MODE_UNRELIABLE, -}; - -static struct vio_driver_ops vnet_vio_ops = { - .send_attr = vnet_send_attr, - .handle_attr = vnet_handle_attr, - .handshake_complete = vnet_handshake_complete, -}; - -static void __devinit print_version(void) -{ - printk_once(KERN_INFO "%s", version); -} - -const char *remote_macaddr_prop = "remote-mac-address"; - -static int __devinit vnet_port_probe(struct vio_dev *vdev, - const struct vio_device_id *id) -{ - struct mdesc_handle *hp; - struct vnet_port *port; - unsigned long flags; - struct vnet *vp; - const u64 *rmac; - int len, i, err, switch_port; - - print_version(); - - hp = mdesc_grab(); - - vp = vnet_find_parent(hp, vdev->mp); - if (IS_ERR(vp)) { - pr_err("Cannot find port parent vnet\n"); - err = PTR_ERR(vp); - goto err_out_put_mdesc; - } - - rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len); - err = -ENODEV; - if (!rmac) { - pr_err("Port lacks %s property\n", remote_macaddr_prop); - goto err_out_put_mdesc; - } - - port = kzalloc(sizeof(*port), GFP_KERNEL); - err = -ENOMEM; - if (!port) { - pr_err("Cannot allocate vnet_port\n"); - goto err_out_put_mdesc; - } - - for (i = 0; i < ETH_ALEN; i++) - port->raddr[i] = (*rmac >> (5 - i) * 8) & 0xff; - - port->vp = vp; - - err = vio_driver_init(&port->vio, vdev, VDEV_NETWORK, - vnet_versions, ARRAY_SIZE(vnet_versions), - &vnet_vio_ops, vp->dev->name); - if (err) - goto err_out_free_port; - - err = vio_ldc_alloc(&port->vio, &vnet_ldc_cfg, port); - if (err) - goto err_out_free_port; - - err = vnet_port_alloc_tx_bufs(port); - if (err) - goto err_out_free_ldc; - - INIT_HLIST_NODE(&port->hash); - INIT_LIST_HEAD(&port->list); - - switch_port = 0; - if (mdesc_get_property(hp, vdev->mp, "switch-port", NULL) != NULL) - switch_port = 1; - port->switch_port = switch_port; - - spin_lock_irqsave(&vp->lock, flags); - if (switch_port) - list_add(&port->list, &vp->port_list); - else - list_add_tail(&port->list, &vp->port_list); - hlist_add_head(&port->hash, &vp->port_hash[vnet_hashfn(port->raddr)]); - spin_unlock_irqrestore(&vp->lock, flags); - - dev_set_drvdata(&vdev->dev, port); - - pr_info("%s: PORT ( remote-mac %pM%s )\n", - vp->dev->name, port->raddr, switch_port ? " switch-port" : ""); - - vio_port_up(&port->vio); - - mdesc_release(hp); - - return 0; - -err_out_free_ldc: - vio_ldc_free(&port->vio); - -err_out_free_port: - kfree(port); - -err_out_put_mdesc: - mdesc_release(hp); - return err; -} - -static int vnet_port_remove(struct vio_dev *vdev) -{ - struct vnet_port *port = dev_get_drvdata(&vdev->dev); - - if (port) { - struct vnet *vp = port->vp; - unsigned long flags; - - del_timer_sync(&port->vio.timer); - - spin_lock_irqsave(&vp->lock, flags); - list_del(&port->list); - hlist_del(&port->hash); - spin_unlock_irqrestore(&vp->lock, flags); - - vnet_port_free_tx_bufs(port); - vio_ldc_free(&port->vio); - - dev_set_drvdata(&vdev->dev, NULL); - - kfree(port); - } - return 0; -} - -static const struct vio_device_id vnet_port_match[] = { - { - .type = "vnet-port", - }, - {}, -}; -MODULE_DEVICE_TABLE(vio, vnet_port_match); - -static struct vio_driver vnet_port_driver = { - .id_table = vnet_port_match, - .probe = vnet_port_probe, - .remove = vnet_port_remove, - .driver = { - .name = "vnet_port", - .owner = THIS_MODULE, - } -}; - -static int __init vnet_init(void) -{ - return vio_register_driver(&vnet_port_driver); -} - -static void __exit vnet_exit(void) -{ - vio_unregister_driver(&vnet_port_driver); -} - -module_init(vnet_init); -module_exit(vnet_exit); |