From mboxrd@z Thu Jan 1 00:00:00 1970 From: zhuyj Subject: Re: [PATCH] net: unisys: adding unisys virtnic driver Date: Mon, 22 Dec 2014 16:32:08 +0800 Message-ID: <5497D708.7070109@gmail.com> References: <1418842340-29894-1-git-send-email-earfvids@redhat.com> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: QUOTED-PRINTABLE To: Erik Arfvidson , benjamin.romer@unisys.com, netdev@vger.kernel.org, dzickus@redhat.com, davem@davemloft.net, Bruce.Vessey@unisys.com, sparmaintainer@unisys.com, prarit@redhat.com Return-path: Received: from mail-pd0-f180.google.com ([209.85.192.180]:55879 "EHLO mail-pd0-f180.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751583AbaLVIcP (ORCPT ); Mon, 22 Dec 2014 03:32:15 -0500 Received: by mail-pd0-f180.google.com with SMTP id w10so5380599pde.25 for ; Mon, 22 Dec 2014 00:32:14 -0800 (PST) In-Reply-To: <1418842340-29894-1-git-send-email-earfvids@redhat.com> Sender: netdev-owner@vger.kernel.org List-ID: Compared with veth, tun/tap, is there any difference about this virtnic= ? Zhu Yanjun On 12/18/2014 02:52 AM, Erik Arfvidson wrote: > The purpose of this patch is to add Unisys virtual network driver > into the network directory and also to start a discussion about > the requirements needed. > > Signed-off-by: Erik Arfvidson > --- > drivers/net/virtnic.c | 2475 ++++++++++++++++++++++++++++++++++++++= +++++++++++ > 1 file changed, 2475 insertions(+) > create mode 100644 drivers/net/virtnic.c > > diff --git a/drivers/net/virtnic.c b/drivers/net/virtnic.c > new file mode 100644 > index 0000000..0af48f3 > --- /dev/null > +++ b/drivers/net/virtnic.c > @@ -0,0 +1,2475 @@ > +/* virtnic.c > + * > + * Copyright =C2=A9 2010 - 2014 UNISYS CORPORATION > + * All rights reserved. > + * > + * This program is free software; you can redistribute it and/or mod= ify > + * it under the terms of the GNU General Public License as published= by > + * the Free Software Foundation; either version 2 of the License, or= (at > + * your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, b= ut > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE o= r > + * NON INFRINGEMENT. See the GNU General Public License for more > + * details. > + */ > + > +#define EXPORT_SYMTAB > + > +#include > +#ifdef CONFIG_MODVERSIONS > +#include > +#endif > + > +#include "uniklog.h" > +#include "diagnostics/appos_subsystems.h" > +#include "uisutils.h" > +#include "uisthread.h" > +#include "uisqueue.h" > +#include "visorchipset.h" > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "virtpci.h" > +#include "version.h" > + > +/* this is shorter than using __FILE__ (full path name) in */ > +/* debug/info/error messages */ > +#define __MYFILE__ "virtnic.c" > + > +/* turn off collecting of debug statistics */ > +#define VIRTNIC_STATS 0 > + > + /* MAX_BUF =3D 64 lines x 32 MAXVHBA x 80 characters > + * =3D 163840 bytes ~ 40 pages > + */ > +#define MAX_BUF 163840 > + > +/* > + * uisnic virtnic > + * <---- xmit --- virtnic_xmit(hard-start-xmit) > + * <-- rcvpost -- open, virtnic_rx > + * <-- unpost --- close > + * <-- enb/dis -- open, close > + * > + * open & close can't run at the same time as each other or rcv/xmit= , but > + * virtnic_xmit and virtnic_rx could be running at the same time. > + * and all messages being sent to uisnic MUST be sent so if the queu= e is > + * full we have to retry, but we don't want to retry with a spinlock= held. > + */ > + > +/*****************************************************/ > +/* Forward declarations */ > +/*****************************************************/ > +static int virtnic_probe(struct virtpci_dev *dev, > + const struct pci_device_id *id); > +static void virtnic_remove(struct virtpci_dev *dev); > +static int virtnic_change_mtu(struct net_device *netdev, int new_mtu= ); > +static int virtnic_close(struct net_device *netdev); > +static struct net_device_stats *virtnic_get_stats(struct net_device = *netdev); > +static int virtnic_open(struct net_device *netdev); > +static int virtnic_ioctl(struct net_device *netdev, struct ifreq *if= r, > + int cmd); > +static void virtnic_rx(struct uiscmdrsp *cmdrsp); > +static int virtnic_xmit(struct sk_buff *skb, struct net_device *netd= ev); > +static void virtnic_xmit_timeout(struct net_device *netdev); > +static void virtnic_set_multi(struct net_device *netdev); > +static int virtnic_serverdown(struct virtpci_dev *virtpcidev, u32 st= ate); > +static int virtnic_serverup(struct virtpci_dev *virtpcidev); > +static void virtnic_serverdown_complete(struct work_struct *work); > +static void virtnic_timeout_reset(struct work_struct *work); > +static int process_incoming_rsps(void *); > +static ssize_t info_debugfs_read(struct file *file, char __user *buf= , > + size_t len, loff_t *offset); > +static ssize_t enable_ints_write(struct file *file, > + const char __user *buffer, > + size_t count, loff_t *ppos); > + > +/*****************************************************/ > +/* Globals */ > +/*****************************************************/ > + > +#define VIRTNIC_XMIT_TIMEOUT (5 * HZ) /* Default timeout period in j= iffies */ > +#define VIRTNIC_INFINITE_RESPONSE_WAIT 0 > +#define INTERRUPT_VECTOR_MASK 0x3F > + > +static struct workqueue_struct *virtnic_serverdown_workqueue; > +static struct workqueue_struct *virtnic_timeout_reset_workqueue; > + > +static const struct pci_device_id virtnic_id_table[] =3D { > + { > + PCI_DEVICE(PCI_VENDOR_ID_UNISYS, PCI_DEVICE_ID_VIRTNIC)}, { > +0},}; > +/* export virtnic_id_table */ > +MODULE_DEVICE_TABLE(pci, virtnic_id_table); > + > +static struct virtpci_driver virtnic_driver =3D { > + .name =3D "uisvirtnic", > + .version =3D VERSION, > + .vertag =3D NULL, > + .id_table =3D virtnic_id_table, > + .probe =3D virtnic_probe, > + .remove =3D virtnic_remove, > + .suspend =3D virtnic_serverdown, > + .resume =3D virtnic_serverup > +}; > + > +#define SEND_ENBDIS(ndev, state, cmdrsp, queue, insertlock, stats) {= \ > + DBGINF("sending rcv enb/dis netdev:%p state:%d\n", ndev, state); \ > + cmdrsp->net.enbdis.enable =3D state; \ > + cmdrsp->net.enbdis.context =3D ndev; \ > + cmdrsp->net.type =3D NET_RCV_ENBDIS; \ > + cmdrsp->cmdtype =3D CMD_NET_TYPE; \ > + uisqueue_put_cmdrsp_with_lock_client(queue, cmdrsp, IOCHAN_TO_IOPAR= T, \ > + (void *)insertlock, \ > + DONT_ISSUE_INTERRUPT, \ > + (uint64_t)NULL, \ > + OK_TO_WAIT, "vnic"); \ > + stats.sent_enbdis++;\ > +} > + > +struct chanstat { > + unsigned long got_rcv; /* count of NET_RCV received */ > + unsigned long got_enbdisack; /* count of NET_RCV_ENBDIS_ACK rcvd */ > + unsigned long got_xmit_done; /* count of NET_XMIT_DONE received */ > + unsigned long xmit_fail; /* count of NET_XMIT_DONE failures */ > + unsigned long sent_enbdis; /* count of NET_RCV_ENBDIS sent */ > + unsigned long sent_promisc; /* count of NET_RCV_PROMISC sent */ > + unsigned long sent_post; /* count of NET_RCV_POST sent */ > + unsigned long sent_xmit; /* count of NET_XMIT sent */ > + unsigned long reject_count; /* count of NET_XMIT rejected because *= / > + /* of BUSY/queue full */ > + unsigned long extra_rcvbufs_sent; > +#if VIRTNIC_STATS > + unsigned long reject_jiffies_start; /* jiffie count at start of > + NET_XMIT rejects */ > +#endif /* VIRTNIC_STATS */ > +}; > + > +struct datachan { > + struct chaninfo chinfo; > + struct chanstat chstat; > +}; > + > +struct virtnic_info { > + struct virtpci_dev *virtpcidev; > + struct net_device *netdev; > + struct net_device_stats net_stats; > + spinlock_t priv_lock; /* spinlock check for private lock */ > + struct datachan datachan; > + struct sk_buff **rcvbuf; /* rcvbuf is the array of rcv buffer */ > + /* we post to */ > + unsigned long long uniquenum; > + > + /* the IOPART end */ > + int num_rcv_bufs; /* indicates how many receive buffers the > + vnic will post */ > + int num_rcv_bufs_could_not_alloc; > + atomic_t num_rcv_bufs_in_iovm; /* indicates how many receive buffer= s > + have actully been sent to the iovm */ > + unsigned long inner_loop_limit_reached_cnt; > + unsigned long alloc_failed_in_if_needed_cnt; > + unsigned long alloc_failed_in_repost_return_cnt; > + > + struct sk_buff_head xmitbufhead; /* xmitbufhead is the head of > + the xmit buffer list that > + have been sent to the IOPART > + end */ > + int max_outstanding_net_xmits; /* absolute max number of outstandin= g > + xmits - should never hit this */ > + int upper_threshold_net_xmits; /* high water mark for calling > + netif_stop_queue() */ > + int lower_threshold_net_xmits; /* high water mark for calling > + netif_wake_queue() */ > + uuid_le zoneguid; /* specifies the zone for the switch in > + which this VNIC resides */ > + struct uiscmdrsp *cmdrsp_rcv; /* cmdrsp_rcv is used for > + posting/unposting rcv buffers */ > + unsigned short enabled; /* 0 disabled 1 enabled to receive */ > + unsigned short enab_dis_acked; /* NET_RCV_ENABLE/DISABLE acked by > + uisnic */ > + atomic_t usage; /* count of users */ > + unsigned short old_flags; /* flags as they were prior to > + set_multicast_list */ > + struct uiscmdrsp *xmit_cmdrsp; /* used to issue NET_XMIT - there i= s > + never more that one xmit in progress > + at a time */ > + struct dentry *eth_debugfs_dir; /* this points to /proc/eth? > + directory */ > + struct dentry *zone_debugfs_entry; /* this points to > + /proc/virtnic/eth?/zone */ > + /* file */ > + struct dentry *clientstr_debugfs_entry;/* this points to > + /proc/virtnic/eth?/clientstr > + file */ > + struct irq_info intr; /* use recvInterrupt info to connect > + to this to receive interrupts when > + IOs complete */ > + int interrupt_vector; > + int thread_wait_ms; > + int queuefullmsg_logged; /* flag for throttling queue full */ > + /* messages */ > + /* some debug counters */ > + ulong n_rcv0; /* # rcvs of 0 buffers */ > + ulong n_rcv1; /* # rcvs of 1 buffer */ > + ulong n_rcv2; /* # rcvs of 2 buffers */ > + ulong n_rcvx; /* # rcvs of >2 buffers */ > + ulong found_repost_rcvbuf_cnt; /* #time we called repost_rcvbuf_cnt= */ > + ulong repost_found_skb_cnt; /* # times found the skb */ > + ulong n_repost_deficit; /* # times we couldn't find all of the > + rcv buffers */ > + ulong bad_rcv_buf; /* # times we neglected to > + free the rcv skb because > + we didn't know where it > + came from */ > + ulong n_rcv_packet_not_accepted; /* # bogus recv packets */ > + bool server_down; > + bool server_change_state; > + unsigned long long interrupts_rcvd; > + unsigned long long interrupts_notme; > + unsigned long long interrupts_disabled; > + unsigned long long busy_cnt; > + unsigned long long flow_control_upper_hits; > + unsigned long long flow_control_lower_hits; > + struct work_struct serverdown_completion; > + struct work_struct timeout_reset; > + uint64_t __iomem *flags_addr; > + atomic_t interrupt_rcvd; > + wait_queue_head_t rsp_queue; > +}; > + > +struct virtnic_devices_open { > + struct net_device *netdev; > + struct virtnic_info *vnicinfo; > +}; > + > +static ssize_t show_zone(struct device *dev, struct device_attribute= *attr, > + char *buf) > +{ > + struct net_device *net =3D to_net_dev(dev); > + struct virtnic_info *vnicinfo =3D netdev_priv(net); > + > + return scnprintf(buf, PAGE_SIZE, "%pUL\n", &vnicinfo->zoneguid); > +} > + > +static ssize_t show_clientstr(struct device *dev, struct device_attr= ibute *attr, > + char *buf) > +{ > + struct net_device *net =3D to_net_dev(dev); > + struct virtnic_info *vnicinfo =3D netdev_priv(net); > + struct spar_io_channel_protocol *chan =3D > + (struct spar_io_channel_protocol *)vnicinfo-> > + datachan.chinfo.queueinfo->chan; > + > + return scnprintf(buf, PAGE_SIZE, "%s\n", > + (char *)&chan->client_string); > +} > +static DEVICE_ATTR(clientstr, S_IRUGO, show_clientstr, NULL); > +static DEVICE_ATTR(zone, S_IRUGO, show_zone, NULL); > + > +#define VIRTNICSOPENMAX 32 > +/* array of open devices maintained by open() and close() */ > +static struct virtnic_devices_open num_virtnic_open[VIRTNICSOPENMAX]= ; > +static struct dentry *virtnic_debugfs_dir; > + > +static const struct file_operations debugfs_info_fops =3D { > + .read =3D info_debugfs_read, > +}; > + > +static const struct file_operations debugfs_enable_ints_fops =3D { > + .write =3D enable_ints_write, > +}; > + > +/*****************************************************/ > +/* Probe Remove Functions */ > +/*****************************************************/ > +/* set up net.rcvpost struct in cmdrsp. > + * all rcv buf skb are allocated at RCVPOST_BUF_SIZE, so length is > + * RCVPOST_BUF_SIZE by default. and since RCVPOST_BUF_SIZE < 2048, o= ne > + * phys_info struct can describe the rcv buf. > + */ > +static inline void > +post_skb(struct uiscmdrsp *cmdrsp, > + struct virtnic_info *vnicinfo, struct sk_buff *skb) > +{ > + cmdrsp->net.buf =3D skb; > + cmdrsp->net.rcvpost.frag.pi_pfn =3D page_to_pfn(virt_to_page(skb->d= ata)); > + cmdrsp->net.rcvpost.frag.pi_off =3D > + (unsigned long)skb->data & PI_PAGE_MASK; > + cmdrsp->net.rcvpost.frag.pi_len =3D skb->len; > + cmdrsp->net.rcvpost.unique_num =3D vnicinfo->uniquenum; > + > + DBGINF("RCV_POST skb:%p pfn:%llu off:%x len:%d\n", skb, > + cmdrsp->net.rcvpost.frag.pi_pfn, > + cmdrsp->net.rcvpost.frag.pi_off, > + cmdrsp->net.rcvpost.frag.pi_len); > + if ((cmdrsp->net.rcvpost.frag.pi_off + skb->len) > PI_PAGE_SIZE) { > + LOGERRNAME(vnicinfo->netdev, > + "**** pi_off:0x%x pi_len:%d SPAN ACROSS A PAGE\n", > + cmdrsp->net.rcvpost.frag.pi_off, skb->len); > + } else { > + cmdrsp->net.type =3D NET_RCV_POST; > + cmdrsp->cmdtype =3D CMD_NET_TYPE; > + uisqueue_put_cmdrsp_with_lock_client(vnicinfo->datachan.chinfo. > + queueinfo, cmdrsp, > + IOCHAN_TO_IOPART, > + (void *)&vnicinfo-> > + datachan.chinfo.insertlock, > + DONT_ISSUE_INTERRUPT, > + (uint64_t)NULL, > + OK_TO_WAIT, > + "vnic"); > + atomic_inc(&vnicinfo->num_rcv_bufs_in_iovm); > + vnicinfo->datachan.chstat.sent_post++; > + } > +} > + > +static irqreturn_t > +virtnic_ISR(int irq, void *dev_id) > +{ > + struct virtnic_info *vnicinfo =3D (struct virtnic_info *)dev_id; > + > + struct channel_header __iomem *p_channel_header; > + > + struct signal_queue_header __iomem *pqhdr; > + uint64_t mask; > + unsigned long long rc1; > + > + if (vnicinfo =3D=3D NULL) > + return IRQ_NONE; > + vnicinfo->interrupts_rcvd++; > + p_channel_header =3D vnicinfo->datachan.chinfo.queueinfo->chan; > + if (((readq(&p_channel_header->features) & > + ULTRA_IO_IOVM_IS_OK_WITH_DRIVER_DISABLING_INTS) !=3D 0) && > + ((readq(&p_channel_header->features) & > + ULTRA_IO_DRIVER_DISABLES_INTS) !=3D 0)) { > + /* > + * should not enter this path because we setup without > + * DRIVER_DISABLES_INTS. > + */ > + vnicinfo->interrupts_disabled++; > + mask =3D ~ULTRA_CHANNEL_ENABLE_INTS; > + rc1 =3D uisqueue_interlocked_and(vnicinfo->flags_addr, mask); > + } > + if (spar_signalqueue_empty(p_channel_header, IOCHAN_FROM_IOPART)) { > + vnicinfo->interrupts_notme++; > + return IRQ_NONE; > + } > + pqhdr =3D (struct signal_queue_header __iomem *) > + ((char __iomem *)p_channel_header + > + readq(&p_channel_header->ch_space_offset)) + > + IOCHAN_FROM_IOPART; > + writeq(readq(&pqhdr->num_irq_received) + 1, > + &pqhdr->num_irq_received); > + atomic_set(&vnicinfo->interrupt_rcvd, 1); > + wake_up_interruptible(&vnicinfo->rsp_queue); > + return IRQ_HANDLED; > +} > + > +static const struct net_device_ops virtnic_dev_ops =3D { > + .ndo_open =3D virtnic_open, > + .ndo_stop =3D virtnic_close, > + .ndo_start_xmit =3D virtnic_xmit, > + .ndo_get_stats =3D virtnic_get_stats, > + .ndo_do_ioctl =3D virtnic_ioctl, > + .ndo_change_mtu =3D virtnic_change_mtu, > + .ndo_tx_timeout =3D virtnic_xmit_timeout, > + .ndo_set_rx_mode =3D virtnic_set_multi, > +}; > + > +static int > +virtnic_probe(struct virtpci_dev *virtpcidev, const struct pci_devic= e_id *id) > +{ > + struct net_device *netdev =3D NULL; > + struct virtnic_info *vnicinfo; > + int err; > + int rsp; > + irq_handler_t handler =3D virtnic_ISR; > + struct channel_header __iomem *p_channel_header; > + struct signal_queue_header __iomem *pqhdr; > + uint64_t mask; > + > +#define RETFAIL(res) {\ > + kfree(vnicinfo->cmdrsp_rcv); \ > + kfree(vnicinfo->xmit_cmdrsp); \ > + kfree(vnicinfo->rcvbuf); \ > + if (vnicinfo->interrupt_vector !=3D -1) \ > + free_irq(vnicinfo->interrupt_vector, vnicinfo); \ > + if (netdev) \ > + free_netdev(netdev); \ > + return res; \ > +} > + > + DBGINF("virtpci_dev:%p\n", virtpcidev); > + DBGINF("virtpcidev busNo<<%d>>devNo<<%d>>", > + virtpcidev->busNo, virtpcidev->deviceNo); > + netdev =3D alloc_etherdev(sizeof(struct virtnic_info)); > + if (netdev =3D=3D NULL) { > + LOGERR("**** FAILED to alloc etherdev\n"); > + return -ENOMEM; > + } > + netdev->netdev_ops =3D &virtnic_dev_ops; > + netdev->watchdog_timeo =3D VIRTNIC_XMIT_TIMEOUT; > + > + memcpy(netdev->dev_addr, virtpcidev->net.mac_addr, MAX_MACADDR_LEN)= ; > + netdev->addr_len =3D MAX_MACADDR_LEN; > + /* netdev->name should be ethx already */ > + netdev->dev.parent =3D &virtpcidev->generic_dev; > + > + /* setup our private struct */ > + vnicinfo =3D netdev_priv(netdev); > + memset(vnicinfo, 0, sizeof(struct virtnic_info)); > + vnicinfo->interrupt_vector =3D -1; > + vnicinfo->netdev =3D netdev; > + vnicinfo->virtpcidev =3D virtpcidev; > + init_waitqueue_head(&vnicinfo->rsp_queue); > + spin_lock_init(&vnicinfo->priv_lock); > + vnicinfo->datachan.chinfo.queueinfo =3D &virtpcidev->queueinfo; > + spin_lock_init(&vnicinfo->datachan.chinfo.insertlock); > + vnicinfo->enabled =3D 0; /* not yet */ > + atomic_set(&vnicinfo->usage, 1); /* starting val */ > + vnicinfo->zoneguid =3D virtpcidev->net.zone_uuid; > + vnicinfo->num_rcv_bufs =3D virtpcidev->net.num_rcv_bufs; > + LOGINFNAME(vnicinfo->netdev, "num_rcv_bufs =3D %d\n", > + vnicinfo->num_rcv_bufs); > + vnicinfo->rcvbuf =3D kmalloc(sizeof(struct sk_buff *) * > + vnicinfo->num_rcv_bufs, GFP_ATOMIC); > + if (vnicinfo->rcvbuf =3D=3D NULL) { > + LOGERRNAME(vnicinfo->netdev, > + "**** FAILED to allocate memory for %d receive buffers.\n", > + vnicinfo->num_rcv_bufs); > + RETFAIL(-ENOMEM); > + } > + memset(vnicinfo->rcvbuf, 0, > + sizeof(struct sk_buff *) * vnicinfo->num_rcv_bufs); > + /* set the net_xmit outstanding threshold */ > + vnicinfo->max_outstanding_net_xmits =3D > + max(3, ((vnicinfo->num_rcv_bufs / 3) - 2)); > + /* always leave two slots open but you should have 3 at a minimum *= / > + LOGINFNAME(vnicinfo->netdev, "max_outstanding_net_xmits =3D %d\n", > + vnicinfo->max_outstanding_net_xmits); > + vnicinfo->upper_threshold_net_xmits =3D > + max(2, vnicinfo->max_outstanding_net_xmits - 1); > + LOGINFNAME(vnicinfo->netdev, "upper_threshold_net_xmits =3D %d\n", > + vnicinfo->upper_threshold_net_xmits); > + vnicinfo->lower_threshold_net_xmits =3D > + max(1, vnicinfo->max_outstanding_net_xmits / 2); > + LOGINFNAME(vnicinfo->netdev, "lower_threshold_net_xmits =3D %d\n", > + vnicinfo->lower_threshold_net_xmits); > + skb_queue_head_init(&vnicinfo->xmitbufhead); > + > + /* create a cmdrsp we can use to post and unpost rcv buffers */ > + vnicinfo->cmdrsp_rcv =3D kmalloc(SIZEOF_CMDRSP, GFP_ATOMIC); > + if (vnicinfo->cmdrsp_rcv =3D=3D NULL) { > + LOGERRNAME(vnicinfo->netdev, > + "**** FAILED to allocate cmdrsp to use for posting rcv buffers= \n"); > + RETFAIL(-ENOMEM); > + } > + vnicinfo->xmit_cmdrsp =3D kmalloc(SIZEOF_CMDRSP, GFP_ATOMIC); > + if (vnicinfo->xmit_cmdrsp =3D=3D NULL) { > + LOGERRNAME(vnicinfo->netdev, > + "**** FAILED to allocate cmdrsp to use for xmits\n"); > + RETFAIL(-ENOMEM); > + } > + INIT_WORK(&vnicinfo->serverdown_completion, > + virtnic_serverdown_complete); > + INIT_WORK(&vnicinfo->timeout_reset, virtnic_timeout_reset); > + vnicinfo->server_down =3D false; > + vnicinfo->server_change_state =3D false; > + > + /* set the default mtu */ > + netdev->mtu =3D virtpcidev->net.mtu; > + > + vnicinfo->intr =3D virtpcidev->intr; > + /* buffers will be allocated in open using mtu */ > + > + /* save off netdev in virtpcidev */ > + virtpcidev->net.netdev =3D netdev; > + > + /* start thread that will receive responses */ > + writeq(readq(&vnicinfo->datachan.chinfo.queueinfo->chan->features) = | > + ULTRA_IO_CHANNEL_IS_POLLING, > + &vnicinfo->datachan.chinfo.queueinfo->chan->features); > + DBGINF("starting rsp thread queueinfo:%p threadinfo:%p\n", > + vnicinfo->datachan.chinfo.queueinfo, > + &vnicinfo->datachan.chinfo.threadinfo); > + p_channel_header =3D vnicinfo->datachan.chinfo.queueinfo->chan; > + pqhdr =3D (struct signal_queue_header __iomem *) > + ((char __iomem *)p_channel_header + > + readq(&p_channel_header->ch_space_offset)) + > + IOCHAN_FROM_IOPART; > + vnicinfo->flags_addr =3D (__force uint64_t __iomem *)&pqhdr->featur= es; > + vnicinfo->thread_wait_ms =3D 2; > + if (!uisthread_start(&vnicinfo->datachan.chinfo.threadinfo, > + process_incoming_rsps, &vnicinfo->datachan, > + "vnic_incoming")) { > + LOGERRNAME(vnicinfo->netdev, "**** FAILED to start thread\n"); > + RETFAIL(-ENODEV); > + } > + > + /* register_netdev */ > + LOGINFNAME(vnicinfo->netdev, "sendInterruptHandle=3D0x%16llX", > + (unsigned long long)vnicinfo->intr.send_irq_handle); > + LOGINFNAME(vnicinfo->netdev, "recvInterruptHandle=3D0x%16llX", > + (unsigned long long)vnicinfo->intr.recv_irq_handle); > + LOGINFNAME(vnicinfo->netdev, "recvInterruptVector=3D0x%8X", > + vnicinfo->intr.recv_irq_vector); > + LOGINFNAME(vnicinfo->netdev, "recvInterruptShared=3D0x%2X", > + vnicinfo->intr.recv_irq_shared); > + LOGINFNAME(vnicinfo->netdev, "netdev->name=3D%s", netdev->name); > + vnicinfo->interrupt_vector =3D vnicinfo->intr.recv_irq_handle & > + INTERRUPT_VECTOR_MASK; > + netdev->irq =3D vnicinfo->interrupt_vector; > + err =3D register_netdev(netdev); > + if (err) { > + uisthread_stop(&vnicinfo->datachan.chinfo.threadinfo); > + RETFAIL(err); > + } > + > + /* create proc/ethx directory */ > + vnicinfo->eth_debugfs_dir =3D debugfs_create_dir(netdev->name, > + virtnic_debugfs_dir); > + if (!vnicinfo->eth_debugfs_dir) { > + LOGERRNAME(vnicinfo->netdev, > + "****FAILED to create proc dir entry:%s\n", > + netdev->name); > + uisthread_stop(&vnicinfo->datachan.chinfo.threadinfo); > + RETFAIL(-ENODEV); > + } > + > + if (device_create_file(&netdev->dev, &dev_attr_zone) < 0) { > + uisthread_stop(&vnicinfo->datachan.chinfo.threadinfo); > + RETFAIL(-ENODEV); > + } > + if (device_create_file(&netdev->dev, &dev_attr_clientstr) < 0) { > + device_remove_file(&netdev->dev, &dev_attr_zone); > + uisthread_stop(&vnicinfo->datachan.chinfo.threadinfo); > + RETFAIL(-ENODEV); > + } > + /* create proc/ethx directory */ > + rsp =3D request_irq(vnicinfo->interrupt_vector, handler, IRQF_SHARE= D, > + netdev->name, vnicinfo); > + if (rsp !=3D 0) { > + LOGERRNAME(vnicinfo->netdev, > + "request_irq(%d) uislib_vnic_ISR request failed with rsp=3D%d\= n", > + vnicinfo->interrupt_vector, rsp); > + vnicinfo->interrupt_vector =3D -1; > + } else { > + uint64_t __iomem *features_addr =3D > + &vnicinfo->datachan.chinfo.queueinfo->chan->features; > + LOGERRNAME(vnicinfo->netdev, > + "request_irq(%d) uislib_vnic_ISR request succeeded\n", > + vnicinfo->interrupt_vector); > + mask =3D ~(ULTRA_IO_CHANNEL_IS_POLLING | > + ULTRA_IO_DRIVER_DISABLES_INTS | > + ULTRA_IO_DRIVER_SUPPORTS_ENHANCED_RCVBUF_CHECKING); > + uisqueue_interlocked_and(features_addr, mask); > + mask =3D ULTRA_IO_DRIVER_ENABLES_INTS | > + ULTRA_IO_DRIVER_SUPPORTS_ENHANCED_RCVBUF_CHECKING; > + uisqueue_interlocked_or(features_addr, mask); > + > + vnicinfo->thread_wait_ms =3D 2000; > + } > + > + LOGINFNAME(vnicinfo->netdev, > + "Added VirtNic:%p %s insertlock:%p %02x:%02x:%02x:%02x:%02x:%02= x\n", > + netdev, netdev->name, &vnicinfo->datachan.chinfo.insertlock, > + netdev->dev_addr[0], netdev->dev_addr[1], > + netdev->dev_addr[2], netdev->dev_addr[3], > + netdev->dev_addr[4], netdev->dev_addr[5]); > + return 0; > +} > + > +static void > +virtnic_remove(struct virtpci_dev *virtpcidev) > +{ > + struct net_device *netdev =3D virtpcidev->net.netdev; > + struct virtnic_info *vnicinfo; > + > + vnicinfo =3D netdev_priv(netdev); > + > + LOGINFNAME(vnicinfo->netdev, > + "virtpcidev:%p netdev:%p name:%s vnicinfo:%p\n", > + virtpcidev, netdev, netdev->name, vnicinfo); > + LOGINFNAME(vnicinfo->netdev, > + "virtpcidev busNo<<%d>>devNo<<%d>>", > + virtpcidev->bus_no, virtpcidev->device_no); > + /* REMOVE netdev */ > + DBGINF("unregistering netdev\n"); > + if (vnicinfo->interrupt_vector !=3D -1) > + free_irq(vnicinfo->interrupt_vector, vnicinfo); > + unregister_netdev(netdev); > + /* this is going to call virtnic_close which will send out */ > + /* disable don't take thread down until after that */ > + uisthread_stop(&vnicinfo->datachan.chinfo.threadinfo); > + > + /* freeing of rcv bufs should have happened in close. */ > + /* free cmdrsp we allocated for rcv post/unpost */ > + kfree(vnicinfo->cmdrsp_rcv); > + kfree(vnicinfo->xmit_cmdrsp); > + > + /* delete proc file entries */ > + device_remove_file(&netdev->dev, &dev_attr_zone); > + device_remove_file(&netdev->dev, &dev_attr_clientstr); > + > + debugfs_remove(vnicinfo->eth_debugfs_dir); > + LOGINFNAME(vnicinfo->netdev, "removed dentry %s\n", > + netdev->name); > + > + kfree(vnicinfo->rcvbuf); > + free_netdev(netdev); > + > + LOGINF("virtnic removed\n"); > +} > + > +/*****************************************************/ > +/* NIC statistics handling */ > +/*****************************************************/ > + > +/* update rcv stats - locking done by invoker */ > +#define UPD_RCV_STATS { \ > + vnicinfo->net_stats.rx_packets++; \ > + vnicinfo->net_stats.rx_bytes +=3D skb->len; \ > +} > + > +/* update xmt stats - locking done by invoker */ > +#define UPD_XMT_STATS { \ > + vnicinfo->net_stats.tx_packets++; \ > + vnicinfo->net_stats.tx_bytes +=3D skb->len; \ > +} > + > +static struct net_device_stats * > +virtnic_get_stats(struct net_device *netdev) > +{ > + struct virtnic_info *vnicinfo =3D netdev_priv(netdev); > + > + /* take this opportunity to print out our internal stats */ > + DBGINF > + ("NET_RCV_ENBDIS sent: %ld NET_RCV_ENBDIS_ACK received: %ld= \n", > + vnicinfo->datachan.chstat.sent_enbdis, > + vnicinfo->datachan.chstat.got_enbdisack); > + > + DBGINF("NET_RCV received: %ld NET_RCV_POST sent: %ld\n", > + vnicinfo->datachan.chstat.got_rcv, > + vnicinfo->datachan.chstat.sent_post); > + > + DBGINF("extra NET_RCV_POST sent: %ld\n", > + vnicinfo->datachan.chstat.extra_rcvbufs_sent); > + > + DBGINF("NET_XMIT sent: %ld NET_XMIT_DONE received: %ld\n"= , > + vnicinfo->datachan.chstat.sent_xmit, > + vnicinfo->datachan.chstat.got_xmit_done); > + > + DBGINF("XMIT failures: %ld NET_RCV_PROMISC sent: %ld\n", > + vnicinfo->datachan.chstat.xmit_fail, > + vnicinfo->datachan.chstat.sent_promisc); > + > + DBGINF("XMIT reject/busy: %ld\n", > + vnicinfo->datachan.chstat.reject_count); > + > + return &vnicinfo->net_stats; > +} > + > +/*****************************************************/ > +/* Local functions */ > +/*****************************************************/ > + > +/* > + * This function allocates skb, skb->data for first fragment. If Mtu > + * size is > default, it allocates frags. > + */ > +static struct sk_buff * > +alloc_rcv_buf(struct net_device *netdev) > +{ > + struct sk_buff *skb; > + > +/* > + * NOTE: the first fragment in each rcv buffer is pointed to by rcvs= kb->data. > + * For now all rcv buffers will be RCVPOST_BUF_SIZE in length, so th= e firstfrag > + * is large enough to hold 1514. > + */ > + DBGINF("netdev->name <<%s>>: allocating skb len:%d\n", netdev->nam= e, > + RCVPOST_BUF_SIZE); > + skb =3D alloc_skb(RCVPOST_BUF_SIZE, GFP_ATOMIC | __GFP_NOWARN); > + if (!skb) { > + LOGVER("**** alloc_skb failed\n"); > + return NULL; > + } > + skb->dev =3D netdev; > + skb->len =3D RCVPOST_BUF_SIZE; > + /* current value of mtu doesn't come into play here; large > + * packets will just end up using multiple rcv buffers all of > + * same size > + */ > + skb->data_len =3D 0; /* dev_alloc_skb already zeroes it out. > + for clarification. */ > + return skb; > +} > + > +static int > +init_rcv_bufs(struct net_device *netdev, struct virtnic_info *vnicin= fo) > +{ > + int i, count; > + > + DBGINF("netdev->name <<%s>>", netdev->name); > + /* > + * allocate fixed number of receive buffers to post to uisnic > + * post receive buffers after we've allocated a required > + * amount > + */ > + for (i =3D 0; i < vnicinfo->num_rcv_bufs; i++) { > + vnicinfo->rcvbuf[i] =3D alloc_rcv_buf(netdev); > + if (!vnicinfo->rcvbuf[i]) > + break; /* if we failed to allocate one let us stop */ > + } > + if (i < vnicinfo->num_rcv_bufs) { > + LOGWRNNAME(vnicinfo->netdev, > + "only allocated %d of %d receive buffers", i, > + vnicinfo->num_rcv_bufs); > + if (i =3D=3D 0) { > + /* couldn't even allocate one - bail out */ > + LOGERRNAME(vnicinfo->netdev, > + "**** FAILED to allocate any rcv buffers\n"); > + return -ENOMEM; > + } > + } > + count =3D i; > + /* Ensure we can alloc 2/3rd of the requested number of > + * buffers. 2/3 is an arbitraty choice; used also in ndis > + * init.c. > + */ > + if (count < ((2 * vnicinfo->num_rcv_bufs) / 3)) { > + LOGERRNAME(vnicinfo->netdev, > + "**** FAILED to allocate enough rcv bufs; allocated only:%d MA= X_NET_RCV_BUFS:%d\n", > + count, MAX_NET_RCV_BUFS); > + /* free receive buffers we did allocate and then bail out */ > + for (i =3D 0; i < count; i++) { > + kfree_skb(vnicinfo->rcvbuf[i]); > + vnicinfo->rcvbuf[i] =3D NULL; > + } > + return -ENOMEM; > + } > + > + /* post receive buffers to receive incoming input - without holding= */ > + /* lock - we've not enabled nor started the queue so there shouldn'= t */ > + /* be any rcv or xmit activity */ > + for (i =3D 0; i < count; i++) > + post_skb(vnicinfo->cmdrsp_rcv, vnicinfo, vnicinfo->rcvbuf[i]); > + > + /* push through with what buffers we've got - unallocated ones will= */ > + /* be null */ > + LOGINFNAME(vnicinfo->netdev, "Allocated & posted %d rcv buffers\n", > + count); > + > + return 0; > +} > + > +/* Sends disable to IOVM and frees receive buffers that were posted = to > + * IOVM (cleared by IOVM when disable is received) > + * returns 0 on success, negative number on failure > + * > + * timeout is defined in msecs (timeout of 0 specifies infinite wait= ) > + */ > +static int > +virtnic_disable_with_timeout(struct net_device *netdev, const int ti= meout) > +{ > + struct virtnic_info *vnicinfo =3D netdev_priv(netdev); > + int i, count =3D 0; > + unsigned long flags; > + int wait =3D 0; > + > + LOGINFNAME(vnicinfo->netdev, "netdev->name <<%s>>", netdev->name); > + /* stop the transmit queue so nothing more can be transmitted */ > + netif_stop_queue(netdev); > + > + /* send a msg telling the other end we are stopping incoming pkts *= / > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + vnicinfo->enabled =3D 0; > + vnicinfo->enab_dis_acked =3D 0; /* must wait for ack */ > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + > + /* send disable and wait for ack - don't hold lock when > + * sending disable because if the queue is full, insert might > + * sleep. > + */ > + SEND_ENBDIS(netdev, 0, vnicinfo->cmdrsp_rcv, > + vnicinfo->datachan.chinfo.queueinfo, > + &vnicinfo->datachan.chinfo.insertlock, > + vnicinfo->datachan.chstat); > + > + LOGINFNAME(vnicinfo->netdev, > + "Waiting for ENBDIS ACK before freeing rcv buffers...\n"); > + /* wait for ack to arrive before we try to free rcv buffers > + * NOTE: the other end automatically unposts the rcv buffers > + * when it gets a disable. > + */ > + while ((timeout =3D=3D VIRTNIC_INFINITE_RESPONSE_WAIT) || > + (wait < timeout)) { > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + if (vnicinfo->n_rcv_packet_not_accepted) { > + /* now we can continue with disable */ > + break; > + } else if (vnicinfo->server_down || > + vnicinfo->server_change_state) { > + LOGERRNAME(vnicinfo->netdev, > + "IOVM is down so disable will not be acknowledged. Stopping = wait.\n"); > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + return -1; > + } > + set_current_state(TASK_INTERRUPTIBLE); > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + wait +=3D schedule_timeout(msecs_to_jiffies(10)); > + } > + if (!vnicinfo->n_rcv_packet_not_accepted) { > + LOGERRNAME(vnicinfo->netdev, > + "IOVM did not respond to Disable in allocated time (%d msecs).= \n", > + timeout); > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + return -1; > + } > + LOGINFNAME(vnicinfo->netdev, > + "Got ENBDIS ACK; now waiting for 0 usage count...\n"); > + > + /* > + * wait for usage to go to 1 (no other users) before freeing > + * rcv buffers > + */ > + if (atomic_read(&vnicinfo->usage) > 1) { > + /* wait for usage count to be 1 */ > + while (1) { > + set_current_state(TASK_INTERRUPTIBLE); > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + schedule_timeout(msecs_to_jiffies(10)); > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + if (atomic_read(&vnicinfo->usage) =3D=3D 1) { > + break; /* go do work and only after > + that give up lock */ > + } > + } > + } > + /* we've set enabled to 0, so we can give up the lock. */ > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + LOGINFNAME(vnicinfo->netdev, > + "Usage count is 0; freeing the rcv buffers now\n"); > + > + /* free rcv buffers - other end has automatically unposted > + * them on disable > + */ > + for (i =3D 0; i < vnicinfo->num_rcv_bufs; i++) { > + if (vnicinfo->rcvbuf[i]) { > + kfree_skb(vnicinfo->rcvbuf[i]); > + vnicinfo->rcvbuf[i] =3D NULL; > + count++; > + } > + } > + LOGINFNAME(vnicinfo->netdev, "Freed %d rcv bufs\n", count); > + > + /* remove references from debug array */ > + for (i =3D 0; i < VIRTNICSOPENMAX; i++) { > + if (num_virtnic_open[i].netdev =3D=3D netdev) { > + num_virtnic_open[i].netdev =3D NULL; > + num_virtnic_open[i].vnicinfo =3D NULL; > + break; > + } > + } > + > + return 0; > +} > + > +/* Wait indefinitely for IOVM to acknowledge disable request */ > +static int > +virtnic_disable(struct net_device *netdev) > +{ > + return virtnic_disable_with_timeout(netdev, > + VIRTNIC_INFINITE_RESPONSE_WAIT); > +} > + > +/* Sends enable to IOVM, inits, and posts receive buffers to IOVM > + * returns 0 on success, negative number on failure > + * > + * timeout is defined in msecs (timeout of 0 specifies infinite wait= ) > + */ > +static int > +virtnic_enable_with_timeout(struct net_device *netdev, const int tim= eout) > +{ > + int i; > + struct virtnic_info *vnicinfo =3D netdev_priv(netdev); > + unsigned long flags; > + int wait =3D 0; > + > + /* NOTE: the other end automatically unposts the rcv buffers when > + * it gets a disable. > + */ > + i =3D init_rcv_bufs(netdev, vnicinfo); > + if (i < 0) > + return i; > + > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + vnicinfo->enabled =3D 1; > + /* now we're ready, let's send an ENB to uisnic but until we > + * get an ACK back from uisnic, we'll drop the packets > + */ > + vnicinfo->n_rcv_packet_not_accepted =3D 0; > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + > + /* send enable and wait for ack - don't hold lock when sending > + * enable because if the queue is full, insert might sleep. > + */ > + SEND_ENBDIS(netdev, 1, vnicinfo->cmdrsp_rcv, > + vnicinfo->datachan.chinfo.queueinfo, > + &vnicinfo->datachan.chinfo.insertlock, > + vnicinfo->datachan.chstat); > + > + LOGINFNAME(vnicinfo->netdev, "netdev->name <<%s>>", netdev->name); > + LOGINFNAME(vnicinfo->netdev, > + "Waiting for ENBDIS ACK before starting device queue...\n"); > + while ((timeout =3D=3D VIRTNIC_INFINITE_RESPONSE_WAIT) || > + (wait < timeout)) { > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + if (vnicinfo->enab_dis_acked) { > + /* now we can continue */ > + break; > + } else if (vnicinfo->server_down || > + vnicinfo->server_change_state) { > + /* IOVM is going down so don't wait for a response */ > + LOGERRNAME(vnicinfo->netdev, > + "IOVM is down so enable will not be acknowledged. Stopping w= ait.\n"); > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + return -1; > + } > + set_current_state(TASK_INTERRUPTIBLE); > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + wait +=3D schedule_timeout(msecs_to_jiffies(10)); > + } > + if (!vnicinfo->enab_dis_acked) { > + LOGERRNAME(vnicinfo->netdev, > + "IOVM did not respond to Enable in allocated time (%d msecs).\= n", > + timeout); > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + return -1; > + } > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + LOGINFNAME(vnicinfo->netdev, "Got ENBDIS ACK\n"); > + > + /* find an open slot in the array to save off VirtNic > + * references for debug > + */ > + for (i =3D 0; i < VIRTNICSOPENMAX; i++) { > + if (num_virtnic_open[i].netdev =3D=3D NULL) { > + num_virtnic_open[i].netdev =3D netdev; > + num_virtnic_open[i].vnicinfo =3D vnicinfo; > + break; > + } > + } > + if (i =3D=3D VIRTNICSOPENMAX) > + LOGINFNAME(vnicinfo->netdev, > + "No storage for debug ref for netdev =3D 0x%p vnicinfo =3D 0x%= p\n", > + netdev, vnicinfo); > + > + return 0; > +} > + > +/* Wait indefinitely for IOVM to acknowledge enable request */ > +static int > +virtnic_enable(struct net_device *netdev) > +{ > + return virtnic_enable_with_timeout(netdev, > + VIRTNIC_INFINITE_RESPONSE_WAIT); > +} > + > +static void > +send_rcv_posts_if_needed(struct virtnic_info *vnicinfo) > +{ > + int i; > + struct net_device *netdev; > + struct uiscmdrsp *cmdrsp =3D vnicinfo->cmdrsp_rcv; > + int cur_num_rcv_bufs_to_alloc, rcv_bufs_allocated; > + > + if (!(vnicinfo->enabled && vnicinfo->enab_dis_acked)) { > + /* dont do this until vnic is marked ready. */ > + return; > + } > + netdev =3D vnicinfo->netdev; > + rcv_bufs_allocated =3D 0; > + /* this code is trying to prevent getting stuck here forever, > + * but still retry it if you cant allocate them all this > + * time. > + */ > + cur_num_rcv_bufs_to_alloc =3D vnicinfo->num_rcv_bufs_could_not_allo= c; > + while (cur_num_rcv_bufs_to_alloc > 0) { > + cur_num_rcv_bufs_to_alloc--; > + for (i =3D 0; i < vnicinfo->num_rcv_bufs; i++) { > + if (vnicinfo->rcvbuf[i] !=3D NULL) > + continue; > + vnicinfo->rcvbuf[i] =3D alloc_rcv_buf(netdev); > + if (!vnicinfo->rcvbuf[i]) { > + LOGVER("**** %s FAILED to allocate new rcv buf - no REPOST\n", > + netdev->name); > + vnicinfo-> > + alloc_failed_in_if_needed_cnt++; > + break; > + } else { > + rcv_bufs_allocated++; > + post_skb(cmdrsp, vnicinfo, > + vnicinfo->rcvbuf[i]); > + vnicinfo->datachan.chstat. > + extra_rcvbufs_sent++; > + } > + } > + } > + vnicinfo->num_rcv_bufs_could_not_alloc -=3D rcv_bufs_allocated; > + if (vnicinfo->num_rcv_bufs_could_not_alloc > 0) { > + /* > + * this path means you failed to alloc an skb in the > + * normal path, and you are trying again later, and > + * it still fails. > + */ > + LOGVER("attempted to recover buffers which could not be allocated = and failed"); > + LOGVER("rcv_bufs_allocated=3D%d, num_rcv_bufs_could_not_alloc=3D%d= ", > + rcv_bufs_allocated, > + vnicinfo->num_rcv_bufs_could_not_alloc); > + } > +} > + > +static void > +drain_queue(struct datachan *dc, struct uiscmdrsp *cmdrsp, > + struct virtnic_info *vnicinfo) > +{ > + unsigned long flags; > + int qrslt; > + struct net_device *netdev; > + > + /* drain queue */ > + while (1) { > + spin_lock_irqsave(&dc->chinfo.insertlock, flags); > + if (!spar_channel_client_acquire_os(dc->chinfo.queueinfo->chan, > + "vnic")) { > + spin_unlock_irqrestore(&dc->chinfo.insertlock, > + flags); > + break; > + } > + qrslt =3D uisqueue_get_cmdrsp(dc->chinfo.queueinfo, cmdrsp, > + IOCHAN_FROM_IOPART); > + spar_channel_client_release_os(dc->chinfo.queueinfo->chan, > + "vnic"); > + spin_unlock_irqrestore(&dc->chinfo.insertlock, flags); > + if (qrslt =3D=3D 0) > + break; /* queue empty */ > + DBGINF("%p cmdrsp->net.type:%d\n", > + &dc->chinfo.queueinfo, cmdrsp->net.type); > + switch (cmdrsp->net.type) { > + case NET_RCV: > + DBGINF("Got NET_RCV\n"); > + dc->chstat.got_rcv++; > + /* process incoming packet */ > + virtnic_rx(cmdrsp); > + break; > + case NET_XMIT_DONE: > + DBGINF("Got NET_XMIT_DONE %p\n", cmdrsp->net.buf); > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + dc->chstat.got_xmit_done++; > + if (cmdrsp->net.xmtdone.xmt_done_result) { > + LOGERRNAME(vnicinfo->netdev, > + "XMIT_DONE failure buf:%p\n", > + cmdrsp->net.buf); > + dc->chstat.xmit_fail++; > + } > + /* only call queue wake if we stopped it */ > + netdev =3D ((struct sk_buff *)cmdrsp->net.buf)->dev; > + /* ASSERT netdev =3D=3D vnicinfo->netdev; */ > + if (netdev !=3D vnicinfo->netdev) { > + LOGERRNAME(vnicinfo->netdev, "NET_XMIT_DONE something wrong; vni= cinfo->netdev:%p !=3D cmdrsp->net.buf)->dev:%p\n", > + vnicinfo->netdev, netdev); > + } else if (netif_queue_stopped(netdev)) { > + /* > + * check to see if we have crossed > + * the lower watermark for > + * netif_wake_queue() > + */ > + if (((vnicinfo->datachan.chstat.sent_xmit >=3D > + vnicinfo->datachan.chstat.got_xmit_done) && > + (vnicinfo->datachan.chstat.sent_xmit - > + vnicinfo->datachan.chstat.got_xmit_done <=3D > + vnicinfo->lower_threshold_net_xmits)) || > + ((vnicinfo->datachan.chstat.sent_xmit < > + vnicinfo->datachan.chstat.got_xmit_done) && > + (ULONG_MAX - > + vnicinfo->datachan.chstat.got_xmit_done > + + vnicinfo->datachan.chstat.sent_xmit <=3D > + vnicinfo->lower_threshold_net_xmits))) { > + /* > + * enough NET_XMITs completed > + * so can restart netif queue > + */ > + netif_wake_queue(netdev); > + vnicinfo->flow_control_lower_hits++; > + } > + } > + skb_unlink(cmdrsp->net.buf, &vnicinfo->xmitbufhead); > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + kfree_skb(cmdrsp->net.buf); > + break; > + case NET_RCV_ENBDIS_ACK: > + DBGINF("Got NET_RCV_ENBDIS_ACK on:%p\n", > + (struct net_device *) > + cmdrsp->net.enbdis.context); > + dc->chstat.got_enbdisack++; > + netdev =3D (struct net_device *) > + cmdrsp->net.enbdis.context; > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + vnicinfo->enab_dis_acked =3D 1; > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + > + if (vnicinfo->server_down && > + vnicinfo->server_change_state) { > + /* Inform Linux that the link is up */ > + vnicinfo->server_down =3D false; > + vnicinfo->server_change_state =3D false; > + netif_wake_queue(netdev); > + netif_carrier_on(netdev); > + } > + break; > + case NET_CONNECT_STATUS: > + DBGINF("NET_CONNECT_STATUS, enable=3D:%d\n", > + cmdrsp->net.enbdis.enable); > + netdev =3D vnicinfo->netdev; > + if (cmdrsp->net.enbdis.enable =3D=3D 1) { > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + vnicinfo->enabled =3D cmdrsp->net.enbdis.enable; > + spin_unlock_irqrestore(&vnicinfo->priv_lock, > + flags); > + netif_wake_queue(netdev); > + netif_carrier_on(netdev); > + } else { > + netif_stop_queue(netdev); > + netif_carrier_off(netdev); > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + vnicinfo->enabled =3D cmdrsp->net.enbdis.enable; > + spin_unlock_irqrestore(&vnicinfo->priv_lock, > + flags); > + } > + break; > + default: > + LOGERRNAME(vnicinfo->netdev, > + "Invalid net type:%d in cmdrsp\n", > + cmdrsp->net.type); > + break; > + } > + /* cmdrsp is now available for reuse */ > + > + if (dc->chinfo.threadinfo.should_stop) > + break; > + } > +} > + > +static int > +process_incoming_rsps(void *v) > +{ > + struct datachan *dc =3D v; > + struct uiscmdrsp *cmdrsp =3D NULL; > + const int SZ =3D SIZEOF_CMDRSP; > + struct virtnic_info *vnicinfo; > + struct channel_header __iomem *p_channel_header; > + struct signal_queue_header __iomem *pqhdr; > + uint64_t mask; > + unsigned long long rc1; > + > + UIS_DAEMONIZE("vnic_incoming"); > + DBGINF("In process_incoming_rsps pid:%d queueinfo:%p threadinfo:%p\= n", > + current->pid, dc->chinfo.queueinfo, &dc->chinfo.threadinfo); > + /* alloc once and reuse */ > + vnicinfo =3D container_of(dc, struct virtnic_info, datachan); > + cmdrsp =3D kmalloc(SZ, GFP_ATOMIC); > + if (cmdrsp =3D=3D NULL) { > + LOGERRNAME(vnicinfo->netdev, > + "**** FAILED to malloc - thread exiting\n"); > + complete_and_exit(&dc->chinfo.threadinfo.has_stopped, 0); > + } > + p_channel_header =3D vnicinfo->datachan.chinfo.queueinfo->chan; > + pqhdr =3D > + (struct signal_queue_header __iomem *) > + ((char __iomem *)p_channel_header + > + readq(&p_channel_header->ch_space_offset)) + > + IOCHAN_FROM_IOPART; > + mask =3D ULTRA_CHANNEL_ENABLE_INTS; > + while (1) { > + wait_event_interruptible_timeout( > + vnicinfo->rsp_queue, (atomic_read > + (&vnicinfo->interrupt_rcvd) =3D=3D 1), > + msecs_to_jiffies(vnicinfo->thread_wait_ms)); > + /* > + * periodically check to see if there any rcv bufs which > + * need to get sent to the iovm. This can only happen if > + * we run out of memory when trying to allocate skbs. > + */ > + atomic_set(&vnicinfo->interrupt_rcvd, 0); > + send_rcv_posts_if_needed(vnicinfo); > + drain_queue(dc, cmdrsp, vnicinfo); > + rc1 =3D uisqueue_interlocked_or((uint64_t __iomem *) > + vnicinfo->flags_addr, mask); > + if (dc->chinfo.threadinfo.should_stop) > + break; > + } > + > + kfree(cmdrsp); > + DBGINF("In process_incoming_nic_rsp exiting\n"); > + complete_and_exit(&dc->chinfo.threadinfo.has_stopped, 0); > +} > + > +/*****************************************************/ > +/* NIC support functions called external */ > +/*****************************************************/ > + > +static int > +virtnic_change_mtu(struct net_device *netdev, int new_mtu) > +{ > + LOGERRNAME(netdev, "netdev->name <<%s>>", netdev->name); > + LOGERRNAME(netdev, "**** FAILED: MTU cannot be changed at this end.= \n"); > + LOGERRNAME(netdev, "The same MTU is used for all the PNICs and VNIC= s in a switch.\n"); > + LOGERRNAME(netdev, "Please change MTU from the Resource Partition\n= "); > + LOGERRNAME(netdev, "Current MTU is: %d\n", netdev->mtu); > + return -EINVAL; > + /* > + * we cannot willy-nilly change the MTU; it has to come from > + * CONTROL VM and all the vnics and pnics in a switch have to > + * have the same MTU for everything to work. > + */ > +} > + > +/* > + * Called by kernel when ifconfig down is run. > + * Returns 0 on success, negative value on failure. > + */ > +static int > +virtnic_close(struct net_device *netdev) > +{ > + /* this is called on ifconfig down but also if the device is > + * being removed > + */ > + LOGINFNAME(netdev, "Closing %p name:%s\n", netdev, netdev->name); > + > + netif_stop_queue(netdev); > + virtnic_disable(netdev); > + > + LOGINFNAME(netdev, "Closed:%p\n", netdev); > + > + return 0; > +} > + > +static int > +virtnic_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) > +{ > + return -EOPNOTSUPP; > +} > + > +/* > + * Called by kernel when ifconfig up is run. > + * Returns 0 on success, negative value on failure. > +*/ > +static int > +virtnic_open(struct net_device *netdev) > +{ > + struct virtnic_info *vnicinfo =3D netdev_priv(netdev); > + void *p =3D (__force void *)netdev->ip_ptr; > + > + LOGINFNAME(vnicinfo->netdev, > + "Opening %p name:%s allocating:%d rcvbufs mtu:%d\n", netdev, > + netdev->name, vnicinfo->num_rcv_bufs, netdev->mtu); > + > + virtnic_enable(netdev); > + /* start the interface's transmit queue, allowing it accept > + * packets for transmission > + */ > + netif_start_queue(netdev); > + > + LOGINFNAME(vnicinfo->netdev, > + "Opened %p netdev->ip_ptr:%p name:%s %02x:%02x:%02x:%02x:%02x:%= 02x\n", > + netdev, netdev->ip_ptr, netdev->name, netdev->dev_addr[0], > + netdev->dev_addr[1], netdev->dev_addr[2], > + netdev->dev_addr[3], netdev->dev_addr[4], > + netdev->dev_addr[5]); > + > + /* > + * temporary code to see trap to catch if vnic inet addresses > + * are getting trashed > + */ > + if (p !=3D (__force void *)netdev->ip_ptr) { > + LOGERRNAME(vnicinfo->netdev, "***********FAILURE HAPPENED\n"); > + LOGERRNAME(vnicinfo->netdev, " Test to catch if vnic ine= t addresses are getting trashed.\n"); > + set_current_state(TASK_INTERRUPTIBLE); > + schedule_timeout(msecs_to_jiffies(1000)); > + } > + return 0; > +} > + > +static inline int > +repost_return( > + struct uiscmdrsp *cmdrsp, > + struct virtnic_info *vnicinfo, > + struct sk_buff *skb, > + struct net_device *netdev) > +{ > + struct net_pkt_rcv copy; > + int i =3D 0, cc, numreposted; > + int found_skb =3D 0; > + int status =3D 0; > + > + copy =3D cmdrsp->net.rcv; > + LOGVER("REPOST_RETURN: realloc rcv skbs to replace:%d rcvbufs\n", > + copy.numrcvbufs); > + switch (copy.numrcvbufs) { > + case 0: > + vnicinfo->n_rcv0++; > + break; > + case 1: > + vnicinfo->n_rcv1++; > + break; > + case 2: > + vnicinfo->n_rcv2++; > + break; > + default: > + vnicinfo->n_rcvx++; > + break; > + } > + for (cc =3D 0, numreposted =3D 0; cc < copy.numrcvbufs; cc++) { > + for (i =3D 0; i < vnicinfo->num_rcv_bufs; i++) { > + if (vnicinfo->rcvbuf[i] !=3D copy.rcvbuf[cc]) > + continue; > + > + LOGVER("REPOST_RETURN: orphaning old rcvbuf[%d]:%p cc=3D%d", > + i, vnicinfo->rcvbuf[i], cc); > + vnicinfo->found_repost_rcvbuf_cnt++; > + if ((skb) && vnicinfo->rcvbuf[i] =3D=3D skb) { > + found_skb =3D 1; > + vnicinfo->repost_found_skb_cnt++; > + } > + vnicinfo->rcvbuf[i] =3D alloc_rcv_buf(netdev); > + if (!vnicinfo->rcvbuf[i]) { > + LOGVER("**** %s FAILED to reallocate new rcv buf - no REPOST, fo= und_skb=3D%d, cc=3D%d, i=3D%d\n", > + netdev->name, found_skb, cc, i); > + vnicinfo->num_rcv_bufs_could_not_alloc++; > + vnicinfo->alloc_failed_in_repost_return_cnt++; > + status =3D -1; > + break; > + } > + LOGVER("REPOST_RETURN: reposting new rcvbuf[%d]:%p\n", > + i, vnicinfo->rcvbuf[i]); > + post_skb(cmdrsp, vnicinfo, vnicinfo->rcvbuf[i]); > + numreposted++; > + break; > + } > + } > + LOGVER("REPOST_RETURN: num rcvbufs posted:%d\n", numreposted); > + if (numreposted !=3D copy.numrcvbufs) { > + LOGVER("**** %s FAILED to repost all the rcv bufs; numreposted:%d = rcv.numrcvbufs:%d\n", > + netdev->name, numreposted, copy.numrcvbufs); > + vnicinfo->n_repost_deficit++; > + status =3D -1; > + } > + if (skb) { > + if (found_skb) { > + LOGVER("REPOST_RETURN: skb is %p - freeing it", skb); > + kfree_skb(skb); > + } else { > + LOGERRNAME(vnicinfo->netdev, "%s REPOST_RETURN: skb %p NOT found = in rcvbuf list!!", > + netdev->name, skb); > + status =3D -3; > + vnicinfo->bad_rcv_buf++; > + } > + } > + atomic_dec(&vnicinfo->usage); > + return status; > +} > + > +static void > +virtnic_rx(struct uiscmdrsp *cmdrsp) > +{ > + struct virtnic_info *vnicinfo; > + struct sk_buff *skb, *prev, *curr; > + struct net_device *netdev; > + int cc, currsize, off, status; > + struct ethhdr *eth; > + unsigned long flags; > +#ifdef DEBUG > + struct phys_info testfrags[MAX_PHYS_INFO]; > +#endif > + > +/* > + * post new rcv buf to the other end using the cmdrsp we have at han= d > + * post it without holding lock - but we'll use the signal lock to s= ynchronize > + * the queue insert the cmdrsp that contains the net.rcv is the one = we are > + * using to repost, so copy the info we need from it. > + */ > + skb =3D cmdrsp->net.buf; > + netdev =3D skb->dev; > + > + if (netdev) > + DBGINF("in virtnic_rx %p %s len:%d\n", netdev, netdev->name, > + cmdrsp->net.rcv.rcv_done_len); > + else { > + /* We must have previously downed this network device and > + * this skb and device is no longer valid. This also means > + * the skb reference was removed from virtnic->rcvbuf so no > + * need to search for it. > + * All we can do is free the skb and return. > + * Note: We crash if we try to log this here. > + */ > + kfree_skb(skb); > + return; > + } > + > + vnicinfo =3D netdev_priv(netdev); > + > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + atomic_dec(&vnicinfo->num_rcv_bufs_in_iovm); > + > + /* update rcv stats - call it with priv_lock held */ > + UPD_RCV_STATS; > + > + atomic_inc(&vnicinfo->usage); /* don't want a close to happen befor= e > + we're done here */ > + /* > + * set length to how much was ACTUALLY received - > + * NOTE: rcv_done_len includes actual length of data rcvd > + * including ethhdr > + */ > + skb->len =3D cmdrsp->net.rcv.rcv_done_len; > + > + /* test enabled while holding lock */ > + if (!(vnicinfo->enabled && vnicinfo->enab_dis_acked)) { > + /* > + * don't process it unless we're in enable mode and until > + * we've gotten an ACK saying the other end got our RCV enable > + */ > + LOGERRNAME(vnicinfo->netdev, > + "%s dropping packet - perhaps old\n", netdev->name); > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + if (repost_return(cmdrsp, vnicinfo, skb, netdev) < 0) > + LOGERRNAME(vnicinfo->netdev, "repost_return failed"); > + return; > + } > + > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + > + /* > + * when skb was allocated, skb->dev, skb->data, skb->len and > + * skb->data_len were setup. AND, data has already put into the > + * skb (both first frag and in frags pages) > + * NOTE: firstfragslen is the amount of data in skb->data and that > + * which is not in nr_frags or frag_list. This is now simply > + * RCVPOST_BUF_SIZE. bump tail to show how much data is in > + * firstfrag & set data_len to show rest see if we have to chain > + * frag_list. > + */ > + if (skb->len > RCVPOST_BUF_SIZE) { /* do PRECAUTIONARY check */ > + if (cmdrsp->net.rcv.numrcvbufs < 2) { > + LOGERRNAME(vnicinfo->netdev, "**** %s Something is wrong; rcv_don= e_len:%d > RCVPOST_BUF_SIZE:%d but numrcvbufs:%d < 2\n", > + netdev->name, skb->len, RCVPOST_BUF_SIZE, > + cmdrsp->net.rcv.numrcvbufs); > + if (repost_return(cmdrsp, vnicinfo, skb, netdev) < 0) > + LOGERRNAME(vnicinfo->netdev, > + "repost_return failed"); > + return; > + } > + /* length rcvd is greater than firstfrag in this skb rcv buf */ > + skb->tail +=3D RCVPOST_BUF_SIZE; /* amount in skb->data */ > + skb->data_len =3D skb->len - RCVPOST_BUF_SIZE; /* amount that > + will be in > + frag_list */ > + DBGINF("len:%d data:%d\n", skb->len, skb->data_len); > + } else { > + /* > + * data fits in this skb - no chaining - do PRECAUTIONARY check > + */ > + if (cmdrsp->net.rcv.numrcvbufs !=3D 1) { /* should be 1 */ > + LOGERRNAME(vnicinfo->netdev, "**** %s Something is wrong; rcv_don= e_len:%d <=3D RCVPOST_BUF_SIZE:%d but numrcvbufs:%d !=3D 1\n", > + netdev->name, skb->len, RCVPOST_BUF_SIZE, > + cmdrsp->net.rcv.numrcvbufs); > + if (repost_return(cmdrsp, vnicinfo, skb, netdev) < 0) > + LOGERRNAME(vnicinfo->netdev, > + "repost_return failed"); > + return; > + } > + skb->tail +=3D skb->len; > + skb->data_len =3D 0; /* nothing rcvd in frag_list */ > + } > + off =3D skb_tail_pointer(skb) - skb->data; > + /* > + * amount we bumped tail by in the head skb > + * it is used to calculate the size of each chained skb below > + * it is also used to index into bufline to continue the copy > + * (for chansocktwopc) > + * if necessary chain the rcv skbs together. > + * NOTE: index 0 has the same as cmdrsp->net.rcv.skb; we need to > + * chain the rest to that one. > + * - do PRECAUTIONARY check > + */ > + if (cmdrsp->net.rcv.rcvbuf[0] !=3D skb) { > + LOGERRNAME(vnicinfo->netdev, "**** %s Something is wrong; rcvbuf[0= ]:%p !=3D skb:%p\n", > + netdev->name, cmdrsp->net.rcv.rcvbuf[0], skb); > + if (repost_return(cmdrsp, vnicinfo, skb, netdev) < 0) > + LOGERRNAME(vnicinfo->netdev, "repost_return failed"); > + return; > + } > + > + if (cmdrsp->net.rcv.numrcvbufs > 1) { > + /* chain the various rcv buffers into the skb's frag_list. */ > + /* Note: off was initialized above */ > + for (cc =3D 1, prev =3D NULL; > + cc < cmdrsp->net.rcv.numrcvbufs; cc++) { > + curr =3D (struct sk_buff *)cmdrsp->net.rcv.rcvbuf[cc]; > + curr->next =3D NULL; > + DBGINF("chaining skb:%p data:%p to skb:%p data:%p\n", > + curr, curr->data, skb, skb->data); > + if (prev =3D=3D NULL) /* start of list- set head */ > + skb_shinfo(skb)->frag_list =3D curr; > + else > + prev->next =3D curr; > + prev =3D curr; > + /* > + * should we set skb->len and skb->data_len for each > + * buffer being chained??? can't hurt! > + */ > + currsize =3D > + min(skb->len - off, > + (unsigned int)RCVPOST_BUF_SIZE); > + curr->len =3D currsize; > + curr->tail +=3D currsize; > + curr->data_len =3D 0; > + off +=3D currsize; > + } > +#ifdef DEBUG > + /* assert skb->len =3D=3D off */ > + if (skb->len !=3D off) { > + LOGERRNAME(vnicinfo->netdev, "%s something wrong; skb->len:%d !=3D= off:%d\n", > + netdev->name, skb->len, off); > + } > + /* test code */ > + cc =3D util_copy_fragsinfo_from_skb("rcvchaintest", skb, > + RCVPOST_BUF_SIZE, > + MAX_PHYS_INFO, testfrags); > + LOGINFNAME(vnicinfo->netdev, "rcvchaintest returned:%d\n", cc); > + if (cc !=3D cmdrsp->net.rcv.numrcvbufs) { > + LOGERRNAME(vnicinfo->netdev, "**** %s Something wrong; rcvd chain= length %d different from one we calculated %d\n", > + netdev->name, cmdrsp->net.rcv.numrcvbufs, > + cc); > + } > + for (i =3D 0; i < cc; i++) { > + LOGINFNAME(vnicinfo->netdev, "test:RCVPOST_BUF_SIZE:%d[%d] pfn:%l= lu off:0x%x len:%d\n", > + RCVPOST_BUF_SIZE, i, testfrags[i].pi_pfn, > + testfrags[i].pi_off, testfrags[i].pi_len); > + } > +#endif > + } > + > + /* set up packet's protocl type using ethernet header - this > + * sets up skb->pkt_type & it also PULLS out the eth header > + */ > + skb->protocol =3D eth_type_trans(skb, netdev); > + > + eth =3D eth_hdr(skb); > + > + DBGINF("%d Src:%02x:%02x:%02x:%02x:%02x:%02x Dest:%02x:%02x:%02x:%0= 2x:%02x:%02x proto:%x\n", > + skb->pkt_type, eth->h_source[0], eth->h_source[1], > + eth->h_source[2], eth->h_source[3], eth->h_source[4], > + eth->h_source[5], eth->h_dest[0], eth->h_dest[1], eth->h_des= t[2], > + eth->h_dest[3], eth->h_dest[4], eth->h_dest[5], eth->h_proto= ); > + > + skb->csum =3D 0; > + skb->ip_summed =3D CHECKSUM_NONE; /* trust me, the checksum has > + been verified */ > + > + do { > + if (netdev->flags & IFF_PROMISC) { > + DBGINF("IFF_PROMISC is set.\n"); > + break; /* accept all packets */ > + } > + if (skb->pkt_type =3D=3D PACKET_BROADCAST) { > + DBGINF("packet is broadcast.\n"); > + if (netdev->flags & IFF_BROADCAST) { > + DBGINF("IFF_BROADCAST is set.\n"); > + break; /* accept all broadcast packets */ > + } > + } else if (skb->pkt_type =3D=3D PACKET_MULTICAST) { > + DBGINF("packet is multicast.\n"); > + if (netdev->flags & IFF_ALLMULTI) > + DBGINF("IFF_ALLMULTI is set.\n"); > + if ((netdev->flags & IFF_MULTICAST) && > + (netdev_mc_count(netdev))) { > + struct netdev_hw_addr *ha; > + int found_mc =3D 0; > + > + DBGINF("IFF_MULTICAST is set %d.\n", > + netdev_mc_count(netdev)); > + /* > + * only accept multicast packets that we can > + * find in our multicast address list > + */ > + netdev_for_each_mc_addr(ha, netdev) { > + if (memcmp > + (eth->h_dest, ha->addr, > + MAX_MACADDR_LEN) =3D=3D 0) { > + DBGINF("multicast address is in our list at index:%i.\n", i); > + found_mc =3D 1; > + break; > + } > + } > + if (found_mc) { > + break; /* accept packet, dest > + matches a multicast > + address */ > + } > + } > + } else if (skb->pkt_type =3D=3D PACKET_HOST) { > + DBGINF("packet is directed.\n"); > + break; /* accept packet, h_dest must match vnic > + mac address */ > + } else if (skb->pkt_type =3D=3D PACKET_OTHERHOST) { > + /* something is not right */ > + LOGERRNAME(vnicinfo->netdev, "**** FAILED to deliver rcv packet t= o OS; name:%s Dest:%02x:%02x:%02x:%02x:%02x:%02x VNIC:%02x:%02x:%02x:%0= 2x:%02x:%02x\n", > + netdev->name, eth->h_dest[0], eth->h_dest[1], > + eth->h_dest[2], eth->h_dest[3], > + eth->h_dest[4], eth->h_dest[5], > + netdev->dev_addr[0], netdev->dev_addr[1], > + netdev->dev_addr[2], netdev->dev_addr[3], > + netdev->dev_addr[4], netdev->dev_addr[5]); > + } > + /* drop packet - don't forward it up to OS */ > + DBGINF("we cannot indicate this recv pkt! (netdev->flags:0x%04x, s= kb->pkt_type:0x%02x).\n", > + netdev->flags, skb->pkt_type); > + vnicinfo->n_rcv_packet_not_accepted++; > + if (repost_return(cmdrsp, vnicinfo, skb, netdev) < 0) > + LOGERRNAME(vnicinfo->netdev, "repost_return failed"); > + return; > + } while (0); > + > + DBGINF("Calling netif_rx skb:%p head:%p end:%p data:%p tail:%p len:= %d data_len:%d skb->nr_frags:%d\n", > + skb, skb->head, skb->end, skb->data, skb->tail, skb->len, > + skb->data_len, skb_shinfo(skb)->nr_frags); > + > + status =3D netif_rx(skb); > + if (status !=3D NET_RX_SUCCESS) > + LOGWRNNAME(vnicinfo->netdev, "status=3D%d\n", status); > + /* > + * netif_rx returns various values, but "in practice most drivers > + * ignore the return value > + */ > + > + skb =3D NULL; > + /* > + * whether the packet got dropped or handled, the skb is freed by > + * kernel code, so we shouldn't free it. but we should repost a > + * new rcv buffer. > + */ > + if (repost_return(cmdrsp, vnicinfo, skb, netdev) < 0) > + LOGVER("repost_return failed"); > + return; > +} > + > +/* > + * This function is protected from concurrent calls by a spinlock xm= it_lock > + * in the net_device struct, but as soon as the function returns it= can be > + * called again. > + * Return 0, OK, !0 for error. > + */ > +static int > +virtnic_xmit(struct sk_buff *skb, struct net_device *netdev) > +{ > + struct virtnic_info *vnicinfo; > + int len, firstfraglen, padlen; > + struct uiscmdrsp *cmdrsp =3D NULL; > + unsigned long flags; > + int qrslt; > + > +/* Note: NETDEV_TX_OK is 0, NETDEV_TX_BUSY is 1. */ > +#define BUSY { \ > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); \ > + vnicinfo->busy_cnt++; \ > + return NETDEV_TX_BUSY; \ > +} > + > +/* return value NETDEV_TX_OK =3D=3D 0 */ > + DBGINF("got xmit for netdev:%p %s len:%d ip_summed:%d skb->data:%p = data_len:%d skb->h.raw:%p maxdatalen:%d\n", > + netdev, netdev->name, skb->len, skb->ip_summed, skb->data, > + skb->data_len, skb->h.raw, skb->end - skb->data); > + > + vnicinfo =3D netdev_priv(netdev); > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + /*Modified for Trac #2395 FIX TEL_CKS */ > + if (netif_queue_stopped(netdev)) { > + LOGINFNAME(vnicinfo->netdev, > + "Returning Busy because queue is stopped\n"); > + BUSY; > + } > + if (vnicinfo->server_down || vnicinfo->server_change_state) { > + LOGINFNAME(vnicinfo->netdev, "Returning BUSY because server is dow= n/changing state\n"); > + BUSY; > + } > + /* > + * sk_buff struct is used to host network data throughout all the > + * Linux network subsystems > + */ > + len =3D skb->len; > + /* > + * skb->len is the FULL length of data (including fragmentary porti= on) > + * skb->data_len is the length of the fragment portion in frags > + * skb->len - skb->data_len is the size of the 1st fragment in skb-= >data > + * calculate the length of the first fragment that skb->data is > + * pointing to > + */ > + firstfraglen =3D skb->len - skb->data_len; > + if (firstfraglen < ETH_HEADER_SIZE) { > + LOGERRNAME(vnicinfo->netdev, "first fragment in skb->data too smal= l for ethernet header len:%d data_len:%d\n", > + skb->len, skb->data_len); > + BUSY; /* NOT LIKELY TO HAPPEN */ > + } > + > + if ((len < ETH_MIN_PACKET_SIZE) && > + ((skb_end_pointer(skb) - skb->data) >=3D ETH_MIN_PACKET_SIZE)) = { > + /* pad the packet out to minimum size */ > + padlen =3D ETH_MIN_PACKET_SIZE - len; > + DBGINF("padding %d\n", padlen); > + memset(&skb->data[len], 0, padlen); > + skb->tail +=3D padlen; > + skb->len +=3D padlen; > + len +=3D padlen; > + firstfraglen +=3D padlen; > + } > + > + cmdrsp =3D vnicinfo->xmit_cmdrsp; > + /* clear cmdrsp */ > + memset(cmdrsp, 0, SIZEOF_CMDRSP); > + cmdrsp->net.type =3D NET_XMIT; > + cmdrsp->cmdtype =3D CMD_NET_TYPE; > + > + /* save the pointer to skb - we'll need it for completion */ > + cmdrsp->net.buf =3D skb; > + > + if (((vnicinfo->datachan.chstat.sent_xmit >=3D > + vnicinfo->datachan.chstat.got_xmit_done) && > + (vnicinfo->datachan.chstat.sent_xmit - > + vnicinfo->datachan.chstat.got_xmit_done >=3D > + vnicinfo->max_outstanding_net_xmits)) || > + /* OR check wrap condition */ > + ((vnicinfo->datachan.chstat.sent_xmit < > + vnicinfo->datachan.chstat.got_xmit_done) && > + (ULONG_MAX - vnicinfo->datachan.chstat.got_xmit_done + > + vnicinfo->datachan.chstat.sent_xmit >=3D > + vnicinfo->max_outstanding_net_xmits)) > + ) { > + /* > + * too many NET_XMITs queued over to IOVM - need to wait > + * Might need to remove the below message as these might be > + * excessive under load. > + */ > + vnicinfo->datachan.chstat.reject_count++; > + if (!vnicinfo->queuefullmsg_logged && > + ((vnicinfo->datachan.chstat.reject_count & 0x3ff) =3D=3D > + 1)) { > + vnicinfo->queuefullmsg_logged =3D 1; > +#if VIRTNIC_STATS > + vnicinfo->datachan.chstat.reject_jiffies_start =3D > + jiffies; > +#endif > + LOGINFNAME(vnicinfo->netdev, "**** REJECTING NET_XMIT - rejected = count=3D%ld chstat.sent_xmit=3D%lu chstat.got_xmit_done=3D%lu\n", > + vnicinfo->datachan.chstat.reject_count, > + vnicinfo->datachan.chstat.sent_xmit, > + vnicinfo->datachan.chstat.got_xmit_done); > + } > + netif_stop_queue(netdev); /* calling stop queue */ > + BUSY; /* return status that packet not accepted */ > + } else if (vnicinfo->queuefullmsg_logged) { > +#if VIRTNIC_STATS > + LOGINFNAME(vnicinfo->netdev, "**** NET_XMITs now working again - r= ejected count =3D %ld msec =3D %ld\n", > + vnicinfo->datachan.chstat.reject_count, > + ((long)jiffies - > + (long)(vnicinfo->datachan.chstat. > + reject_jiffies_start)) * 1000 / HZ); > +#else > + LOGINFNAME(vnicinfo->netdev, "**** NET_XMITs now working again - r= ejected count =3D %ld\n", > + vnicinfo->datachan.chstat.reject_count); > +#endif > + /* queue is not blocked so reset the logging flag */ > + vnicinfo->queuefullmsg_logged =3D 0; > + } > + > + if (skb->ip_summed =3D=3D CHECKSUM_UNNECESSARY) { > + DBGINF("CHECKSUM_HW protocol:%x csum:%x tso_size:%x data:%p h.raw:= %p nh.raw:%p\n", > + skb->protocol, skb->csum, skb_shinfo(skb)->tso_size, > + skb->data, skb->h.raw, skb->nh.raw); > + cmdrsp->net.xmt.lincsum.valid =3D 1; > + cmdrsp->net.xmt.lincsum.protocol =3D skb->protocol; > + if (skb_transport_header(skb) > skb->data) { > + cmdrsp->net.xmt.lincsum.hrawoff =3D > + skb_transport_header(skb) - skb->data; > + cmdrsp->net.xmt.lincsum.hrawoffv =3D 1; > + } > + if (skb_network_header(skb) > skb->data) { > + cmdrsp->net.xmt.lincsum.nhrawoff =3D > + skb_network_header(skb) - skb->data; > + cmdrsp->net.xmt.lincsum.nhrawoffv =3D 1; > + } > + cmdrsp->net.xmt.lincsum.csum =3D skb->csum; > + } else { > + cmdrsp->net.xmt.lincsum.valid =3D 0; > + } > + /* save off the length of the entire data packet */ > + cmdrsp->net.xmt.len =3D len; /* total data length */ > + /* > + * copy ethernet header from first frag into cmdrsp > + * - everything else will be passed in frags & DMA'ed > + */ > + memcpy(cmdrsp->net.xmt.ethhdr, skb->data, ETH_HEADER_SIZE); > + /* > + * copy frags info - from skb->data we need to only provide access > + * beyond eth header > + */ > + cmdrsp->net.xmt.num_frags =3D > + uisutil_copy_fragsinfo_from_skb("virtnic_xmit", skb, firstfragl= en, > + MAX_PHYS_INFO, > + cmdrsp->net.xmt.frags); > + if (cmdrsp->net.xmt.num_frags =3D=3D -1) { > + LOGERRNAME(vnicinfo->netdev, "**** FAILED to copy fragsinfo\n"); > + BUSY; /* WILL HAPPEN ONLY IF FRAG ARRAY WITH > + MAX_PHYS_INFO ENTRIES IS NOT ENOUGH */ > + } > + > + DBGINF("Forwarding packet cmdrsp:%p\n", cmdrsp); > + > + /* > + * don't hold lock when forwarding xmit - if queue is full insert > + * might sleep > + */ > + qrslt =3D uisqueue_put_cmdrsp_with_lock_client( > + vnicinfo->datachan.chinfo.queueinfo, cmdrsp, > + IOCHAN_TO_IOPART, > + (void *)&vnicinfo->datachan.chinfo.insertlock, > + DONT_ISSUE_INTERRUPT, (uint64_t)NULL, > + 0 /* don't wait */ , > + "vnic"); > + if (!qrslt) { > + /* failed to queue xmit - return busy */ > + LOGERRNAME(vnicinfo->netdev, > + "**** FAILED to insert NET_XMIT\n"); > + netif_stop_queue(netdev); /* calling stop queue */ > + BUSY; /* return status that packet not accepted */ > + } > + /* Track the skbs that have been sent to the IOVM for XMIT */ > + skb_queue_head(&vnicinfo->xmitbufhead, skb); > + > + /* > + * set the last transmission start time > + * linux docs says: Do not forget to update netdev->trans_start to > + * jiffies after each new tx packet is given to the hardware. > + */ > + netdev->trans_start =3D jiffies; /* some code in Linux uses this. *= / > + > + /* update xmt stats */ > + UPD_XMT_STATS; > + vnicinfo->datachan.chstat.sent_xmit++; > + > + /* > + * check to see if we have hit the high watermark for > + * netif_stop_queue() > + */ > + if (((vnicinfo->datachan.chstat.sent_xmit >=3D > + vnicinfo->datachan.chstat.got_xmit_done) && > + (vnicinfo->datachan.chstat.sent_xmit - > + vnicinfo->datachan.chstat.got_xmit_done >=3D > + vnicinfo->upper_threshold_net_xmits)) || > + /* OR check wrap condition */ > + ((vnicinfo->datachan.chstat.sent_xmit < > + vnicinfo->datachan.chstat.got_xmit_done) && > + (ULONG_MAX - vnicinfo->datachan.chstat.got_xmit_done + > + vnicinfo->datachan.chstat.sent_xmit >=3D > + vnicinfo->upper_threshold_net_xmits)) > + ) { > + /* too many NET_XMITs queued over to IOVM - need to wait */ > + netif_stop_queue(netdev); /* calling stop queue - call > + netif_wake_queue() after lower > + threshold */ > + vnicinfo->flow_control_upper_hits++; > + } > + > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + > + /* skb will be freed when we get back NET_XMIT_DONE */ > + return NETDEV_TX_OK; > +} > + > +static void > +virtnic_serverdown_complete(struct work_struct *work) > +{ > + struct virtnic_info *vnicinfo; > + struct net_device *netdev; > + struct virtpci_dev *virtpcidev; > + unsigned long flags; > + int i =3D 0, count =3D 0; > + > + vnicinfo =3D > + container_of(work, struct virtnic_info, serverdown_completion); > + netdev =3D vnicinfo->netdev; > + virtpcidev =3D vnicinfo->virtpcidev; > + > + DBGINF("virtpcidev busNo<<%d>>devNo<<%d>>", virtpcidev->busNo, > + virtpcidev->deviceNo); > + DBGINF("net_device name<<%s>>", netdev->name); > + /* Stop Using Datachan */ > + uisthread_stop(&vnicinfo->datachan.chinfo.threadinfo); > + > + /* Inform Linux that the link is down */ > + netif_carrier_off(netdev); > + netif_stop_queue(netdev); > + > + /* > + * Free the skb for XMITs that haven't been serviced by the server > + * We shouldn't have to inform Linux about these IOs because they > + * are "lost in the ethernet" > + */ > + skb_queue_purge(&vnicinfo->xmitbufhead); > + > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + /* free rcv buffers */ > + for (i =3D 0; i < vnicinfo->num_rcv_bufs; i++) { > + if (vnicinfo->rcvbuf[i]) { > + kfree_skb(vnicinfo->rcvbuf[i]); > + vnicinfo->rcvbuf[i] =3D NULL; > + count++; > + } > + } > + atomic_set(&vnicinfo->num_rcv_bufs_in_iovm, 0); > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + > + LOGINFNAME(vnicinfo->netdev, "Closed:%p Freed %d rcv bufs\n", netde= v, > + count); > + > + vnicinfo->server_down =3D true; > + vnicinfo->server_change_state =3D false; > + visorchipset_device_pause_response(virtpcidev->bus_no, > + virtpcidev->device_no, 0); > +} > + > +/* As per VirtpciFunc returns 1 for success and 0 for failure */ > +static int > +virtnic_serverdown(struct virtpci_dev *virtpcidev, u32 state) > +{ > + struct net_device *netdev =3D virtpcidev->net.netdev; > + struct virtnic_info *vnicinfo =3D netdev_priv(netdev); > + > + DBGINF("virtpcidev busNo<<%d>>devNo<<%d>>", virtpcidev->busNo, > + virtpcidev->deviceNo); > + DBGINF("entering virtnic_serverdown"); > + > + if (!vnicinfo->server_down && !vnicinfo->server_change_state) { > + vnicinfo->server_change_state =3D true; > + queue_work(virtnic_serverdown_workqueue, > + &vnicinfo->serverdown_completion); > + } else if (vnicinfo->server_change_state) { > + LOGERRNAME(vnicinfo->netdev, > + "Server already processing change state message."); > + return 0; > + } else > + LOGERRNAME(vnicinfo->netdev, > + "Server already down, but another server down message received= =2E"); > + DBGINF("exiting virtnic_serverdown"); > + return 1; > +} > + > +/* As per VirtpciFunc returns 1 for success and 0 for failure */ > +static int > +virtnic_serverup(struct virtpci_dev *virtpcidev) > +{ > + struct net_device *netdev =3D virtpcidev->net.netdev; > + struct virtnic_info *vnicinfo =3D netdev_priv(netdev); > + unsigned long flags; > + > + DBGINF("entering virtnic_serverup"); > + DBGINF("virtpcidev busNo<<%d>>devNo<<%d>>", virtpcidev->busNo, > + virtpcidev->deviceNo); > + DBGINF("net_device name<<%s>>", netdev->name); > + if (vnicinfo->server_down && !vnicinfo->server_change_state) { > + vnicinfo->server_change_state =3D true; > + /* > + * Must transition channel to ATTACHED state BEFORE we can > + * start using the device again > + */ > + SPAR_CHANNEL_CLIENT_TRANSITION(vnicinfo->datachan.chinfo. > + queueinfo->chan, > + dev_name(&virtpcidev-> > + generic_dev), > + CHANNELCLI_ATTACHED, NULL); > + > + if (!uisthread_start(&vnicinfo->datachan.chinfo.threadinfo, > + process_incoming_rsps, > + &vnicinfo->datachan, "vnic_incoming")) { > + LOGERRNAME(vnicinfo->netdev, > + "**** FAILED to start thread\n"); > + return 0; > + } > + > + init_rcv_bufs(netdev, vnicinfo); > + > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + vnicinfo->enabled =3D 1; > + /* > + * now we're ready, let's send an ENB to uisnic > + * but until we get an ACK back from uisnic, we'll drop > + * the packets > + */ > + vnicinfo->enab_dis_acked =3D 0; > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + > + /* > + * send enable and wait for ack - don't hold lock when > + * sending enable because if the queue is full, insert > + * might sleep. > + */ > + SEND_ENBDIS(netdev, 1, vnicinfo->cmdrsp_rcv, > + vnicinfo->datachan.chinfo.queueinfo, > + &vnicinfo->datachan.chinfo.insertlock, > + vnicinfo->datachan.chstat); > + } else if (vnicinfo->server_change_state) { > + LOGERRNAME(vnicinfo->netdev, > + "Server already processing change state message."); > + return 0; > + } else { > + DBGINF("Server up message received for server that was already up.= "); > + } > + DBGINF("exiting virtnic_serverup"); > + return 1; > +} > + > +static void > +virtnic_timeout_reset(struct work_struct *work) > +{ > + struct virtnic_info *vnicinfo; > + struct net_device *netdev; > + struct virtpci_dev *virtpcidev; > + int response =3D 0; > + > + vnicinfo =3D container_of(work, struct virtnic_info, timeout_reset)= ; > + netdev =3D vnicinfo->netdev; > + > + DBGINF("net_device name<<%s>>", netdev->name); > + /* Transmit Timeouts are typically handled by resetting the > + * device for our virtual NIC we will send a Disable and > + * Enable to the IOVM. If it doesn't respond we will trigger > + * a serverdown > + */ > + DBGINF("Disabling connection to server.\n"); > + netif_stop_queue(netdev); > + response =3D virtnic_disable_with_timeout(netdev, 100); > + if (response !=3D 0) > + goto call_serverdown; > + > + DBGINF("Disable returned so reenable connection to server.\n"); > + response =3D virtnic_enable_with_timeout(netdev, 100); > + if (response !=3D 0) > + goto call_serverdown; > + netif_wake_queue(netdev); > + > + LOGWRNNAME(vnicinfo->netdev, "Virtual connection reset.\n"); > + return; > + > +call_serverdown: > + LOGERRNAME(vnicinfo->netdev, > + "Disable/enabled Pair failed to return so start serverdown.\n")= ; > + virtpcidev =3D vnicinfo->virtpcidev; > + virtnic_serverdown(virtpcidev, 0); > + return; > +} > + > +static void > +virtnic_xmit_timeout(struct net_device *netdev) > +{ > + struct virtnic_info *vnicinfo =3D netdev_priv(netdev); > + unsigned long flags; > + > + LOGWRNNAME(vnicinfo->netdev, > + "Transmit Timeout. Resetting virtual connection.\n"); > + LOGWRNNAME(vnicinfo->netdev, "net_device name<<%s>>", netdev->name)= ; > + > + spin_lock_irqsave(&vnicinfo->priv_lock, flags); > + /* Ensure that a ServerDown message hasn't been received */ > + if (!vnicinfo->enabled || > + (vnicinfo->server_down && !vnicinfo->server_change_state)) { > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + return; > + } > + spin_unlock_irqrestore(&vnicinfo->priv_lock, flags); > + > + queue_work(virtnic_timeout_reset_workqueue, &vnicinfo->timeout_rese= t); > +} > + > +static void > +virtnic_set_multi(struct net_device *netdev) > +{ > + struct uiscmdrsp *cmdrsp; > + struct virtnic_info *vnicinfo =3D netdev_priv(netdev); > + > + DBGINF("net_device name<<%s>>", netdev->name); > + DBGINF("entering virtnic_set_multi\n"); > + > + /* any filtering changes? */ > + if (vnicinfo->old_flags !=3D netdev->flags) { > + LOGINFNAME(vnicinfo->netdev, > + "old filter =3D 0x%04x, new filter =3D 0x%04x.\n", > + vnicinfo->old_flags, netdev->flags); > + if ((netdev->flags & IFF_PROMISC) !=3D > + (vnicinfo->old_flags & IFF_PROMISC)) { > + LOGINFNAME(vnicinfo->netdev, > + "we are %s promiscuous mode.\n", > + (netdev-> > + flags & IFF_PROMISC) ? "entering" : > + "exiting"); > + cmdrsp =3D kmalloc(SIZEOF_CMDRSP, GFP_ATOMIC); > + if (cmdrsp =3D=3D NULL) { > + LOGERRNAME(vnicinfo->netdev, > + "**** FAILED to kmalloc cmdrsp.\n"); > + return; > + } > + memset(cmdrsp, 0, SIZEOF_CMDRSP); > + cmdrsp->cmdtype =3D CMD_NET_TYPE; > + cmdrsp->net.type =3D NET_RCV_PROMISC; > + cmdrsp->net.enbdis.context =3D netdev; > + cmdrsp->net.enbdis.enable =3D > + (netdev->flags & IFF_PROMISC); > + if (uisqueue_put_cmdrsp_with_lock_client > + (vnicinfo->datachan.chinfo.queueinfo, cmdrsp, > + IOCHAN_TO_IOPART, > + (void *)&vnicinfo->datachan.chinfo.insertlock, > + DONT_ISSUE_INTERRUPT, (uint64_t)NULL, > + 0 /* don't wait */ , "vnic")) { > + vnicinfo->datachan.chstat.sent_promisc++; > + } else > + LOGERRNAME(vnicinfo->netdev, > + "**** FAILED to insert NET_RCV_PROMISC.\n"); > + kfree(cmdrsp); > + } > + > + vnicinfo->old_flags =3D netdev->flags; > + } > + DBGINF("exiting virtnic_set_multi\n"); > +} > + > +/*****************************************************/ > +/* debugfs filesystem functions */ > +/*****************************************************/ > + > +static ssize_t info_debugfs_read(struct file *file, > + char __user *buf, size_t len, loff_t *offset) > +{ > + int i; > + ssize_t bytes_read =3D 0; > + int str_pos =3D 0; > + struct virtnic_info *vni; > + char *vbuf; > + > + if (len > MAX_BUF) > + len =3D MAX_BUF; > + vbuf =3D kzalloc(len, GFP_KERNEL); > + if (!vbuf) > + return -ENOMEM; > + > + /* for each vnic channel > + * dump out channel specific data > + */ > + for (i =3D 0; i < VIRTNICSOPENMAX; i++) { > + if (num_virtnic_open[i].netdev =3D=3D NULL) > + continue; > + > + vni =3D num_virtnic_open[i].vnicinfo; > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, "Vnic i =3D %d\n", i); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, "netdev =3D %s (0x%p), MAC Addr: %02x:%02x:%02x:%= 02x:%02x:%02x\n", > + num_virtnic_open[i].netdev->name, > + num_virtnic_open[i].netdev, > + num_virtnic_open[i].netdev->dev_addr[0], > + num_virtnic_open[i].netdev->dev_addr[1], > + num_virtnic_open[i].netdev->dev_addr[2], > + num_virtnic_open[i].netdev->dev_addr[3], > + num_virtnic_open[i].netdev->dev_addr[4], > + num_virtnic_open[i].netdev->dev_addr[5]); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, "vnicinfo =3D 0x%p\n", vni); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " num_rcv_bufs =3D %d\n", > + vni->num_rcv_bufs); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " features =3D 0x%016llX\n", > + (uint64_t)readq(&vni->datachan.chinfo.queueinfo->chan-> > + features)); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " max_outstanding_net_xmits =3D %d\n", > + vni->max_outstanding_net_xmits); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " upper_threshold_net_xmits =3D %d\n", > + vni->upper_threshold_net_xmits); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " lower_threshold_net_xmits =3D %d\n", > + vni->lower_threshold_net_xmits); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " queuefullmsg_logged =3D %d\n", > + vni->queuefullmsg_logged); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " queueinfo->packets_sent =3D %lld\n", > + vni->datachan.chinfo.queueinfo->packets_sent); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " queueinfo->packets_received =3D %lld\n", > + vni->datachan.chinfo.queueinfo->packets_received); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " chstat.got_rcv =3D %lu\n", > + vni->datachan.chstat.got_rcv); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " chstat.got_enbdisack =3D %lu\n", > + vni->datachan.chstat.got_enbdisack); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " chstat.got_xmit_done =3D %lu\n", > + vni->datachan.chstat.got_xmit_done); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " chstat.xmit_fail =3D %lu\n", > + vni->datachan.chstat.xmit_fail); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " chstat.sent_enbdis =3D %lu\n", > + vni->datachan.chstat.sent_enbdis); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " chstat.sent_promisc =3D %lu\n", > + vni->datachan.chstat.sent_promisc); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " chstat.sent_post =3D %lu\n", > + vni->datachan.chstat.sent_post); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " chstat.sent_xmit =3D %lu\n", > + vni->datachan.chstat.sent_xmit); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " chstat.reject_count =3D %lu\n", > + vni->datachan.chstat.reject_count); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " chstat.extra_rcvbufs_sent =3D %lu\n", > + vni->datachan.chstat.extra_rcvbufs_sent); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " n_rcv0 =3D %lu\n", vni->n_rcv0); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " n_rcv1 =3D %lu\n", vni->n_rcv1); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " n_rcv2 =3D %lu\n", vni->n_rcv2); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " n_rcvx =3D %lu\n", vni->n_rcvx); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " num_rcv_bufs_in_iovm =3D %d\n", > + atomic_read(&vni->num_rcv_bufs_in_iovm)); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " alloc_failed_in_if_needed_cnt =3D %lu\n", > + vni->alloc_failed_in_if_needed_cnt); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " alloc_failed_in_repost_return_cnt =3D %lu\n", > + vni->alloc_failed_in_repost_return_cnt); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " inner_loop_limit_reached_cnt =3D %lu\n", > + vni->inner_loop_limit_reached_cnt); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " found_repost_rcvbuf_cnt =3D %lu\n", > + vni->found_repost_rcvbuf_cnt); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " repost_found_skb_cnt =3D %lu\n", > + vni->repost_found_skb_cnt); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " n_repost_deficit =3D %lu\n", > + vni->n_repost_deficit); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " bad_rcv_buf =3D %lu\n", > + vni->bad_rcv_buf); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " n_rcv_packet_not_accepted =3D %lu\n", > + vni->n_rcv_packet_not_accepted); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " interrupts_rcvd =3D %llu\n", > + vni->interrupts_rcvd); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " interrupts_notme =3D %llu\n", > + vni->interrupts_notme); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " interrupts_disabled =3D %llu\n", > + vni->interrupts_disabled); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " busy_cnt =3D %llu\n", > + vni->busy_cnt); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " flow_control_upper_hits =3D %llu\n", > + vni->flow_control_upper_hits); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " flow_control_lower_hits =3D %llu\n", > + vni->flow_control_lower_hits); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " thread_wait_ms =3D %d\n", > + vni->thread_wait_ms); > + str_pos +=3D scnprintf(vbuf + str_pos, > + len - str_pos, " netif_queue =3D %s\n", > + netif_queue_stopped(vni->netdev) ? > + "stopped" : "running"); > + } > + bytes_read =3D simple_read_from_buffer(buf, len, offset, vbuf, str_= pos); > + kfree(vbuf); > + return bytes_read; > +} > + > +static ssize_t enable_ints_write(struct file *file, > + const char __user *buffer, > + size_t count, loff_t *ppos) > +{ > + char buf[4]; > + int i, new_value; > + struct virtnic_info *vnicinfo; > + uint64_t __iomem *features_addr; > + uint64_t mask; > + > + if (count >=3D ARRAY_SIZE(buf)) > + return -EINVAL; > + > + buf[count] =3D '\0'; > + if (copy_from_user(buf, buffer, count)) { > + LOGERR("copy_from_user failed.\n"); > + return -EFAULT; > + } > + > + i =3D kstrtoint(buf, 10 , &new_value); > + > + if (i !=3D 0) { > + LOGERR("Failed to scan value for enable_ints, buf<<%.*s>>", > + (int)count, buf); > + return -EFAULT; > + } > + > + /* set all counts to new_value usually 0 */ > + for (i =3D 0; i < VIRTNICSOPENMAX; i++) { > + if (num_virtnic_open[i].vnicinfo !=3D NULL) { > + vnicinfo =3D num_virtnic_open[i].vnicinfo; > + features_addr =3D > + &vnicinfo->datachan.chinfo.queueinfo->chan-> > + features; > + if (new_value =3D=3D 1) { > + mask =3D > + ~(ULTRA_IO_CHANNEL_IS_POLLING | > + ULTRA_IO_DRIVER_DISABLES_INTS); > + uisqueue_interlocked_and(features_addr, mask); > + mask =3D ULTRA_IO_DRIVER_ENABLES_INTS; > + uisqueue_interlocked_or(features_addr, mask); > + vnicinfo->thread_wait_ms =3D 2000; > + } else { > + mask =3D > + ~(ULTRA_IO_DRIVER_ENABLES_INTS | > + ULTRA_IO_DRIVER_DISABLES_INTS); > + uisqueue_interlocked_and(features_addr, mask); > + mask =3D ULTRA_IO_CHANNEL_IS_POLLING; > + uisqueue_interlocked_or(features_addr, mask); > + vnicinfo->thread_wait_ms =3D 2; > + } > + } > +} > + > +return count; > +} > + > +/*****************************************************/ > +/* Module init & exit functions */ > +/*****************************************************/ > + > +static int __init > +virtnic_mod_init(void) > +{ > + int error, i; > + > + LOGINF("entering virtnic_mod_init"); > + /* ASSERT RCVPOST_BUF_SIZE < 4K */ > + if (RCVPOST_BUF_SIZE > PI_PAGE_SIZE) { > + LOGERR("**** FAILED RCVPOST_BUF_SIZE:%d larger than a page\n", > + RCVPOST_BUF_SIZE); > + return -1; > + } > + /* ASSERT RCVPOST_BUF_SIZE is big enough to hold eth header */ > + if (RCVPOST_BUF_SIZE < ETH_HEADER_SIZE) { > + LOGERR("**** FAILED RCVPOST_BUF_SIZE:%d is < ETH_HEADER_SIZE:%d\n"= , > + RCVPOST_BUF_SIZE, ETH_HEADER_SIZE); > + return -1; > + } > + > + /* clear out array */ > + for (i =3D 0; i < VIRTNICSOPENMAX; i++) { > + num_virtnic_open[i].netdev =3D NULL; > + num_virtnic_open[i].vnicinfo =3D NULL; > + } > + /* create workqueue for serverdown completion */ > + virtnic_serverdown_workqueue =3D > + create_singlethread_workqueue("virtnic_serverdown"); > + if (virtnic_serverdown_workqueue =3D=3D NULL) { > + LOGERR("**** FAILED virtnic_serverdown_workqueue creation\n"); > + return -1; > + } > + /* create workqueue for tx timeout reset */ > + virtnic_timeout_reset_workqueue =3D > + create_singlethread_workqueue("virtnic_timeout_reset"); > + if (virtnic_timeout_reset_workqueue =3D=3D NULL) { > + LOGERR > + ("**** FAILED virtnic_timeout_reset_workqueue creation\n"); > + return -1; > + } > + virtnic_debugfs_dir =3D debugfs_create_dir("virtnic", NULL); > + debugfs_create_file("info", S_IRUSR, virtnic_debugfs_dir, > + NULL, &debugfs_info_fops); > + debugfs_create_file("enable_ints", S_IWUSR, > + virtnic_debugfs_dir, NULL, > + &debugfs_enable_ints_fops); > + > + error =3D virtpci_register_driver(&virtnic_driver); > + if (error < 0) { > + LOGERR("**** FAILED to register driver %x\n", error); > + debugfs_remove_recursive(virtnic_debugfs_dir); > + return -1; > + } > + LOGINF("exiting virtnic_mod_init"); > + return error; > +} > + > +static void __exit > +virtnic_mod_exit(void) > +{ > + LOGINF("entering virtnic_mod_exit...\n"); > + virtpci_unregister_driver(&virtnic_driver); > + /* unregister is going to call virtnic_remove for all devices */ > + /* destroy serverdown completion workqueue */ > + if (virtnic_serverdown_workqueue) { > + destroy_workqueue(virtnic_serverdown_workqueue); > + virtnic_serverdown_workqueue =3D NULL; > + } > + > + /* destroy timeout reset workqueue */ > + if (virtnic_timeout_reset_workqueue) { > + destroy_workqueue(virtnic_timeout_reset_workqueue); > + virtnic_timeout_reset_workqueue =3D NULL; > + } > + > + debugfs_remove_recursive(virtnic_debugfs_dir); > + LOGINF("exiting virtnic_mod_exit...\n"); > +} > + > +module_init(virtnic_mod_init); > +module_exit(virtnic_mod_exit); > +MODULE_LICENSE("GPL"); > +MODULE_AUTHOR("Usha Srinivasan"); > +MODULE_ALIAS("uisvirtnic"); > +/* this is extracted during depmod and kept in modules.dep */