162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * (C) Copyright 2020 Hewlett Packard Enterprise Development LP 762306a36Sopenharmony_ci * Copyright (C) 1999-2009 Silicon Graphics, Inc. All rights reserved. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * Cross Partition Network Interface (XPNET) support 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * XPNET provides a virtual network layered on top of the Cross 1462306a36Sopenharmony_ci * Partition communication layer. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * XPNET provides direct point-to-point and broadcast-like support 1762306a36Sopenharmony_ci * for an ethernet-like device. The ethernet broadcast medium is 1862306a36Sopenharmony_ci * replaced with a point-to-point message structure which passes 1962306a36Sopenharmony_ci * pointers to a DMA-capable block that a remote partition should 2062306a36Sopenharmony_ci * retrieve and pass to the upper level networking layer. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/netdevice.h> 2762306a36Sopenharmony_ci#include <linux/etherdevice.h> 2862306a36Sopenharmony_ci#include "xp.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * The message payload transferred by XPC. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * buf_pa is the physical address where the DMA should pull from. 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * NOTE: for performance reasons, buf_pa should _ALWAYS_ begin on a 3662306a36Sopenharmony_ci * cacheline boundary. To accomplish this, we record the number of 3762306a36Sopenharmony_ci * bytes from the beginning of the first cacheline to the first useful 3862306a36Sopenharmony_ci * byte of the skb (leadin_ignore) and the number of bytes from the 3962306a36Sopenharmony_ci * last useful byte of the skb to the end of the last cacheline 4062306a36Sopenharmony_ci * (tailout_ignore). 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * size is the number of bytes to transfer which includes the skb->len 4362306a36Sopenharmony_ci * (useful bytes of the senders skb) plus the leadin and tailout 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistruct xpnet_message { 4662306a36Sopenharmony_ci u16 version; /* Version for this message */ 4762306a36Sopenharmony_ci u16 embedded_bytes; /* #of bytes embedded in XPC message */ 4862306a36Sopenharmony_ci u32 magic; /* Special number indicating this is xpnet */ 4962306a36Sopenharmony_ci unsigned long buf_pa; /* phys address of buffer to retrieve */ 5062306a36Sopenharmony_ci u32 size; /* #of bytes in buffer */ 5162306a36Sopenharmony_ci u8 leadin_ignore; /* #of bytes to ignore at the beginning */ 5262306a36Sopenharmony_ci u8 tailout_ignore; /* #of bytes to ignore at the end */ 5362306a36Sopenharmony_ci unsigned char data; /* body of small packets */ 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* 5762306a36Sopenharmony_ci * Determine the size of our message, the cacheline aligned size, 5862306a36Sopenharmony_ci * and then the number of message will request from XPC. 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * XPC expects each message to exist in an individual cacheline. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci#define XPNET_MSG_SIZE XPC_MSG_PAYLOAD_MAX_SIZE 6362306a36Sopenharmony_ci#define XPNET_MSG_DATA_MAX \ 6462306a36Sopenharmony_ci (XPNET_MSG_SIZE - offsetof(struct xpnet_message, data)) 6562306a36Sopenharmony_ci#define XPNET_MSG_NENTRIES (PAGE_SIZE / XPC_MSG_MAX_SIZE) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define XPNET_MAX_KTHREADS (XPNET_MSG_NENTRIES + 1) 6862306a36Sopenharmony_ci#define XPNET_MAX_IDLE_KTHREADS (XPNET_MSG_NENTRIES + 1) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * Version number of XPNET implementation. XPNET can always talk to versions 7262306a36Sopenharmony_ci * with same major #, and never talk to versions with a different version. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci#define _XPNET_VERSION(_major, _minor) (((_major) << 4) | (_minor)) 7562306a36Sopenharmony_ci#define XPNET_VERSION_MAJOR(_v) ((_v) >> 4) 7662306a36Sopenharmony_ci#define XPNET_VERSION_MINOR(_v) ((_v) & 0xf) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define XPNET_VERSION _XPNET_VERSION(1, 0) /* version 1.0 */ 7962306a36Sopenharmony_ci#define XPNET_VERSION_EMBED _XPNET_VERSION(1, 1) /* version 1.1 */ 8062306a36Sopenharmony_ci#define XPNET_MAGIC 0x88786984 /* "XNET" */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define XPNET_VALID_MSG(_m) \ 8362306a36Sopenharmony_ci ((XPNET_VERSION_MAJOR(_m->version) == XPNET_VERSION_MAJOR(XPNET_VERSION)) \ 8462306a36Sopenharmony_ci && (msg->magic == XPNET_MAGIC)) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define XPNET_DEVICE_NAME "xp0" 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci * When messages are queued with xpc_send_notify, a kmalloc'd buffer 9062306a36Sopenharmony_ci * of the following type is passed as a notification cookie. When the 9162306a36Sopenharmony_ci * notification function is called, we use the cookie to decide 9262306a36Sopenharmony_ci * whether all outstanding message sends have completed. The skb can 9362306a36Sopenharmony_ci * then be released. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cistruct xpnet_pending_msg { 9662306a36Sopenharmony_ci struct sk_buff *skb; 9762306a36Sopenharmony_ci atomic_t use_count; 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic struct net_device *xpnet_device; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * When we are notified of other partitions activating, we add them to 10462306a36Sopenharmony_ci * our bitmask of partitions to which we broadcast. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_cistatic unsigned long *xpnet_broadcast_partitions; 10762306a36Sopenharmony_ci/* protect above */ 10862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(xpnet_broadcast_lock); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* 11162306a36Sopenharmony_ci * Since the Block Transfer Engine (BTE) is being used for the transfer 11262306a36Sopenharmony_ci * and it relies upon cache-line size transfers, we need to reserve at 11362306a36Sopenharmony_ci * least one cache-line for head and tail alignment. The BTE is 11462306a36Sopenharmony_ci * limited to 8MB transfers. 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * Testing has shown that changing MTU to greater than 64KB has no effect 11762306a36Sopenharmony_ci * on TCP as the two sides negotiate a Max Segment Size that is limited 11862306a36Sopenharmony_ci * to 64K. Other protocols May use packets greater than this, but for 11962306a36Sopenharmony_ci * now, the default is 64KB. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci#define XPNET_MAX_MTU (0x800000UL - L1_CACHE_BYTES) 12262306a36Sopenharmony_ci/* 68 comes from min TCP+IP+MAC header */ 12362306a36Sopenharmony_ci#define XPNET_MIN_MTU 68 12462306a36Sopenharmony_ci/* 32KB has been determined to be the ideal */ 12562306a36Sopenharmony_ci#define XPNET_DEF_MTU (0x8000UL) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* 12862306a36Sopenharmony_ci * The partid is encapsulated in the MAC address beginning in the following 12962306a36Sopenharmony_ci * octet and it consists of two octets. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci#define XPNET_PARTID_OCTET 2 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* Define the XPNET debug device structures to be used with dev_dbg() et al */ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic struct device_driver xpnet_dbg_name = { 13662306a36Sopenharmony_ci .name = "xpnet" 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic struct device xpnet_dbg_subname = { 14062306a36Sopenharmony_ci .init_name = "", /* set to "" */ 14162306a36Sopenharmony_ci .driver = &xpnet_dbg_name 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic struct device *xpnet = &xpnet_dbg_subname; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* 14762306a36Sopenharmony_ci * Packet was recevied by XPC and forwarded to us. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cistatic void 15062306a36Sopenharmony_cixpnet_receive(short partid, int channel, struct xpnet_message *msg) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct sk_buff *skb; 15362306a36Sopenharmony_ci void *dst; 15462306a36Sopenharmony_ci enum xp_retval ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (!XPNET_VALID_MSG(msg)) { 15762306a36Sopenharmony_ci /* 15862306a36Sopenharmony_ci * Packet with a different XPC version. Ignore. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci xpc_received(partid, channel, (void *)msg); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci xpnet_device->stats.rx_errors++; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci dev_dbg(xpnet, "received 0x%lx, %d, %d, %d\n", msg->buf_pa, msg->size, 16762306a36Sopenharmony_ci msg->leadin_ignore, msg->tailout_ignore); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* reserve an extra cache line */ 17062306a36Sopenharmony_ci skb = dev_alloc_skb(msg->size + L1_CACHE_BYTES); 17162306a36Sopenharmony_ci if (!skb) { 17262306a36Sopenharmony_ci dev_err(xpnet, "failed on dev_alloc_skb(%d)\n", 17362306a36Sopenharmony_ci msg->size + L1_CACHE_BYTES); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci xpc_received(partid, channel, (void *)msg); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci xpnet_device->stats.rx_errors++; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* 18362306a36Sopenharmony_ci * The allocated skb has some reserved space. 18462306a36Sopenharmony_ci * In order to use xp_remote_memcpy(), we need to get the 18562306a36Sopenharmony_ci * skb->data pointer moved forward. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci skb_reserve(skb, (L1_CACHE_BYTES - ((u64)skb->data & 18862306a36Sopenharmony_ci (L1_CACHE_BYTES - 1)) + 18962306a36Sopenharmony_ci msg->leadin_ignore)); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * Update the tail pointer to indicate data actually 19362306a36Sopenharmony_ci * transferred. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ci skb_put(skb, (msg->size - msg->leadin_ignore - msg->tailout_ignore)); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * Move the data over from the other side. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci if ((XPNET_VERSION_MINOR(msg->version) == 1) && 20162306a36Sopenharmony_ci (msg->embedded_bytes != 0)) { 20262306a36Sopenharmony_ci dev_dbg(xpnet, "copying embedded message. memcpy(0x%p, 0x%p, " 20362306a36Sopenharmony_ci "%lu)\n", skb->data, &msg->data, 20462306a36Sopenharmony_ci (size_t)msg->embedded_bytes); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci skb_copy_to_linear_data(skb, &msg->data, 20762306a36Sopenharmony_ci (size_t)msg->embedded_bytes); 20862306a36Sopenharmony_ci } else { 20962306a36Sopenharmony_ci dst = (void *)((u64)skb->data & ~(L1_CACHE_BYTES - 1)); 21062306a36Sopenharmony_ci dev_dbg(xpnet, "transferring buffer to the skb->data area;\n\t" 21162306a36Sopenharmony_ci "xp_remote_memcpy(0x%p, 0x%p, %u)\n", dst, 21262306a36Sopenharmony_ci (void *)msg->buf_pa, msg->size); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci ret = xp_remote_memcpy(xp_pa(dst), msg->buf_pa, msg->size); 21562306a36Sopenharmony_ci if (ret != xpSuccess) { 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * !!! Need better way of cleaning skb. Currently skb 21862306a36Sopenharmony_ci * !!! appears in_use and we can't just call 21962306a36Sopenharmony_ci * !!! dev_kfree_skb. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci dev_err(xpnet, "xp_remote_memcpy(0x%p, 0x%p, 0x%x) " 22262306a36Sopenharmony_ci "returned error=0x%x\n", dst, 22362306a36Sopenharmony_ci (void *)msg->buf_pa, msg->size, ret); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci xpc_received(partid, channel, (void *)msg); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci xpnet_device->stats.rx_errors++; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci dev_dbg(xpnet, "<skb->head=0x%p skb->data=0x%p skb->tail=0x%p " 23462306a36Sopenharmony_ci "skb->end=0x%p skb->len=%d\n", (void *)skb->head, 23562306a36Sopenharmony_ci (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), 23662306a36Sopenharmony_ci skb->len); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, xpnet_device); 23962306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci dev_dbg(xpnet, "passing skb to network layer\n" 24262306a36Sopenharmony_ci "\tskb->head=0x%p skb->data=0x%p skb->tail=0x%p " 24362306a36Sopenharmony_ci "skb->end=0x%p skb->len=%d\n", 24462306a36Sopenharmony_ci (void *)skb->head, (void *)skb->data, skb_tail_pointer(skb), 24562306a36Sopenharmony_ci skb_end_pointer(skb), skb->len); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci xpnet_device->stats.rx_packets++; 24862306a36Sopenharmony_ci xpnet_device->stats.rx_bytes += skb->len + ETH_HLEN; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci netif_rx(skb); 25162306a36Sopenharmony_ci xpc_received(partid, channel, (void *)msg); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* 25562306a36Sopenharmony_ci * This is the handler which XPC calls during any sort of change in 25662306a36Sopenharmony_ci * state or message reception on a connection. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_cistatic void 25962306a36Sopenharmony_cixpnet_connection_activity(enum xp_retval reason, short partid, int channel, 26062306a36Sopenharmony_ci void *data, void *key) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci DBUG_ON(partid < 0 || partid >= xp_max_npartitions); 26362306a36Sopenharmony_ci DBUG_ON(channel != XPC_NET_CHANNEL); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci switch (reason) { 26662306a36Sopenharmony_ci case xpMsgReceived: /* message received */ 26762306a36Sopenharmony_ci DBUG_ON(data == NULL); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci xpnet_receive(partid, channel, (struct xpnet_message *)data); 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci case xpConnected: /* connection completed to a partition */ 27362306a36Sopenharmony_ci spin_lock_bh(&xpnet_broadcast_lock); 27462306a36Sopenharmony_ci __set_bit(partid, xpnet_broadcast_partitions); 27562306a36Sopenharmony_ci spin_unlock_bh(&xpnet_broadcast_lock); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci netif_carrier_on(xpnet_device); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci dev_dbg(xpnet, "%s connected to partition %d\n", 28062306a36Sopenharmony_ci xpnet_device->name, partid); 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci default: 28462306a36Sopenharmony_ci spin_lock_bh(&xpnet_broadcast_lock); 28562306a36Sopenharmony_ci __clear_bit(partid, xpnet_broadcast_partitions); 28662306a36Sopenharmony_ci spin_unlock_bh(&xpnet_broadcast_lock); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (bitmap_empty(xpnet_broadcast_partitions, 28962306a36Sopenharmony_ci xp_max_npartitions)) { 29062306a36Sopenharmony_ci netif_carrier_off(xpnet_device); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci dev_dbg(xpnet, "%s disconnected from partition %d\n", 29462306a36Sopenharmony_ci xpnet_device->name, partid); 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int 30062306a36Sopenharmony_cixpnet_dev_open(struct net_device *dev) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci enum xp_retval ret; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci dev_dbg(xpnet, "calling xpc_connect(%d, 0x%p, NULL, %ld, %ld, %ld, " 30562306a36Sopenharmony_ci "%ld)\n", XPC_NET_CHANNEL, xpnet_connection_activity, 30662306a36Sopenharmony_ci (unsigned long)XPNET_MSG_SIZE, 30762306a36Sopenharmony_ci (unsigned long)XPNET_MSG_NENTRIES, 30862306a36Sopenharmony_ci (unsigned long)XPNET_MAX_KTHREADS, 30962306a36Sopenharmony_ci (unsigned long)XPNET_MAX_IDLE_KTHREADS); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ret = xpc_connect(XPC_NET_CHANNEL, xpnet_connection_activity, NULL, 31262306a36Sopenharmony_ci XPNET_MSG_SIZE, XPNET_MSG_NENTRIES, 31362306a36Sopenharmony_ci XPNET_MAX_KTHREADS, XPNET_MAX_IDLE_KTHREADS); 31462306a36Sopenharmony_ci if (ret != xpSuccess) { 31562306a36Sopenharmony_ci dev_err(xpnet, "ifconfig up of %s failed on XPC connect, " 31662306a36Sopenharmony_ci "ret=%d\n", dev->name, ret); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return -ENOMEM; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci dev_dbg(xpnet, "ifconfig up of %s; XPC connected\n", dev->name); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int 32762306a36Sopenharmony_cixpnet_dev_stop(struct net_device *dev) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci xpc_disconnect(XPC_NET_CHANNEL); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci dev_dbg(xpnet, "ifconfig down of %s; XPC disconnected\n", dev->name); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci/* 33762306a36Sopenharmony_ci * Notification that the other end has received the message and 33862306a36Sopenharmony_ci * DMA'd the skb information. At this point, they are done with 33962306a36Sopenharmony_ci * our side. When all recipients are done processing, we 34062306a36Sopenharmony_ci * release the skb and then release our pending message structure. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_cistatic void 34362306a36Sopenharmony_cixpnet_send_completed(enum xp_retval reason, short partid, int channel, 34462306a36Sopenharmony_ci void *__qm) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct xpnet_pending_msg *queued_msg = (struct xpnet_pending_msg *)__qm; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci DBUG_ON(queued_msg == NULL); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci dev_dbg(xpnet, "message to %d notified with reason %d\n", 35162306a36Sopenharmony_ci partid, reason); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (atomic_dec_return(&queued_msg->use_count) == 0) { 35462306a36Sopenharmony_ci dev_dbg(xpnet, "all acks for skb->head=-x%p\n", 35562306a36Sopenharmony_ci (void *)queued_msg->skb->head); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci dev_kfree_skb_any(queued_msg->skb); 35862306a36Sopenharmony_ci kfree(queued_msg); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic void 36362306a36Sopenharmony_cixpnet_send(struct sk_buff *skb, struct xpnet_pending_msg *queued_msg, 36462306a36Sopenharmony_ci u64 start_addr, u64 end_addr, u16 embedded_bytes, int dest_partid) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci u8 msg_buffer[XPNET_MSG_SIZE]; 36762306a36Sopenharmony_ci struct xpnet_message *msg = (struct xpnet_message *)&msg_buffer; 36862306a36Sopenharmony_ci u16 msg_size = sizeof(struct xpnet_message); 36962306a36Sopenharmony_ci enum xp_retval ret; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci msg->embedded_bytes = embedded_bytes; 37262306a36Sopenharmony_ci if (unlikely(embedded_bytes != 0)) { 37362306a36Sopenharmony_ci msg->version = XPNET_VERSION_EMBED; 37462306a36Sopenharmony_ci dev_dbg(xpnet, "calling memcpy(0x%p, 0x%p, 0x%lx)\n", 37562306a36Sopenharmony_ci &msg->data, skb->data, (size_t)embedded_bytes); 37662306a36Sopenharmony_ci skb_copy_from_linear_data(skb, &msg->data, 37762306a36Sopenharmony_ci (size_t)embedded_bytes); 37862306a36Sopenharmony_ci msg_size += embedded_bytes - 1; 37962306a36Sopenharmony_ci } else { 38062306a36Sopenharmony_ci msg->version = XPNET_VERSION; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci msg->magic = XPNET_MAGIC; 38362306a36Sopenharmony_ci msg->size = end_addr - start_addr; 38462306a36Sopenharmony_ci msg->leadin_ignore = (u64)skb->data - start_addr; 38562306a36Sopenharmony_ci msg->tailout_ignore = end_addr - (u64)skb_tail_pointer(skb); 38662306a36Sopenharmony_ci msg->buf_pa = xp_pa((void *)start_addr); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci dev_dbg(xpnet, "sending XPC message to %d:%d\n" 38962306a36Sopenharmony_ci "msg->buf_pa=0x%lx, msg->size=%u, " 39062306a36Sopenharmony_ci "msg->leadin_ignore=%u, msg->tailout_ignore=%u\n", 39162306a36Sopenharmony_ci dest_partid, XPC_NET_CHANNEL, msg->buf_pa, msg->size, 39262306a36Sopenharmony_ci msg->leadin_ignore, msg->tailout_ignore); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci atomic_inc(&queued_msg->use_count); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ret = xpc_send_notify(dest_partid, XPC_NET_CHANNEL, XPC_NOWAIT, msg, 39762306a36Sopenharmony_ci msg_size, xpnet_send_completed, queued_msg); 39862306a36Sopenharmony_ci if (unlikely(ret != xpSuccess)) 39962306a36Sopenharmony_ci atomic_dec(&queued_msg->use_count); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci/* 40362306a36Sopenharmony_ci * Network layer has formatted a packet (skb) and is ready to place it 40462306a36Sopenharmony_ci * "on the wire". Prepare and send an xpnet_message to all partitions 40562306a36Sopenharmony_ci * which have connected with us and are targets of this packet. 40662306a36Sopenharmony_ci * 40762306a36Sopenharmony_ci * MAC-NOTE: For the XPNET driver, the MAC address contains the 40862306a36Sopenharmony_ci * destination partid. If the destination partid octets are 0xffff, 40962306a36Sopenharmony_ci * this packet is to be broadcast to all connected partitions. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_cistatic netdev_tx_t 41262306a36Sopenharmony_cixpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct xpnet_pending_msg *queued_msg; 41562306a36Sopenharmony_ci u64 start_addr, end_addr; 41662306a36Sopenharmony_ci short dest_partid; 41762306a36Sopenharmony_ci u16 embedded_bytes = 0; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci dev_dbg(xpnet, ">skb->head=0x%p skb->data=0x%p skb->tail=0x%p " 42062306a36Sopenharmony_ci "skb->end=0x%p skb->len=%d\n", (void *)skb->head, 42162306a36Sopenharmony_ci (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), 42262306a36Sopenharmony_ci skb->len); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (skb->data[0] == 0x33) { 42562306a36Sopenharmony_ci dev_kfree_skb(skb); 42662306a36Sopenharmony_ci return NETDEV_TX_OK; /* nothing needed to be done */ 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* 43062306a36Sopenharmony_ci * The xpnet_pending_msg tracks how many outstanding 43162306a36Sopenharmony_ci * xpc_send_notifies are relying on this skb. When none 43262306a36Sopenharmony_ci * remain, release the skb. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ci queued_msg = kmalloc(sizeof(struct xpnet_pending_msg), GFP_ATOMIC); 43562306a36Sopenharmony_ci if (queued_msg == NULL) { 43662306a36Sopenharmony_ci dev_warn(xpnet, "failed to kmalloc %ld bytes; dropping " 43762306a36Sopenharmony_ci "packet\n", sizeof(struct xpnet_pending_msg)); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci dev->stats.tx_errors++; 44062306a36Sopenharmony_ci dev_kfree_skb(skb); 44162306a36Sopenharmony_ci return NETDEV_TX_OK; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* get the beginning of the first cacheline and end of last */ 44562306a36Sopenharmony_ci start_addr = ((u64)skb->data & ~(L1_CACHE_BYTES - 1)); 44662306a36Sopenharmony_ci end_addr = L1_CACHE_ALIGN((u64)skb_tail_pointer(skb)); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* calculate how many bytes to embed in the XPC message */ 44962306a36Sopenharmony_ci if (unlikely(skb->len <= XPNET_MSG_DATA_MAX)) { 45062306a36Sopenharmony_ci /* skb->data does fit so embed */ 45162306a36Sopenharmony_ci embedded_bytes = skb->len; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* 45562306a36Sopenharmony_ci * Since the send occurs asynchronously, we set the count to one 45662306a36Sopenharmony_ci * and begin sending. Any sends that happen to complete before 45762306a36Sopenharmony_ci * we are done sending will not free the skb. We will be left 45862306a36Sopenharmony_ci * with that task during exit. This also handles the case of 45962306a36Sopenharmony_ci * a packet destined for a partition which is no longer up. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci atomic_set(&queued_msg->use_count, 1); 46262306a36Sopenharmony_ci queued_msg->skb = skb; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (skb->data[0] == 0xff) { 46562306a36Sopenharmony_ci /* we are being asked to broadcast to all partitions */ 46662306a36Sopenharmony_ci for_each_set_bit(dest_partid, xpnet_broadcast_partitions, 46762306a36Sopenharmony_ci xp_max_npartitions) { 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci xpnet_send(skb, queued_msg, start_addr, end_addr, 47062306a36Sopenharmony_ci embedded_bytes, dest_partid); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci } else { 47362306a36Sopenharmony_ci dest_partid = (short)skb->data[XPNET_PARTID_OCTET + 1]; 47462306a36Sopenharmony_ci dest_partid |= (short)skb->data[XPNET_PARTID_OCTET + 0] << 8; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (dest_partid >= 0 && 47762306a36Sopenharmony_ci dest_partid < xp_max_npartitions && 47862306a36Sopenharmony_ci test_bit(dest_partid, xpnet_broadcast_partitions) != 0) { 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci xpnet_send(skb, queued_msg, start_addr, end_addr, 48162306a36Sopenharmony_ci embedded_bytes, dest_partid); 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci dev->stats.tx_packets++; 48662306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (atomic_dec_return(&queued_msg->use_count) == 0) { 48962306a36Sopenharmony_ci dev_kfree_skb(skb); 49062306a36Sopenharmony_ci kfree(queued_msg); 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return NETDEV_TX_OK; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci/* 49762306a36Sopenharmony_ci * Deal with transmit timeouts coming from the network layer. 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_cistatic void 50062306a36Sopenharmony_cixpnet_dev_tx_timeout(struct net_device *dev, unsigned int txqueue) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci dev->stats.tx_errors++; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic const struct net_device_ops xpnet_netdev_ops = { 50662306a36Sopenharmony_ci .ndo_open = xpnet_dev_open, 50762306a36Sopenharmony_ci .ndo_stop = xpnet_dev_stop, 50862306a36Sopenharmony_ci .ndo_start_xmit = xpnet_dev_hard_start_xmit, 50962306a36Sopenharmony_ci .ndo_tx_timeout = xpnet_dev_tx_timeout, 51062306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 51162306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 51262306a36Sopenharmony_ci}; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int __init 51562306a36Sopenharmony_cixpnet_init(void) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 51862306a36Sopenharmony_ci int result; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (!is_uv_system()) 52162306a36Sopenharmony_ci return -ENODEV; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci dev_info(xpnet, "registering network device %s\n", XPNET_DEVICE_NAME); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci xpnet_broadcast_partitions = bitmap_zalloc(xp_max_npartitions, 52662306a36Sopenharmony_ci GFP_KERNEL); 52762306a36Sopenharmony_ci if (xpnet_broadcast_partitions == NULL) 52862306a36Sopenharmony_ci return -ENOMEM; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* 53162306a36Sopenharmony_ci * use ether_setup() to init the majority of our device 53262306a36Sopenharmony_ci * structure and then override the necessary pieces. 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci xpnet_device = alloc_netdev(0, XPNET_DEVICE_NAME, NET_NAME_UNKNOWN, 53562306a36Sopenharmony_ci ether_setup); 53662306a36Sopenharmony_ci if (xpnet_device == NULL) { 53762306a36Sopenharmony_ci bitmap_free(xpnet_broadcast_partitions); 53862306a36Sopenharmony_ci return -ENOMEM; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci netif_carrier_off(xpnet_device); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci xpnet_device->netdev_ops = &xpnet_netdev_ops; 54462306a36Sopenharmony_ci xpnet_device->mtu = XPNET_DEF_MTU; 54562306a36Sopenharmony_ci xpnet_device->min_mtu = XPNET_MIN_MTU; 54662306a36Sopenharmony_ci xpnet_device->max_mtu = XPNET_MAX_MTU; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci memset(addr, 0, sizeof(addr)); 54962306a36Sopenharmony_ci /* 55062306a36Sopenharmony_ci * Multicast assumes the LSB of the first octet is set for multicast 55162306a36Sopenharmony_ci * MAC addresses. We chose the first octet of the MAC to be unlikely 55262306a36Sopenharmony_ci * to collide with any vendor's officially issued MAC. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci addr[0] = 0x02; /* locally administered, no OUI */ 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci addr[XPNET_PARTID_OCTET + 1] = xp_partition_id; 55762306a36Sopenharmony_ci addr[XPNET_PARTID_OCTET + 0] = (xp_partition_id >> 8); 55862306a36Sopenharmony_ci eth_hw_addr_set(xpnet_device, addr); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* 56162306a36Sopenharmony_ci * ether_setup() sets this to a multicast device. We are 56262306a36Sopenharmony_ci * really not supporting multicast at this time. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci xpnet_device->flags &= ~IFF_MULTICAST; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* 56762306a36Sopenharmony_ci * No need to checksum as it is a DMA transfer. The BTE will 56862306a36Sopenharmony_ci * report an error if the data is not retrievable and the 56962306a36Sopenharmony_ci * packet will be dropped. 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_ci xpnet_device->features = NETIF_F_HW_CSUM; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci result = register_netdev(xpnet_device); 57462306a36Sopenharmony_ci if (result != 0) { 57562306a36Sopenharmony_ci free_netdev(xpnet_device); 57662306a36Sopenharmony_ci bitmap_free(xpnet_broadcast_partitions); 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return result; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cimodule_init(xpnet_init); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic void __exit 58562306a36Sopenharmony_cixpnet_exit(void) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci dev_info(xpnet, "unregistering network device %s\n", 58862306a36Sopenharmony_ci xpnet_device[0].name); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci unregister_netdev(xpnet_device); 59162306a36Sopenharmony_ci free_netdev(xpnet_device); 59262306a36Sopenharmony_ci bitmap_free(xpnet_broadcast_partitions); 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cimodule_exit(xpnet_exit); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ciMODULE_AUTHOR("Silicon Graphics, Inc."); 59862306a36Sopenharmony_ciMODULE_DESCRIPTION("Cross Partition Network adapter (XPNET)"); 59962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 600