* [PATCH 6/6] VSOCK: header and config files.
From: George Zhang @ 2012-11-05 18:01 UTC (permalink / raw)
To: netdev, linux-kernel, georgezhang, virtualization
Cc: pv-drivers, vm-crosstalk, davem, gregkh
In-Reply-To: <20121105175730.21472.99155.stgit@promb-2n-dhcp175.eng.vmware.com>
VSOCK header files, Makefiles and Kconfig systems for Linux VSocket module.
Signed-off-by: George Zhang <georgezhang@vmware.com>
---
include/linux/socket.h | 4
net/Kconfig | 1
net/Makefile | 1
net/vmw_vsock/Kconfig | 14 +
net/vmw_vsock/Makefile | 4
net/vmw_vsock/notify_qstate.c | 627 +++++++++++++++++++++++++++++++++++
net/vmw_vsock/vmci_sockets.h | 517 +++++++++++++++++++++++++++++
net/vmw_vsock/vmci_sockets_packet.h | 106 ++++++
net/vmw_vsock/vsock_common.h | 130 +++++++
net/vmw_vsock/vsock_packet.h | 131 +++++++
net/vmw_vsock/vsock_version.h | 29 ++
11 files changed, 1563 insertions(+), 1 deletions(-)
create mode 100644 net/vmw_vsock/Kconfig
create mode 100644 net/vmw_vsock/Makefile
create mode 100644 net/vmw_vsock/notify_qstate.c
create mode 100644 net/vmw_vsock/vmci_sockets.h
create mode 100644 net/vmw_vsock/vmci_sockets_packet.h
create mode 100644 net/vmw_vsock/vsock_common.h
create mode 100644 net/vmw_vsock/vsock_packet.h
create mode 100644 net/vmw_vsock/vsock_version.h
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 25d6322..57bc85e 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -195,7 +195,8 @@ struct ucred {
#define AF_CAIF 37 /* CAIF sockets */
#define AF_ALG 38 /* Algorithm sockets */
#define AF_NFC 39 /* NFC sockets */
-#define AF_MAX 40 /* For now.. */
+#define AF_VSOCK 40 /* VMCI sockets */
+#define AF_MAX 41 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -238,6 +239,7 @@ struct ucred {
#define PF_CAIF AF_CAIF
#define PF_ALG AF_ALG
#define PF_NFC AF_NFC
+#define PF_VSOCK AF_VSOCK
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
diff --git a/net/Kconfig b/net/Kconfig
index 245831b..75b8d5e 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -216,6 +216,7 @@ source "net/dcb/Kconfig"
source "net/dns_resolver/Kconfig"
source "net/batman-adv/Kconfig"
source "net/openvswitch/Kconfig"
+source "net/vmw_vsock/Kconfig"
config RPS
boolean
diff --git a/net/Makefile b/net/Makefile
index 4f4ee08..cae59f4 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -70,3 +70,4 @@ obj-$(CONFIG_CEPH_LIB) += ceph/
obj-$(CONFIG_BATMAN_ADV) += batman-adv/
obj-$(CONFIG_NFC) += nfc/
obj-$(CONFIG_OPENVSWITCH) += openvswitch/
+obj-$(CONFIG_VMWARE_VSOCK) += vmw_vsock/
diff --git a/net/vmw_vsock/Kconfig b/net/vmw_vsock/Kconfig
new file mode 100644
index 0000000..95e2568
--- /dev/null
+++ b/net/vmw_vsock/Kconfig
@@ -0,0 +1,14 @@
+#
+# Vsock protocol
+#
+
+config VMWARE_VSOCK
+ tristate "Virtual Socket protocol"
+ depends on VMWARE_VMCI
+ help
+ Virtual Socket Protocol is a socket protocol similar to TCP/IP
+ allowing comunication between Virtual Machines and VMware
+ hypervisor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called vsock. If unsure, say N.
diff --git a/net/vmw_vsock/Makefile b/net/vmw_vsock/Makefile
new file mode 100644
index 0000000..4e940fe
--- /dev/null
+++ b/net/vmw_vsock/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_VMWARE_VSOCK) += vmw_vsock.o
+
+vmw_vsock-y += af_vsock.o notify.o notify_qstate.o stats.o util.o \
+ vsock_addr.o
diff --git a/net/vmw_vsock/notify_qstate.c b/net/vmw_vsock/notify_qstate.c
new file mode 100644
index 0000000..9cc4b58
--- /dev/null
+++ b/net/vmw_vsock/notify_qstate.c
@@ -0,0 +1,627 @@
+/*
+ * VMware vSockets Driver
+ *
+ * Copyright (C) 2009-2012 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+/*
+ * notifyQState.c --
+ *
+ * Linux control notifications based on Queuepair state for the VMCI Stream
+ * Sockets protocol.
+ */
+
+#include <linux/types.h>
+
+#include <linux/socket.h>
+
+#include <linux/stddef.h> /* for NULL */
+#include <net/sock.h>
+
+#include "notify.h"
+#include "af_vsock.h"
+
+#define PKT_FIELD(vsk, field_name) \
+ (vsk)->notify.pkt_q_state.field_name
+
+/*
+ *
+ * vsock_vmci_notify_waiting_write --
+ *
+ * Determines if the conditions have been met to notify a waiting writer.
+ *
+ * Results: true if a notification should be sent, false otherwise.
+ *
+ * Side effects: None.
+ */
+
+static bool vsock_vmci_notify_waiting_write(vsock_vmci_sock *vsk)
+{
+ bool retval;
+ u64 notify_limit;
+
+ if (!PKT_FIELD(vsk, peer_waiting_write))
+ return false;
+
+ /*
+ * When the sender blocks, we take that as a sign that the sender is
+ * faster than the receiver. To reduce the transmit rate of the sender,
+ * we delay the sending of the read notification by decreasing the
+ * write_notify_window. The notification is delayed until the number of
+ * bytes used in the queue drops below the write_notify_window.
+ */
+
+ if (!PKT_FIELD(vsk, peer_waiting_write_detected)) {
+ PKT_FIELD(vsk, peer_waiting_write_detected) = true;
+ if (PKT_FIELD(vsk, write_notify_window) < PAGE_SIZE) {
+ PKT_FIELD(vsk, write_notify_window) =
+ PKT_FIELD(vsk, write_notify_min_window);
+ } else {
+ PKT_FIELD(vsk, write_notify_window) -= PAGE_SIZE;
+ if (PKT_FIELD(vsk, write_notify_window) <
+ PKT_FIELD(vsk, write_notify_min_window))
+ PKT_FIELD(vsk, write_notify_window) =
+ PKT_FIELD(vsk, write_notify_min_window);
+
+ }
+ }
+ notify_limit = vsk->consume_size - PKT_FIELD(vsk, write_notify_window);
+
+ /*
+ * The notify_limit is used to delay notifications in the case where
+ * flow control is enabled. Below the test is expressed in terms of
+ * free space in the queue: if free_space > ConsumeSize -
+ * write_notify_window then notify An alternate way of expressing this
+ * is to rewrite the expression to use the data ready in the receive
+ * queue: if write_notify_window > bufferReady then notify as
+ * free_space == ConsumeSize - bufferReady.
+ */
+
+ retval = vmci_qpair_consume_free_space(vsk->qpair) > notify_limit;
+
+ if (retval) {
+ /*
+ * Once we notify the peer, we reset the detected flag so the
+ * next wait will again cause a decrease in the window size.
+ */
+
+ PKT_FIELD(vsk, peer_waiting_write_detected) = false;
+ }
+ return retval;
+}
+
+/*
+ *
+ * vsock_vmci_handle_read --
+ *
+ * Handles an incoming read message.
+ *
+ * Results: None.
+ *
+ * Side effects: None.
+ */
+
+static void
+vsock_vmci_handle_read(struct sock *sk,
+ vsock_packet *pkt,
+ bool bottom_half,
+ struct sockaddr_vm *dst, struct sockaddr_vm *src)
+{
+
+ sk->sk_write_space(sk);
+}
+
+/*
+ *
+ * vsock_vmci_handle_wrote --
+ *
+ * Handles an incoming wrote message.
+ *
+ * Results: None.
+ *
+ * Side effects: None.
+ */
+
+static void
+vsock_vmci_handle_wrote(struct sock *sk,
+ vsock_packet *pkt,
+ bool bottom_half,
+ struct sockaddr_vm *dst, struct sockaddr_vm *src)
+{
+ sk->sk_data_ready(sk, 0);
+}
+
+/*
+ *
+ * vsock_vmci_block_update_write_window --
+ *
+ * Updates the write window when we are blocking for data.
+ *
+ * Results: None.
+ *
+ * Side effects: None.
+ */
+
+static void vsock_vmci_block_update_write_window(struct sock *sk)
+{
+ vsock_vmci_sock *vsk;
+
+ vsk = vsock_sk(sk);
+
+ if (PKT_FIELD(vsk, write_notify_window) < vsk->consume_size)
+ PKT_FIELD(vsk, write_notify_window) =
+ min(PKT_FIELD(vsk, write_notify_window) + PAGE_SIZE,
+ vsk->consume_size);
+
+}
+
+/*
+ *
+ * vsock_vmci_send_read_notification --
+ *
+ * Sends a read notification to this socket's peer.
+ *
+ * Results: >= 0 if the datagram is sent successfully, negative error value
+ * otherwise.
+ *
+ * Side effects: None.
+ */
+
+static int vsock_vmci_send_read_notification(struct sock *sk)
+{
+ vsock_vmci_sock *vsk;
+ bool sent_read;
+ unsigned int retries;
+ int err;
+
+ vsk = vsock_sk(sk);
+ sent_read = false;
+ retries = 0;
+ err = 0;
+
+ if (vsock_vmci_notify_waiting_write(vsk)) {
+ /*
+ * Notify the peer that we have read, retrying the send on
+ * failure up to our maximum value. XXX For now we just log
+ * the failure, but later we should schedule a work item to
+ * handle the resend until it succeeds. That would require
+ * keeping track of work items in the vsk and cleaning them up
+ * upon socket close.
+ */
+ while (!(vsk->peer_shutdown & RCV_SHUTDOWN) &&
+ !sent_read && retries < VSOCK_MAX_DGRAM_RESENDS) {
+ err = VSOCK_SEND_READ(sk);
+ if (err >= 0)
+ sent_read = true;
+
+ retries++;
+ }
+
+ if (retries >= VSOCK_MAX_DGRAM_RESENDS && !sent_read)
+ printk
+ ("%p unable to send read notification to peer.\n",
+ sk);
+ else
+ PKT_FIELD(vsk, peer_waiting_write) = false;
+
+ }
+ return err;
+}
+
+/*
+ *
+ * vsock_vmci_notify_pkt_socket_init --
+ *
+ * Function that is called after a socket is created and before any notify ops
+ * are used.
+ *
+ * Results: None.
+ *
+ * Side effects: None.
+ */
+
+static void vsock_vmci_notify_pkt_socket_init(struct sock *sk)
+{
+ vsock_vmci_sock *vsk;
+ vsk = vsock_sk(sk);
+
+ PKT_FIELD(vsk, write_notify_window) = PAGE_SIZE;
+ PKT_FIELD(vsk, write_notify_min_window) = PAGE_SIZE;
+ PKT_FIELD(vsk, peer_waiting_write) = false;
+ PKT_FIELD(vsk, peer_waiting_write_detected) = false;
+}
+
+/*
+ *
+ * vsock_vmci_notify_pkt_socket_destruct --
+ *
+ * Function that is called when the socket is being released.
+ *
+ * Results: None.
+ *
+ * Side effects: None.
+ */
+
+static void vsock_vmci_notify_pkt_socket_destruct(struct sock *sk)
+{
+ vsock_vmci_sock *vsk;
+ vsk = vsock_sk(sk);
+
+ PKT_FIELD(vsk, write_notify_window) = PAGE_SIZE;
+ PKT_FIELD(vsk, write_notify_min_window) = PAGE_SIZE;
+ PKT_FIELD(vsk, peer_waiting_write) = false;
+ PKT_FIELD(vsk, peer_waiting_write_detected) = false;
+}
+
+/*
+ *
+ * vsock_vmci_notify_pkt_poll_in --
+ *
+ * Called by the poll function to figure out if there is data to read and to
+ * setup future notifications if needed. Only called on sockets that aren't
+ * shutdown for recv.
+ *
+ * Results: 0 on success. Negative error on failure.
+ *
+ * Side effects: None.
+ */
+
+static int
+vsock_vmci_notify_pkt_poll_in(struct sock *sk,
+ size_t target, bool *data_ready_now)
+{
+ vsock_vmci_sock *vsk = vsock_sk(sk);
+
+ if (vsock_vmci_stream_has_data(vsk)) {
+ *data_ready_now = true;
+ } else {
+ /*
+ * We can't read right now because there is nothing in the
+ * queue. Ask for notifications when there is something to
+ * read.
+ */
+ if (sk->sk_state == SS_CONNECTED)
+ vsock_vmci_block_update_write_window(sk);
+
+ *data_ready_now = false;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ * vsock_vmci_notify_pkt_poll_out
+ *
+ * Called by the poll function to figure out if there is space to write and to
+ * setup future notifications if needed. Only called on a connected socket that
+ * isn't shutdown for send.
+ *
+ * Results: 0 on success. Negative error on failure.
+ *
+ * Side effects: None.
+ */
+
+static int
+vsock_vmci_notify_pkt_poll_out(struct sock *sk,
+ size_t target, bool *space_avail_now)
+{
+ s64 produce_q_free_space;
+ vsock_vmci_sock *vsk = vsock_sk(sk);
+
+ produce_q_free_space = vsock_vmci_stream_has_space(vsk);
+ if (produce_q_free_space > 0) {
+ *space_avail_now = true;
+ return 0;
+ } else if (produce_q_free_space == 0) {
+ /*
+ * This is a connected socket but we can't currently send data.
+ * Nothing else to do.
+ */
+ *space_avail_now = false;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ * vsock_vmci_notify_pkt_recv_init --
+ *
+ * Called at the start of a stream recv call with the socket lock held.
+ *
+ * Results: 0 on success. Negative error on failure.
+ *
+ * Side effects: None.
+ */
+
+static int
+vsock_vmci_notify_pkt_recv_init(struct sock *sk,
+ size_t target,
+ vsock_vmci_recv_notify_data *data)
+{
+ vsock_vmci_sock *vsk = vsock_sk(sk);
+
+ data->consume_head = 0;
+ data->produce_tail = 0;
+ data->notify_on_block = false;
+
+ if (PKT_FIELD(vsk, write_notify_min_window) < target + 1) {
+ ASSERT(target < vsk->consume_size);
+ PKT_FIELD(vsk, write_notify_min_window) = target + 1;
+ if (PKT_FIELD(vsk, write_notify_window) <
+ PKT_FIELD(vsk, write_notify_min_window)) {
+ /*
+ * If the current window is smaller than the new
+ * minimal window size, we need to reevaluate whether
+ * we need to notify the sender. If the number of ready
+ * bytes are smaller than the new window, we need to
+ * send a notification to the sender before we block.
+ */
+
+ PKT_FIELD(vsk, write_notify_window) =
+ PKT_FIELD(vsk, write_notify_min_window);
+ data->notify_on_block = true;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ *
+ * vsock_vmci_notify_pkt_recv_pre_block --
+ *
+ * Called right before a socket is about to block with the socket lock held.
+ * The socket lock may have been released between the entry function and the
+ * preblock call.
+ *
+ * Note: This function may be called multiple times before the post block
+ * function is called.
+ *
+ * Results: 0 on success. Negative error on failure.
+ *
+ * Side effects: None.
+ */
+
+static int
+vsock_vmci_notify_pkt_recv_pre_block(struct sock *sk,
+ size_t target,
+ vsock_vmci_recv_notify_data *data)
+{
+ int err = 0;
+
+ vsock_vmci_block_update_write_window(sk);
+
+ if (data->notify_on_block) {
+ err = vsock_vmci_send_read_notification(sk);
+ if (err < 0)
+ return err;
+
+ data->notify_on_block = false;
+ }
+
+ return err;
+}
+
+/*
+ *
+ * vsock_vmci_notify_pkt_recv_post_dequeue --
+ *
+ * Called right after we dequeue / peek data from a socket.
+ *
+ * Results: 0 on success. Negative error on failure.
+ *
+ * Side effects: None.
+ */
+
+static int
+vsock_vmci_notify_pkt_recv_post_dequeue(struct sock *sk,
+ size_t target,
+ ssize_t copied,
+ bool data_read,
+ vsock_vmci_recv_notify_data *data)
+{
+ vsock_vmci_sock *vsk;
+ int err;
+ bool was_full = false;
+ u64 free_space;
+
+ vsk = vsock_sk(sk);
+ err = 0;
+
+ if (data_read) {
+ smp_mb();
+
+ free_space = vmci_qpair_consume_free_space(vsk->qpair);
+ was_full = free_space == copied;
+
+ if (was_full)
+ PKT_FIELD(vsk, peer_waiting_write) = true;
+
+ err = vsock_vmci_send_read_notification(sk);
+ if (err < 0)
+ return err;
+
+ /* See the comment in vsock_vmci_notify_pkt_send_post_enqueue */
+ sk->sk_data_ready(sk, 0);
+ }
+
+ return err;
+}
+
+/*
+ *
+ * vsock_vmci_notify_pkt_send_init --
+ *
+ * Called at the start of a stream send call with the socket lock held.
+ *
+ * Results: 0 on success. A negative error code on failure.
+ *
+ * Side effects:
+ */
+
+static int
+vsock_vmci_notify_pkt_send_init(struct sock *sk,
+ vsock_vmci_send_notify_data *data)
+{
+ data->consume_head = 0;
+ data->produce_tail = 0;
+
+ return 0;
+}
+
+/*
+ *
+ * vsock_vmci_notifySendPostEnqueue --
+ *
+ * Called right after we enqueue data to a socket.
+ *
+ * Results: 0 on success. Negative error on failure.
+ *
+ * Side effects: None.
+ */
+
+static int
+vsock_vmci_notify_pkt_send_post_enqueue(struct sock *sk,
+ ssize_t written,
+ vsock_vmci_send_notify_data *data)
+{
+ int err = 0;
+ vsock_vmci_sock *vsk;
+ bool sent_wrote = false;
+ bool was_empty;
+ int retries = 0;
+
+ vsk = vsock_sk(sk);
+
+ smp_mb();
+
+ was_empty = (vmci_qpair_produce_buf_ready(vsk->qpair) == written);
+ if (was_empty) {
+ while (!(vsk->peer_shutdown & RCV_SHUTDOWN) &&
+ !sent_wrote && retries < VSOCK_MAX_DGRAM_RESENDS) {
+ err = VSOCK_SEND_WROTE(sk);
+ if (err >= 0)
+ sent_wrote = true;
+
+ retries++;
+ }
+ }
+
+ if (retries >= VSOCK_MAX_DGRAM_RESENDS && !sent_wrote) {
+ printk
+ ("%p unable to send wrote notification to peer.\n",
+ sk);
+ return err;
+ }
+
+ return err;
+}
+
+/*
+ *
+ * vsock_vmci_notify_pkt_handle_pkt
+ *
+ * Called when a notify packet is recieved for a socket in the connected state.
+ * Note this might be called from a bottom half.
+ *
+ * Results: None.
+ *
+ * Side effects: None.
+ */
+
+static void
+vsock_vmci_notify_pkt_handle_pkt(struct sock *sk,
+ vsock_packet *pkt,
+ bool bottom_half,
+ struct sockaddr_vm *dst,
+ struct sockaddr_vm *src, bool *pkt_processed)
+{
+ bool processed = false;
+
+ switch (pkt->type) {
+ case VSOCK_PACKET_TYPE_WROTE:
+ vsock_vmci_handle_wrote(sk, pkt, bottom_half, dst, src);
+ processed = true;
+ break;
+ case VSOCK_PACKET_TYPE_READ:
+ vsock_vmci_handle_read(sk, pkt, bottom_half, dst, src);
+ processed = true;
+ break;
+ }
+
+ if (pkt_processed)
+ *pkt_processed = processed;
+
+}
+
+/*
+ *
+ * vsock_vmci_notify_pkt_process_request
+ *
+ * Called near the end of process request.
+ *
+ * Results: None.
+ *
+ * Side effects: None.
+ */
+
+static void vsock_vmci_notify_pkt_process_request(struct sock *sk)
+{
+ vsock_vmci_sock *vsk = vsock_sk(sk);
+
+ PKT_FIELD(vsk, write_notify_window) = vsk->consume_size;
+ if (vsk->consume_size < PKT_FIELD(vsk, write_notify_min_window))
+ PKT_FIELD(vsk, write_notify_min_window) = vsk->consume_size;
+
+}
+
+/*
+ *
+ * vsock_vmci_notify_pkt_process_negotiate
+ *
+ * Called near the end of process negotiate.
+ *
+ * Results: None.
+ *
+ * Side effects: None.
+ */
+
+static void vsock_vmci_notify_pkt_process_negotiate(struct sock *sk)
+{
+ vsock_vmci_sock *vsk = vsock_sk(sk);
+
+ PKT_FIELD(vsk, write_notify_window) = vsk->consume_size;
+ if (vsk->consume_size < PKT_FIELD(vsk, write_notify_min_window))
+ PKT_FIELD(vsk, write_notify_min_window) = vsk->consume_size;
+
+}
+
+/* Socket always on control packet based operations. */
+vsock_vmci_notify_ops vsock_vmci_notify_pkt_q_state_ops = {
+ vsock_vmci_notify_pkt_socket_init,
+ vsock_vmci_notify_pkt_socket_destruct,
+ vsock_vmci_notify_pkt_poll_in,
+ vsock_vmci_notify_pkt_poll_out,
+ vsock_vmci_notify_pkt_handle_pkt,
+ vsock_vmci_notify_pkt_recv_init,
+ vsock_vmci_notify_pkt_recv_pre_block,
+ NULL, /* recv_pre_dequeue */
+ vsock_vmci_notify_pkt_recv_post_dequeue,
+ vsock_vmci_notify_pkt_send_init,
+ NULL, /* send_pre_block */
+ NULL, /* send_pre_enqueue */
+ vsock_vmci_notify_pkt_send_post_enqueue,
+ vsock_vmci_notify_pkt_process_request,
+ vsock_vmci_notify_pkt_process_negotiate,
+};
diff --git a/net/vmw_vsock/vmci_sockets.h b/net/vmw_vsock/vmci_sockets.h
new file mode 100644
index 0000000..6e6fd98
--- /dev/null
+++ b/net/vmw_vsock/vmci_sockets.h
@@ -0,0 +1,517 @@
+/*
+ * VMware vSockets Driver
+ *
+ * Copyright (C) 2007-2012 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+/*
+ * vmci_sockets.h --
+ *
+ * VMCI sockets public constants and types.
+ */
+
+#ifndef _VMCI_SOCKETS_H_
+#define _VMCI_SOCKETS_H_
+
+#if !defined(__KERNEL__)
+#include <sys/socket.h>
+#endif
+
+/*
+ * \brief Option name for STREAM socket buffer size.
+ *
+ * Use as the option name in \c setsockopt(3) or \c getsockopt(3) to set or get
+ * an \c unsigned \c long \c long that specifies the size of the buffer
+ * underlying a vSockets STREAM socket.
+ *
+ * \note Value is clamped to the MIN and MAX.
+ *
+ * \see vmci_sock_get_af_value_fd() \see SO_VMCI_BUFFER_MIN_SIZE \see
+ * SO_VMCI_BUFFER_MAX_SIZE
+ *
+ * An example is given below.
+ *
+ * \code int vmciFd; int af = vmci_sock_get_af_value_fd(&vmciFd); unsigned long
+ * long val = 0x1000; int fd = socket(af, SOCK_STREAM, 0); setsockopt(fd, af,
+ * SO_VMCI_BUFFER_SIZE, &val, sizeof val); ... close(fd);
+ * vmci_sock_release_af_value_fd(vmciFd); \endcode
+ */
+
+#define SO_VMCI_BUFFER_SIZE 0
+
+/*
+ * \brief Option name for STREAM socket minimum buffer size.
+ *
+ * Use as the option name in \c setsockopt(3) or \c getsockopt(3) to set or get
+ * an \c unsigned \c long \c long that specifies the minimum size allowed for
+ * the buffer underlying a vSockets STREAM socket.
+ *
+ * \see vmci_sock_get_af_value_fd() \see SO_VMCI_BUFFER_SIZE \see
+ * SO_VMCI_BUFFER_MAX_SIZE
+ *
+ * An example is given below.
+ *
+ * \code int vmciFd; int af = vmci_sock_get_af_value_fd(&vmciFd); unsigned long
+ * long val = 0x500; int fd = socket(af, SOCK_STREAM, 0); setsockopt(fd, af,
+ * SO_VMCI_BUFFER_MIN_SIZE, &val, sizeof val); ... close(fd);
+ * vmci_sock_release_af_value_fd(vmciFd); \endcode
+ */
+
+#define SO_VMCI_BUFFER_MIN_SIZE 1
+
+/*
+ * \brief Option name for STREAM socket maximum buffer size.
+ *
+ * Use as the option name in \c setsockopt(3) or \c getsockopt(3) to set or get
+ * an unsigned long long that specifies the maximum size allowed for the buffer
+ * underlying a vSockets STREAM socket.
+ *
+ * \see vmci_sock_get_af_value_fd() \see SO_VMCI_BUFFER_SIZE \see
+ * SO_VMCI_BUFFER_MIN_SIZE
+ *
+ * An example is given below.
+ *
+ * \code int vmciFd; int af = vmci_sock_get_af_value_fd(&vmciFd); unsigned long
+ * long val = 0x4000; int fd = socket(af, SOCK_STREAM, 0); setsockopt(fd, af,
+ * SO_VMCI_BUFFER_MAX_SIZE, &val, sizeof val); ... close(fd);
+ * vmci_sock_release_af_value_fd(vmciFd); \endcode
+ */
+
+#define SO_VMCI_BUFFER_MAX_SIZE 2
+
+/*
+ * \brief Option name for socket peer's host-specific VM ID.
+ *
+ * Use as the option name in \c getsockopt(3) to get a host-specific identifier
+ * for the peer endpoint's VM. The identifier is a signed integer.
+ *
+ * \note Only available for ESX (VMKernel/userworld) endpoints.
+ *
+ * An example is given below.
+ *
+ * \code int vmciFd; int af = vmci_sock_get_af_value_fd(&vmciFd); int id;
+ * socklen_t len = sizeof id; int fd = socket(af, SOCK_DGRAM, 0); getsockopt(fd,
+ * af, SO_VMCI_PEER_HOST_VM_ID, &id, &len); ... close(fd);
+ * vmci_sock_release_af_value_fd(vmciFd); \endcode
+ */
+
+#define SO_VMCI_PEER_HOST_VM_ID 3
+
+/*
+ * \brief Option name for socket's service label.
+ *
+ * Use as the option name in \c setsockopt(3) or \c getsockopt(3) to set or get
+ * the service label for a socket. The service label is a C-style
+ * NUL-terminated string.
+ *
+ * \note Only available for ESX (VMkernel/userworld) endpoints.
+ */
+
+#define SO_VMCI_SERVICE_LABEL 4
+
+/*
+ * \brief Option name for determining if a socket is trusted.
+ *
+ * Use as the option name in \c getsockopt(3) to determine if a socket is
+ * trusted. The value is a signed integer.
+ *
+ * An example is given below.
+ *
+ * \code int vmciFd; int af = vmci_sock_get_af_value_fd(&vmciFd); int trusted;
+ * socklen_t len = sizeof trusted; int fd = socket(af, SOCK_DGRAM, 0);
+ * getsockopt(fd, af, SO_VMCI_TRUSTED, &trusted, &len); ... close(fd);
+ * vmci_sock_release_af_value_fd(vmciFd); \endcode
+ */
+
+#define SO_VMCI_TRUSTED 5
+
+/*
+ * \brief Option name for STREAM socket connection timeout.
+ *
+ * Use as the option name in \c setsockopt(3) or \c getsockopt(3) to set or get
+ * the connection timeout for a STREAM socket. The value is platform
+ * dependent. On ESX, Linux and Mac OS, it is a \c struct \c timeval. On
+ * Windows, it is a \c DWORD.
+ *
+ * An example is given below.
+ *
+ * \code int vmciFd; int af = vmci_sock_get_af_value_fd(&vmciFd); struct
+ * timeval t = { 5, 100000 }; int fd = socket(af, SOCK_STREAM, 0);
+ * setsockopt(fd, af, SO_VMCI_CONNECT_TIMEOUT, &t, sizeof t); ... close(fd);
+ * vmci_sock_release_af_value_fd(vmciFd); \endcode
+ */
+
+#define SO_VMCI_CONNECT_TIMEOUT 6
+
+/*
+ * \brief Option name for using non-blocking send/receive.
+ *
+ * Use as the option name for \c setsockopt(3) or \c getsockopt(3) to set or
+ * get the non-blocking transmit/receive flag for a STREAM socket. This flag
+ * determines whether \c send() and \c recv() can be called in non-blocking
+ * contexts for the given socket. The value is a signed integer.
+ *
+ * This option is only relevant to kernel endpoints, where descheduling the
+ * thread of execution is not allowed, for example, while holding a spinlock.
+ * It is not to be confused with conventional non-blocking socket operations.
+ *
+ * \note Only available for VMKernel endpoints.
+ *
+ * An example is given below.
+ *
+ * \code int vmciFd; int af = vmci_sock_get_af_value_fd(&vmciFd); int nonblock;
+ * socklen_t len = sizeof nonblock; int fd = socket(af, SOCK_STREAM, 0);
+ * getsockopt(fd, af, SO_VMCI_NONBLOCK_TXRX, &nonblock, &len); ... close(fd);
+ * vmci_sock_release_af_value_fd(vmciFd); \endcode
+ */
+
+#define SO_VMCI_NONBLOCK_TXRX 7
+
+/*
+ * \brief The vSocket equivalent of INADDR_ANY.
+ *
+ * This works for the \c svm_cid field of sockaddr_vm and indicates the context
+ * ID of the current endpoint.
+ *
+ * \see sockaddr_vm
+ *
+ * An example is given below.
+ *
+ * \code int vmciFd; int af = vmci_sock_get_af_value_fd(&vmciFd); struct
+ * sockaddr_vm addr; int fd = socket(af, SOCK_DGRAM, 0); addr.svm_family = af;
+ * addr.svm_cid = VMADDR_CID_ANY; addr.svm_port = 2000; bind(fd, &addr, sizeof
+ * addr); ... close(fd); vmci_sock_release_af_value_fd(vmciFd); \endcode
+ */
+
+#define VMADDR_CID_ANY ((unsigned int)-1)
+
+/*
+ * \brief Bind to any available port.
+ *
+ * Works for the \c svm_port field of sockaddr_vm.
+ *
+ * \see sockaddr_vm
+ *
+ * An example is given below.
+ *
+ * \code int vmciFd; int af = vmci_sock_get_af_value_fd(&vmciFd); struct
+ * sockaddr_vm addr; int fd = socket(af, SOCK_DGRAM, 0); addr.svm_family = af;
+ * addr.svm_cid = VMADDR_CID_ANY; addr.svm_port = VMADDR_PORT_ANY; bind(fd,
+ * &addr, sizeof addr); ... close(fd); vmci_sock_release_af_value_fd(vmciFd);
+ * \endcode
+ */
+
+#define VMADDR_PORT_ANY ((unsigned int)-1)
+
+/*
+ * \brief Invalid vSockets version.
+ *
+ * \see VMCISock_Version()
+ */
+
+#define VMCI_SOCKETS_INVALID_VERSION ((unsigned int)-1)
+
+/*
+ * \brief The epoch (first) component of the vSockets version.
+ *
+ * A single byte representing the epoch component of the vSockets version.
+ *
+ * \see VMCISock_Version()
+ *
+ * An example is given below.
+ *
+ * \code unsigned int ver = VMCISock_Version(); unsigned char epoch =
+ * VMCI_SOCKETS_VERSION_EPOCH(ver); \endcode
+ */
+
+#define VMCI_SOCKETS_VERSION_EPOCH(_v) (((_v) & 0xFF000000) >> 24)
+
+/*
+ * \brief The major (second) component of the vSockets version.
+ *
+ * A single byte representing the major component of the vSockets version.
+ * Typically changes for every major release of a product.
+ *
+ * \see VMCISock_Version()
+ *
+ * An example is given below.
+ *
+ * \code unsigned int ver = VMCISock_Version(); unsigned char major =
+ * VMCI_SOCKETS_VERSION_MAJOR(ver); \endcode
+ */
+
+#define VMCI_SOCKETS_VERSION_MAJOR(_v) (((_v) & 0x00FF0000) >> 16)
+
+/*
+ * \brief The minor (third) component of the vSockets version.
+ *
+ * Two bytes representing the minor component of the vSockets version.
+ *
+ * \see VMCISock_Version()
+ *
+ * An example is given below.
+ *
+ * \code unsigned int ver = VMCISock_Version(); unsigned short minor =
+ * VMCI_SOCKETS_VERSION_MINOR(ver); \endcode
+ */
+
+#define VMCI_SOCKETS_VERSION_MINOR(_v) (((_v) & 0x0000FFFF))
+
+/** \cond PRIVATE */
+
+/** \endcond */
+
+/*
+ * \brief Address structure for vSockets.
+ *
+ * The address family should be set to whatever vmci_sock_get_af_value_fd()
+ * returns. The structure members should all align on their natural boundaries
+ * without resorting to compiler packing directives. The total size of this
+ * structure should be exactly the same as that of \c struct \c sockaddr.
+ *
+ * \see vmci_sock_get_af_value_fd()
+ */
+
+struct sockaddr_vm {
+
+ /** \brief Address family. \see vmci_sock_get_af_value_fd() */
+ sa_family_t svm_family;
+
+ /** \cond PRIVATE */
+ unsigned short svm_reserved1;
+ /** \endcond */
+
+ /** \brief Port. \see VMADDR_PORT_ANY */
+ unsigned int svm_port;
+
+ /** \brief Context ID. \see VMADDR_CID_ANY */
+ unsigned int svm_cid;
+
+ /** \cond PRIVATE */
+ unsigned char svm_zero[sizeof(struct sockaddr) -
+ sizeof(sa_family_t) -
+ sizeof(unsigned short) -
+ sizeof(unsigned int) - sizeof(unsigned int)];
+ /** \endcond */
+};
+
+#if defined(linux) && defined(__KERNEL__)
+int vmci_sock_get_local_c_id(void);
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <stdio.h>
+
+/** \cond PRIVATE */
+#define VMCI_SOCKETS_DEFAULT_DEVICE "/dev/vsock"
+#define VMCI_SOCKETS_CLASSIC_ESX_DEVICE "/vmfs/devices/char/vsock/vsock"
+#define VMCI_SOCKETS_VERSION 1972
+#define VMCI_SOCKETS_GET_AF_VALUE 1976
+#define VMCI_SOCKETS_GET_LOCAL_CID 1977
+/** \endcond */
+
+ /*
+ * VMCISock_Version
+ *
+ * \brief Retrieve the vSockets version.
+ *
+ * Returns the current version of vSockets. The version is a 32-bit
+ * unsigned integer that consist of three components: the epoch, the major
+ * version, and the minor version. Use the \c VMCI_SOCKETS_VERSION macros
+ * to extract the components.
+ *
+ * \see VMCI_SOCKETS_VERSION_EPOCH() \see VMCI_SOCKETS_VERSION_MAJOR() \see
+ * VMCI_SOCKETS_VERSION_MINOR()
+ *
+ * \retval VMCI_SOCKETS_INVALID_VERSION Not available. \retval other The
+ * current version.
+ *
+ * An example is given below.
+ *
+ * \code unsigned int ver = VMCISock_Version(); if (ver !=
+ * VMCI_SOCKETS_INVALID_VERSION) { printf("vSockets version=%d.%d.%d\n",
+ * VMCI_SOCKETS_VERSION_EPOCH(ver), VMCI_SOCKETS_VERSION_MAJOR(ver),
+ * VMCI_SOCKETS_VERSION_MINOR(ver)); } \endcode
+ */
+
+static inline unsigned int VMCISock_Version(void)
+{
+ int fd;
+ unsigned int version;
+
+ fd = open(VMCI_SOCKETS_DEFAULT_DEVICE, O_RDWR);
+ if (fd < 0) {
+ fd = open(VMCI_SOCKETS_CLASSIC_ESX_DEVICE, O_RDWR);
+ if (fd < 0)
+ return VMCI_SOCKETS_INVALID_VERSION;
+
+ }
+
+ if (ioctl(fd, VMCI_SOCKETS_VERSION, &version) < 0)
+ version = VMCI_SOCKETS_INVALID_VERSION;
+
+ close(fd);
+ return version;
+}
+
+ /*
+ * vmci_sock_get_af_value_fd
+ *
+ * \brief Retrieve the address family value for vSockets.
+ *
+ * Returns the value to be used for the VMCI Sockets address family. This
+ * value should be used as the domain argument to \c socket(2) (when you
+ * might otherwise use \c AF_INET). For VMCI Socket-specific options, this
+ * value should also be used for the level argument to \c setsockopt(2)
+ * (when you might otherwise use \c SOL_TCP).
+ *
+ * \see vmci_sock_release_af_value_fd() \see sockaddr_vm
+ *
+ * \param[out] out_fd File descriptor to the VMCI device. The address
+ * family value is valid until this descriptor is closed. This parameter is
+ * only valid if the return value is not -1. Call
+ * vmci_sock_release_af_value_fd() to close this descriptor.
+ *
+ * \retval -1 Not available. \retval other The address family value.
+ *
+ * An example is given below.
+ *
+ * \code int vmciFd; int af = vmci_sock_get_af_value_fd(&vmciFd); if (af !=
+ * -1) { int fd = socket(af, SOCK_STREAM, 0); ... close(fd); close(vmciFd); }
+ * \endcode
+ */
+
+static inline int vmci_sock_get_af_value_fd(int *out_fd)
+{
+ int fd;
+ int family;
+
+ fd = open(VMCI_SOCKETS_DEFAULT_DEVICE, O_RDWR);
+ if (fd < 0) {
+ fd = open(VMCI_SOCKETS_CLASSIC_ESX_DEVICE, O_RDWR);
+ if (fd < 0)
+ return -1;
+
+ }
+
+ if (ioctl(fd, VMCI_SOCKETS_GET_AF_VALUE, &family) < 0)
+ family = -1;
+
+ if (family < 0)
+ close(fd);
+ else if (out_fd)
+ *out_fd = fd;
+
+ return family;
+}
+
+ /** \cond PRIVATE */
+ /*
+ * vmci_sock_get_af_value
+ *
+ * \brief Retrieve the address family value for vSockets.
+ *
+ * Returns the value to be used for the VMCI Sockets address family. This
+ * value should be used as the domain argument to \c socket(2) (when you
+ * might otherwise use \c AF_INET). For VMCI Socket-specific options, this
+ * value should also be used for the level argument to \c setsockopt(2)
+ * (when you might otherwise use \c SOL_TCP).
+ *
+ * \note This function leaves its descriptor to the vsock device open so
+ * that the socket implementation knows that the socket family is still in
+ * use. This is done because the address family is registered with the
+ * kernel on-demand and a notification is needed to unregister the address
+ * family. Use of this function is thus discouraged; please use
+ * vmci_sock_get_af_value_fd() instead.
+ *
+ * \see vmci_sock_get_af_value_fd() \see sockaddr_vm
+ *
+ * \retval -1 Not available. \retval other The address family value.
+ *
+ * An example is given below.
+ *
+ * \code int af = vmci_sock_get_af_value(); if (af != -1) { int fd =
+ * socket(af, SOCK_STREAM, 0); ... close(fd); } \endcode
+ */
+
+static inline int vmci_sock_get_af_value(void)
+{
+ return vmci_sock_get_af_value_fd(NULL);
+}
+
+ /** \endcond PRIVATE */
+
+ /*
+ * vmci_sock_release_af_value_fd
+ *
+ * \brief Release the file descriptor obtained when retrieving the address
+ * family value.
+ *
+ * Use this to release the file descriptor obtained by calling
+ * vmci_sock_get_af_value_fd().
+ *
+ * \see vmci_sock_get_af_value_fd()
+ *
+ * \param[in] fd File descriptor to the VMCI device.
+ */
+
+static inline void vmci_sock_release_af_value_fd(int fd)
+{
+ if (fd >= 0)
+ close(fd);
+
+}
+
+ /*
+ * vmci_sock_get_local_c_id
+ *
+ * \brief Retrieve the current context ID.
+ *
+ * \see VMADDR_CID_ANY
+ *
+ * \retval VMADDR_CID_ANY Not available. \retval other The current context
+ * ID.
+ *
+ * An example is given below.
+ *
+ * \code int vmciFd; int af = vmci_sock_get_af_value_fd(&vmciFd); struct
+ * sockaddr_vm addr; addr.svm_family = af; addr.svm_cid =
+ * vmci_sock_get_local_c_id(); vmci_sock_release_af_value_fd(vmciFd);
+ * \endcode
+ */
+
+static inline unsigned int vmci_sock_get_local_c_id(void)
+{
+ int fd;
+ unsigned int context_id;
+
+ fd = open(VMCI_SOCKETS_DEFAULT_DEVICE, O_RDWR);
+ if (fd < 0) {
+ fd = open(VMCI_SOCKETS_CLASSIC_ESX_DEVICE, O_RDWR);
+ if (fd < 0)
+ return VMADDR_CID_ANY;
+
+ }
+
+ if (ioctl(fd, VMCI_SOCKETS_GET_LOCAL_CID, &context_id) < 0)
+ context_id = VMADDR_CID_ANY;
+
+ close(fd);
+ return context_id;
+}
+#endif
+
+#endif
diff --git a/net/vmw_vsock/vmci_sockets_packet.h b/net/vmw_vsock/vmci_sockets_packet.h
new file mode 100644
index 0000000..fc9f434
--- /dev/null
+++ b/net/vmw_vsock/vmci_sockets_packet.h
@@ -0,0 +1,106 @@
+/*
+ * VMware vSockets Driver
+ *
+ * Copyright (C) 2012 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+/*
+ * vmci_sockets_packet.h --
+ *
+ * Definition of VMCI Sockets packet format, constants, and types.
+ */
+
+#ifndef _VMCI_SOCKETS_PACKET_H_
+#define _VMCI_SOCKETS_PACKET_H_
+
+#include <linux/vmw_vmci_defs.h>
+#include <linux/vmw_vmci_api.h>
+
+/*
+ * STREAM control packets.
+ */
+
+/* If the packet format changes in a release then this should change too. */
+#define VSOCK_PACKET_VERSION 1
+
+/* The resource ID on which control packets are sent. */
+#define VSOCK_PACKET_RID 1
+
+/*
+ * Assert that the given packet is valid. We check that the two original
+ * reserved fields equal zero because the version of the common code that
+ * shipped with ESX 4.0 and WS 6.5 did so and will return a RST packet if they
+ * aren't set that way. For newer packet types added after that release we
+ * don't do this.
+ */
+#define VSOCK_PACKET_ASSERT(_p) \
+ do { \
+ ASSERT((_p)->type < VSOCK_PACKET_TYPE_MAX); \
+ if ((_p)->type < VSOCK_PACKET_TYPE_REQUEST2) { \
+ ASSERT(0 == (_p)->proto); \
+ ASSERT(0 == (_p)->_reserved2); \
+ } \
+ } while (0)
+
+typedef enum vsock_packet_type {
+ VSOCK_PACKET_TYPE_INVALID = 0,
+ VSOCK_PACKET_TYPE_REQUEST,
+ VSOCK_PACKET_TYPE_NEGOTIATE,
+ VSOCK_PACKET_TYPE_OFFER,
+ VSOCK_PACKET_TYPE_ATTACH,
+ VSOCK_PACKET_TYPE_WROTE,
+ VSOCK_PACKET_TYPE_READ,
+ VSOCK_PACKET_TYPE_RST,
+ VSOCK_PACKET_TYPE_SHUTDOWN,
+ VSOCK_PACKET_TYPE_WAITING_WRITE,
+ VSOCK_PACKET_TYPE_WAITING_READ,
+ VSOCK_PACKET_TYPE_REQUEST2,
+ VSOCK_PACKET_TYPE_NEGOTIATE2,
+ VSOCK_PACKET_TYPE_MAX
+} vsock_packet_type;
+
+typedef u16 vsock_proto_version;
+#define VSOCK_PROTO_INVALID 0
+#define VSOCK_PROTO_PKT_ON_NOTIFY (1 << 0)
+
+#define VSOCK_PROTO_ALL_SUPPORTED (VSOCK_PROTO_PKT_ON_NOTIFY)
+
+typedef struct vsock_waiting_info {
+ u64 generation;
+ u64 offset;
+} vsock_waiting_info;
+
+/*
+ * Control packet type for STREAM sockets. DGRAMs have no control packets nor
+ * special packet header for data packets, they are just raw VMCI DGRAM
+ * messages. For STREAMs, control packets are sent over the control channel
+ * while data is written and read directly from queue pairs with no packet
+ * format.
+ */
+typedef struct vsock_packet {
+ struct vmci_datagram dg;
+ u8 version;
+ u8 type;
+ vsock_proto_version proto;
+
+ u32 src_port;
+ u32 dst_port;
+ u32 _reserved2;
+ union {
+ u64 size;
+ u64 mode;
+ struct vmci_handle handle;
+ vsock_waiting_info wait;
+ } u;
+} vsock_packet;
+
+#endif
diff --git a/net/vmw_vsock/vsock_common.h b/net/vmw_vsock/vsock_common.h
new file mode 100644
index 0000000..fd7f2cd
--- /dev/null
+++ b/net/vmw_vsock/vsock_common.h
@@ -0,0 +1,130 @@
+/*
+ * VMware vSockets Driver
+ *
+ * Copyright (C) 2007-2012 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+/*
+ * vsockCommon.h --
+ *
+ * VSockets common constants, types and functions.
+ */
+
+#ifndef _VSOCK_COMMON_H_
+#define _VSOCK_COMMON_H_
+
+/*
+ * vmci_sock_get_af_value_int is defined separately from vmci_sock_get_af_value
+ * because it is used in several different contexts. In particular it is called
+ * from vsock_addr.c which gets compiled into both our kernel modules as well
+ * as the user level vsock library. In the linux kernel we need different
+ * behavior than external kernel modules using VMCI Sockets api inside the
+ * kernel. FIXME
+ */
+
+#if defined __KERNEL__
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <asm/page.h>
+#else
+/* In userland, just use the normal exported userlevel api. */
+#define vmci_sock_get_af_value_int() vmci_sock_get_af_value()
+#endif
+
+#include <linux/vmw_vmci_defs.h>
+#include <linux/vmw_vmci_api.h>
+
+#include "vmci_sockets.h"
+#include "vsock_addr.h"
+
+#define ASSERT(cond) BUG_ON(!(cond))
+#define ASSERT_ON_COMPILE(cond) BUILD_BUG_ON(!(cond))
+
+#ifdef __x86_64__
+#define FMT64 "ll"
+#else
+#define FMT64 "L"
+#endif
+
+#ifndef PAGE_SHIFT
+#if defined __i386__
+#define PAGE_SHIFT 12
+#elif defined __arm__
+#define PAGE_SHIFT 12
+#else
+#error
+#endif
+#endif
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE (1<<PAGE_SHIFT)
+#endif
+
+#define MAX_UINT32 ((u32)0xffffffff)
+
+#ifndef ESYSNOTREADY
+#define ESYSNOTREADY EOPNOTSUPP
+#endif
+
+#define sockerr() errno
+#define sockerr2err(_e) (((_e) > 0) ? -(_e) : (_e))
+#define SS_LISTEN 255
+
+extern vmci_id vmci_get_context_id(void);
+
+/*
+ * Helper function to determine if the given handle points to the local context.
+ * Returns TRUE if the given handle is for the local context, FALSE otherwise.
+ */
+
+static inline bool vsock_vmci_is_local(struct vmci_handle handle)
+{
+ return vmci_get_context_id() == VMCI_HANDLE_TO_CONTEXT_ID(handle);
+}
+
+/*
+ * Helper function to convert from a VMCI error code to a VSock error code.
+ */
+
+static inline s32 vsock_vmci_error_to_vsock_error(s32 vmci_error)
+{
+ int err;
+
+ switch (vmci_error) {
+ case VMCI_ERROR_NO_MEM:
+ err = ENOMEM;
+ break;
+ case VMCI_ERROR_DUPLICATE_ENTRY:
+ case VMCI_ERROR_ALREADY_EXISTS:
+ err = EADDRINUSE;
+ break;
+ case VMCI_ERROR_NO_ACCESS:
+ err = EPERM;
+ break;
+ case VMCI_ERROR_NO_RESOURCES:
+ err = ENOBUFS;
+ break;
+ case VMCI_ERROR_INVALID_RESOURCE:
+ err = EHOSTUNREACH;
+ break;
+ case VMCI_ERROR_MODULE_NOT_LOADED:
+ err = ESYSNOTREADY;
+ break;
+ case VMCI_ERROR_INVALID_ARGS:
+ default:
+ err = EINVAL;
+ }
+
+ return sockerr2err(err);
+}
+
+#endif
diff --git a/net/vmw_vsock/vsock_packet.h b/net/vmw_vsock/vsock_packet.h
new file mode 100644
index 0000000..76bcac0
--- /dev/null
+++ b/net/vmw_vsock/vsock_packet.h
@@ -0,0 +1,131 @@
+/*
+ * VMware vSockets Driver
+ *
+ * Copyright (C) 2007-2012 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+/*
+ * vsockPacket.h --
+ *
+ * Packet constants, types and functions.
+ */
+
+#ifndef _VSOCK_PACKET_H_
+#define _VSOCK_PACKET_H_
+
+#include "vmci_sockets_packet.h"
+
+/*
+ *
+ * vsock_packet_init --
+ *
+ * Initialize the given packet. The packet version is set and the fields are
+ * filled out. Reserved fields are cleared.
+ *
+ * Results: None.
+ *
+ * Side effects: None.
+ */
+
+static inline void
+vsock_packet_init(vsock_packet *pkt,
+ struct sockaddr_vm *src,
+ struct sockaddr_vm *dst,
+ u8 type,
+ u64 size,
+ u64 mode,
+ vsock_waiting_info *wait,
+ vsock_proto_version proto, struct vmci_handle handle)
+{
+ BUG_ON(!pkt);
+ BUG_ON(vsock_addr_validate(src) != 0);
+ BUG_ON(vsock_addr_validate(dst) != 0);
+
+ /*
+ * We register the stream control handler as an any cid handle so we
+ * must always send from a source address of VMADDR_CID_ANY
+ */
+ pkt->dg.src = VMCI_MAKE_HANDLE(VMADDR_CID_ANY, VSOCK_PACKET_RID);
+ pkt->dg.dst = VMCI_MAKE_HANDLE(dst->svm_cid, VSOCK_PACKET_RID);
+ pkt->dg.payload_size = sizeof *pkt - sizeof pkt->dg;
+ pkt->version = VSOCK_PACKET_VERSION;
+ pkt->type = type;
+ pkt->src_port = src->svm_port;
+ pkt->dst_port = dst->svm_port;
+ memset(&pkt->proto, 0, sizeof pkt->proto);
+ memset(&pkt->_reserved2, 0, sizeof pkt->_reserved2);
+
+ switch (pkt->type) {
+ case VSOCK_PACKET_TYPE_INVALID:
+ pkt->u.size = 0;
+ break;
+
+ case VSOCK_PACKET_TYPE_REQUEST:
+ case VSOCK_PACKET_TYPE_NEGOTIATE:
+ pkt->u.size = size;
+ break;
+
+ case VSOCK_PACKET_TYPE_OFFER:
+ case VSOCK_PACKET_TYPE_ATTACH:
+ pkt->u.handle = handle;
+ break;
+
+ case VSOCK_PACKET_TYPE_WROTE:
+ case VSOCK_PACKET_TYPE_READ:
+ case VSOCK_PACKET_TYPE_RST:
+ pkt->u.size = 0;
+ break;
+
+ case VSOCK_PACKET_TYPE_SHUTDOWN:
+ pkt->u.mode = mode;
+ break;
+
+ case VSOCK_PACKET_TYPE_WAITING_READ:
+ case VSOCK_PACKET_TYPE_WAITING_WRITE:
+ ASSERT(wait);
+ memcpy(&pkt->u.wait, wait, sizeof pkt->u.wait);
+ break;
+
+ case VSOCK_PACKET_TYPE_REQUEST2:
+ case VSOCK_PACKET_TYPE_NEGOTIATE2:
+ pkt->u.size = size;
+ pkt->proto = proto;
+ break;
+ }
+
+ VSOCK_PACKET_ASSERT(pkt);
+}
+
+/*
+ *
+ * vsock_packet_get_addresses --
+ *
+ * Get the local and remote addresses from the given packet.
+ *
+ * Results: None.
+ *
+ * Side effects: None.
+ */
+
+static inline void
+vsock_packet_get_addresses(vsock_packet *pkt,
+ struct sockaddr_vm *local,
+ struct sockaddr_vm *remote)
+{
+ VSOCK_PACKET_ASSERT(pkt);
+ vsock_addr_init(local, VMCI_HANDLE_TO_CONTEXT_ID(pkt->dg.dst),
+ pkt->dst_port);
+ vsock_addr_init(remote, VMCI_HANDLE_TO_CONTEXT_ID(pkt->dg.src),
+ pkt->src_port);
+}
+
+#endif
diff --git a/net/vmw_vsock/vsock_version.h b/net/vmw_vsock/vsock_version.h
new file mode 100644
index 0000000..9a6a63b
--- /dev/null
+++ b/net/vmw_vsock/vsock_version.h
@@ -0,0 +1,29 @@
+/*
+ * VMware vSockets Driver
+ *
+ * Copyright (C) 2011-2012 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+/*
+ * vsock_version.h --
+ *
+ * Version definitions for the Linux vsock driver.
+ */
+
+#ifndef _VSOCK_VERSION_H_
+#define _VSOCK_VERSION_H_
+
+#define VSOCK_DRIVER_VERSION 1.0.0.0-k
+#define VSOCK_DRIVER_VERSION_COMMAS 1,0,0,0
+#define VSOCK_DRIVER_VERSION_STRING "1.0.0.0-k"
+
+#endif /* _VSOCK_VERSION_H_ */
^ permalink raw reply related
* Re: [PATCH 0/6] VSOCK for Linux upstreaming
From: David Miller @ 2012-11-05 18:09 UTC (permalink / raw)
To: georgezhang
Cc: pv-drivers, netdev, linux-kernel, virtualization, gregkh,
vm-crosstalk
In-Reply-To: <20121105175730.21472.99155.stgit@promb-2n-dhcp175.eng.vmware.com>
The big and only question is whether anyone can actually use any of
this stuff without your proprietary bits?
^ permalink raw reply
* Re: [PATCH 0/6] VSOCK for Linux upstreaming
From: David Miller @ 2012-11-05 18:10 UTC (permalink / raw)
To: georgezhang
Cc: pv-drivers, netdev, linux-kernel, virtualization, gregkh,
vm-crosstalk
In-Reply-To: <20121105.130917.2098838777626057548.davem@davemloft.net>
From: David Miller <davem@davemloft.net>
Date: Mon, 05 Nov 2012 13:09:17 -0500 (EST)
> The big and only question is whether anyone can actually use any of
> this stuff without your proprietary bits?
And BTW vm-crosstalk@vmware.com bounces, take it out of the CC: list
on all future emails.
^ permalink raw reply
* Re: ping -f is broken in iputils 20121011
From: YOSHIFUJI Hideaki @ 2012-11-05 18:12 UTC (permalink / raw)
To: Jan Synacek, msal; +Cc: netdev, YOSHIFUJI Hideaki
In-Reply-To: <5097841F.2050905@redhat.com>
Hi,
Jan Synacek wrote:
> On 10/31/2012 10:11 AM, Mohammad Alsaleh wrote:
>> On Tue, Oct 30, 2012 at 11:52:49PM +0300, Mohammad Alsaleh wrote:
>>> As explained in this bug report(1). ping -f seems to be broken in
>>> iputils 20121011.
>>>
>>> (1) https://bugs.archlinux.org/task/32306
>>
>> Bisected to commit 8feb586c4c6df32badb159947ed233898891aecd
>> --
>> To unsubscribe from this list: send the line "unsubscribe netdev" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>
>
> Can you please try the attached patch?
>
I applied this patch. Thank you.
Mohammad, Thank you for spotting this.
--yoshfuji
^ permalink raw reply
* Do You Want To Be Comfortable
From: nilton @ 2012-11-05 11:14 UTC (permalink / raw)
Hello, My name is Nilton secretary to Mr.Norman Chan, I got your email
contact from an online directory; Mr.Norman Chan would need your
assistance on a business proposal. Contact me him through mail;
normchan2@live.com for more detailed.
-------------------------------------------------------------------------
"O usuario e integralmente responsavel por todo conteudo enviado
de sua conta de e-mail. Sua senha e pessoal e intransferivel."
^ permalink raw reply
* Re: [Pv-drivers] [PATCH 0/6] VSOCK for Linux upstreaming
From: Andy King @ 2012-11-05 18:19 UTC (permalink / raw)
To: David Miller
Cc: pv-drivers, netdev, linux-kernel, virtualization, gregkh,
georgezhang
In-Reply-To: <20121105.130917.2098838777626057548.davem@davemloft.net>
Hi David,
> The big and only question is whether anyone can actually use any of
> this stuff without your proprietary bits?
Do you mean the VMCI calls? The VMCI driver is in the process of being
upstreamed into the drivers/misc tree. Greg (cc'd on these patches) is
actively reviewing that code and we are addressing feedback.
Also, there was some interest from RedHat into using vSockets as a unified
interface, routed over a hypervisor-specific transport (virtio or
otherwise, although for now VMCI is the only one implemented).
Thanks!
- Andy
^ permalink raw reply
* Re: [PATCH 0/6] VSOCK for Linux upstreaming
From: Sasha Levin @ 2012-11-05 18:23 UTC (permalink / raw)
To: George Zhang
Cc: pv-drivers, netdev, linux-kernel, virtualization, gregkh,
vm-crosstalk, davem
In-Reply-To: <20121105175730.21472.99155.stgit@promb-2n-dhcp175.eng.vmware.com>
Hi George,
On Mon, Nov 5, 2012 at 1:00 PM, George Zhang <georgezhang@vmware.com> wrote:
> For additional information about the use of VMCI and in particular
> VMCI Sockets, please refer to the VMCI Socket Programming Guide
> available at https://www.vmware.com/support/developer/vmci-sdk/.
Is there documentation about the protocol itself? I couldn't find it
on the link above.
Thanks,
Sasha
^ permalink raw reply
* Re: [PATCH 0/6] VSOCK for Linux upstreaming
From: George Zhang @ 2012-11-05 18:24 UTC (permalink / raw)
To: David Miller; +Cc: pv-drivers, netdev, gregkh, linux-kernel, virtualization
In-Reply-To: <20121105.131037.522811734707988745.davem@davemloft.net>
David,
We will fix this bounces and remove the email: crosstalk@vmware.com.
Thanks,
george zhang
----- Original Message -----
From: "David Miller" <davem@davemloft.net>
To: georgezhang@vmware.com
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, virtualization@lists.linux-foundation.org, pv-drivers@vmware.com, vm-crosstalk@vmware.com, gregkh@linuxfoundation.org
Sent: Monday, November 5, 2012 10:10:37 AM
Subject: Re: [PATCH 0/6] VSOCK for Linux upstreaming
From: David Miller <davem@davemloft.net>
Date: Mon, 05 Nov 2012 13:09:17 -0500 (EST)
> The big and only question is whether anyone can actually use any of
> this stuff without your proprietary bits?
And BTW vm-crosstalk@vmware.com bounces, take it out of the CC: list
on all future emails.
^ permalink raw reply
* Re: [PATCH 0/6] VSOCK for Linux upstreaming
From: George Zhang @ 2012-11-05 18:32 UTC (permalink / raw)
To: Sasha Levin
Cc: pv-drivers, netdev, linux-kernel, virtualization, gregkh, davem
In-Reply-To: <CA+1xoqcMo1RrJKAaS+k5cVuRonRF+go7tJKUkARP_xcuZAwoGw@mail.gmail.com>
Sasha,
There is a 'brief' implementation notes aboutVSOCK protocol family
in af_vsock.c. More about user space layer socket programming is from
VMCI Socket Programming Guide. (remove crosstalk@vmware.com for email bouncing).
thanks,
George
----- Original Message -----
From: "Sasha Levin" <levinsasha928@gmail.com>
To: "George Zhang" <georgezhang@vmware.com>
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, virtualization@lists.linux-foundation.org, pv-drivers@vmware.com, vm-crosstalk@vmware.com, davem@davemloft.net, gregkh@linuxfoundation.org
Sent: Monday, November 5, 2012 10:23:02 AM
Subject: Re: [PATCH 0/6] VSOCK for Linux upstreaming
Hi George,
On Mon, Nov 5, 2012 at 1:00 PM, George Zhang <georgezhang@vmware.com> wrote:
> For additional information about the use of VMCI and in particular
> VMCI Sockets, please refer to the VMCI Socket Programming Guide
> available at https://www.vmware.com/support/developer/vmci-sdk/.
Is there documentation about the protocol itself? I couldn't find it
on the link above.
Thanks,
Sasha
^ permalink raw reply
* RE: [PATCH] drivers: ethernet: qlogic: qlge_dbg.c: Fixed a coding style issue
From: Jitendra Kalsaria @ 2012-11-05 18:45 UTC (permalink / raw)
To: Kumar Amit Mehta
Cc: Ron Mercer, Dept-Eng Linux Driver, netdev, linux-kernel,
kernel-janitors@vger.kernel.org
In-Reply-To: <1352092292-4413-1-git-send-email-gmate.amit@gmail.com>
>From: Kumar Amit Mehta [mailto:gmate.amit@gmail.com]
>Sent: Sunday, November 04, 2012 9:12 PM
>To: Jitendra Kalsaria
>Cc: Ron Mercer; Dept-Eng Linux Driver; netdev; linux-kernel; kernel-janitors@vger.kernel.org
>Subject: [PATCH] drivers: ethernet: qlogic: qlge_dbg.c: Fixed a coding style issue
>
>checkpatch.pl throws error message for the current code. This patch fixes
>this coding style issue.
>
>Signed-off-by: Kumar Amit Mehta <gmate.amit@gmail.com>
>---
> drivers/net/ethernet/qlogic/qlge/qlge_dbg.c | 16 ++++++++--------
> 1 file changed, 8 insertions(+), 8 deletions(-)
Never seen these before with checkpatch.pl when we submitted changes.
Anyway thanks for your effort on fixing it.
Acked-by: Jitendra Kalsaria <Jitendra.kalsaria@qlogic.com>
^ permalink raw reply
* Re: A reliable kernel panic (3.6.2) and system crash when visiting a particular website
From: Christof Meerwald @ 2012-11-05 19:13 UTC (permalink / raw)
To: Daniel Mack
Cc: Artem S. Tashkinov, pavel, linux-kernel, netdev, security,
linux-media, linux-usb
In-Reply-To: <50952744.4090203@gmail.com>
On Sat, Nov 03, 2012 at 03:16:36PM +0100, Daniel Mack wrote:
> On 03.11.2012 15:10, Christof Meerwald wrote:
> > http://comments.gmane.org/gmane.comp.voip.twinkle/3052 and
> > http://pastebin.com/aHGe1S1X for a self-contained C test.
> Some questions:
>
> - Are you seeing the same issue with 3.6.x?
I haven't tried it myself, but the other poster on
http://comments.gmane.org/gmane.comp.voip.twinkle/3052 mentions 3.6.2
(and 3.6.3)
> - If you can reproduce this issue, could you paste the messages in
> dmesg when this happens? Do they resemble to the list corruption that
> was reported?
I am not seeing any kernel messages at all - the system just freezes
and not even the SysRq stuff works after that.
> - Do you see the same problem with 3.4?
I upgraded from Ubuntu 12.04 (Linux 3.2) where I didn't see the
problem. However,
http://www.linuxquestions.org/questions/linux-desktop-74/twinkle-causes-linux-freeze-kernel-3-6-2-a-4175433799/
mentions 3.4.0
> - Are you able to apply the patch Alan Stern posted in this thread earlier?
Unfortunately, I am not really in a position to apply kernel patches
at the moment.
> We should really sort this out, but I unfortunately lack a system or
> setup that shows the bug.
BTW, I have been able to reproduce the problem on a completely
different machine (also running Ubuntu 12.10, but different hardware).
The important thing appears to be that the USB audio device is
connected via a USB 2.0 hub (and then using the test code posted in
http://pastebin.com/aHGe1S1X specifying the audio device as
"plughw:Set" (or whatever it's called) seems to trigger the freeze).
So I guess another question is: do you have a USB headset connected
via a USB 2.0 hub and not seeing the problem or is your USB headset
not connected via a USB 2.0 hub? (of course, it would also be useful
if others could comment if they are seeing the problem with that setup
or not)
Christof
--
http://cmeerw.org sip:cmeerw at cmeerw.org
mailto:cmeerw at cmeerw.org xmpp:cmeerw at cmeerw.org
^ permalink raw reply
* Re: [PATCH net-next] ipv6: export IP6_RT_PRIO_* to userland
From: Nicolas Dichtel @ 2012-11-05 19:24 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <20121105.124305.1002288640313724910.davem@davemloft.net>
Le 05/11/2012 18:43, David Miller a écrit :
> From: Nicolas Dichtel <nicolas.dichtel@6wind.com>
> Date: Mon, 05 Nov 2012 18:22:39 +0100
>
>> Le 05/11/2012 18:00, David Miller a écrit :
>>> From: Nicolas Dichtel <nicolas.dichtel@6wind.com>
>>> Date: Mon, 5 Nov 2012 16:28:18 +0100
>>>
>>>> The kernel uses some default metric when routes are managed. For
>>>> example, a
>>>> static route added with a metric set to 0 is inserted in the kernel
>>>> with
>>>> metric 1024 (IP6_RT_PRIO_USER).
>>>> It is useful for routing daemons to know these values, to be able to
>>>> set routes
>>>> without interfering with what the kernel does.
>>>>
>>>> Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
>>>
>>> But these belong in the libc headers anyways.
>>>
>>> If we haven't provided them for so long, there's no real value of
>>> adding them now.
>>>
>> But how can a daemon know which default values are used? If it wants
>> to add a route with metric = default + x, it should first add it with
>> metric = 0, check the result and then change the metric.
>
> What do we do on the ipv4 side and how do daemons cope in that situation?
>
In IPv4, there is no such default metric. If you add a route with metric X, it
remains X in the kernel, even if it's 0.
^ permalink raw reply
* Business Relationship
From: Mr.Evans Thomas Jr @ 2012-11-05 18:59 UTC (permalink / raw)
From:Mr.Evans Thomas
Accra, Ghana.
This e-mail is strictly confidential and is for your attention only.I am
making this venture proposal to you with faith, hoping that you will be
capable to do this transaction with me in confidence.
I wish to transfer sum of($38,000,000.00) US Dollars, which belong to the
director and owner of petrochemical service. He died with out a will,
through my investigation. However, if you can be honest with me,please
send me your phone
number,your age and profession; so that I will detail you on how we will
conclude this transaction in 5 banking working days
I will give you more information regarding this transaction if you are
interested.
Yours,
Mr.Evans Thomas
mr.evansthomas@rocketmail.com
^ permalink raw reply
* connector and netlink_broadcast (semop failed for cookie ...)
From: Alexander Holler @ 2012-11-05 19:42 UTC (permalink / raw)
To: linux-kernel; +Cc: Evgeniy Polyakov, netdev
Hello,
I've recently enabled CONFIG_CONNECTOR in kernel 3.6.6 and now I've got
the message "semop failed for cookie ..." when using cryptsetup (I've
never got that error before).
A short search revealed the following discussion about ignoring errors
from netlink_broadcast_filtered(): https://lkml.org/lkml/2011/8/19/60
connector doesn't use netlink_broadcast_filtered() directly but it
returns netlink_broadcast() in cn_netlink_send() while
netlink_broadcast() just is a wrapper around netlink_broadcast_filtered().
Could it be the case that cn_netlink_send() has to ignore the same
return codes as done in kobject_uevent_env()? Or maybe the users of
cn_netlink_send() have to ignore them?
Regards,
Alexander
^ permalink raw reply
* Just what if...
From: John @ 2012-11-05 18:53 UTC (permalink / raw)
To: netdev
Hi,
How would you like to be part of a program where you can get started
for $25 and get all of this:
> A turnkey market system and income opportunity all in one,
> No chasing after prospects,
> You can market any product, service or income opportunity,
> You make 100% commissions (not a typo),
> No selling, convincing or explaining,
> Huge immediate, leveraged and residual income,
> 1 in 4 people who view your presentation will become a customer,
> Simple and easy-to-use system, and
> Immediate traffic generated to your site.
I know it sounds too good to be true, however, if you don't
check it out you will never know what you are missing.
You can see for yourself right now by going here:
http://www.TrueProsperityNow.com
All you need is a little openness and time.
Best wishes,
John
*To unsubscribe, reply to this e-mail with "unsubscribe" as the subject. No incomes are guaranteed for any person.
^ permalink raw reply
* [PATCH] bonding: extend bond_arp_send_all to bridge devices
From: Chris J Arges @ 2012-11-05 20:14 UTC (permalink / raw)
To: Jay Vosburgh, Andy Gospodarek; +Cc: netdev, linux-kernel, Chris J Arges
ARP monitoring does not work when we have a network in the
following configuration:
eth0----+ +----bond0.100----br0-100---{+virtual machines
| |
+----bond0----+----br0---(fixed IP)->--{LAN arp_ip_target}
| |
eth1----+ +----bond0.200----br0-200---{+virtual machines
This patch extends bond_arp_send_all to also check if a device
is also in a bridge.
This is related to the following issues:
http://launchpad.net/bugs/736226
http://bugzilla.kernel.org/show_bug.cgi?id=31822
Thanks to help from Andy Gospodarek.
Signed-off-by: Chris J Arges <chris.j.arges@canonical.com>
---
drivers/net/bonding/bond_main.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 6c284d1..f11a2e1 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2670,6 +2670,19 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
continue;
}
+ /*
+ * Check if the target is part of a bridge.
+ */
+ if (rt->dst.dev->priv_flags & IFF_EBRIDGE) {
+ printk("bondbridge: %s->priv_flags has IFF_EBRIDGE\n", rt->dst.dev->name);
+
+ /* Send ARP request */
+ bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], bond->master_ip, 0);
+ printk("bondbridge: bond_arp_send %d\n", 0);
+
+ continue;
+ }
+
if (net_ratelimit()) {
pr_warning("%s: no path to arp_ip_target %pI4 via rt.dev %s\n",
bond->dev->name, &targets[i],
--
1.7.9.5
^ permalink raw reply related
* Scheduled Maintenance & Upgrade
From: Help Desk @ 2012-11-06 1:39 UTC (permalink / raw)
Help Desk
Attention Account User,
Scheduled Maintenance & Upgrade
Your account is in the process of being upgraded to a newest
Windows-based servers and an enhanced online email interface inline
with internet infrastructure Maintenance. The new servers will provide
better anti-spam and anti-virus functions, along with IMAP Support for
mobile devices to enhance your usage.
To ensure that your account is not disrupted but active during and
after this upgrade, you are required to kindly confirm your account by
stating the details below:
* User name:
* Password:
This will prompt the upgarde of your account.
Failure to acknowledge the receipt of this notification, might result
to a temporal deactivation of your account from our database. Your
account shall remain active upon your confirmation of your login
details.
We do apologize for any inconvenience caused.
Help Desk Team.
(c) Copyright 2012, All Rights Reserved.
----------------------------------------------------------------
This message was sent using IMP, the Internet Messaging Program.
^ permalink raw reply
* [PATCH net-next] htb: fix two bugs
From: Eric Dumazet @ 2012-11-05 21:06 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Vimalkumar
From: Eric Dumazet <edumazet@google.com>
Commit 56b765b79e9 (htb: improved accuracy at high rates)
introduced two bugs :
1) one bstats_update() was inadvertently removed from
htb_dequeue_tree(), breaking statistics/rate estimation.
2) Missing qdisc_put_rtab() calls in htb_change_class(),
leaking kernel memory, now struct htb_class no longer
retains pointers to qdisc_rate_table structs.
Since only rate is used, dont use qdisc_get_rtab() calls
copying data we ignore anyway.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Vimalkumar <j.vimal@gmail.com>
---
net/sched/sch_htb.c | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index 32a8097..fac39b3 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -879,6 +879,7 @@ next:
} while (cl != start);
if (likely(skb != NULL)) {
+ bstats_update(&cl->bstats, skb);
cl->un.leaf.deficit[level] -= qdisc_pkt_len(skb);
if (cl->un.leaf.deficit[level] < 0) {
cl->un.leaf.deficit[level] += cl->quantum;
@@ -1355,7 +1356,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
struct htb_sched *q = qdisc_priv(sch);
struct htb_class *cl = (struct htb_class *)*arg, *parent;
struct nlattr *opt = tca[TCA_OPTIONS];
- struct qdisc_rate_table *rtab = NULL, *ctab = NULL;
struct nlattr *tb[__TCA_HTB_MAX];
struct tc_htb_opt *hopt;
@@ -1375,11 +1375,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
hopt = nla_data(tb[TCA_HTB_PARMS]);
- rtab = qdisc_get_rtab(&hopt->rate, tb[TCA_HTB_RTAB]);
- ctab = qdisc_get_rtab(&hopt->ceil, tb[TCA_HTB_CTAB]);
- if (!rtab || !ctab)
- goto failure;
-
if (!cl) { /* new class */
struct Qdisc *new_q;
int prio;
@@ -1487,7 +1482,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
* is really leaf before changing cl->un.leaf !
*/
if (!cl->level) {
- cl->quantum = rtab->rate.rate / q->rate2quantum;
+ cl->quantum = hopt->rate.rate / q->rate2quantum;
if (!hopt->quantum && cl->quantum < 1000) {
pr_warning(
"HTB: quantum of class %X is small. Consider r2q change.\n",
@@ -1509,8 +1504,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
cl->buffer = hopt->buffer;
cl->cbuffer = hopt->cbuffer;
- cl->rate.rate_bps = (u64)rtab->rate.rate << 3;
- cl->ceil.rate_bps = (u64)ctab->rate.rate << 3;
+ cl->rate.rate_bps = (u64)hopt->rate.rate << 3;
+ cl->ceil.rate_bps = (u64)hopt->ceil.rate << 3;
htb_precompute_ratedata(&cl->rate);
htb_precompute_ratedata(&cl->ceil);
@@ -1526,10 +1521,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
return 0;
failure:
- if (rtab)
- qdisc_put_rtab(rtab);
- if (ctab)
- qdisc_put_rtab(ctab);
return err;
}
^ permalink raw reply related
* Re: connector and netlink_broadcast (semop failed for cookie ...)
From: Evgeniy Polyakov @ 2012-11-05 21:11 UTC (permalink / raw)
To: Alexander Holler; +Cc: linux-kernel, netdev
In-Reply-To: <509816A8.70005@ahsoftware.de>
Hi
On Mon, Nov 05, 2012 at 08:42:32PM +0100, Alexander Holler (holler@ahsoftware.de) wrote:
> Could it be the case that cn_netlink_send() has to ignore the same
> return codes as done in kobject_uevent_env()? Or maybe the users of
> cn_netlink_send() have to ignore them?
We just do not know what to do with that error.
Pretty much it means nothing can be done about it.
There is no recovery path, no collection and resend, since it may lead to dos.
Its like knowing that udp packet get lost, but you do not have enough
buffer space to save it and block. Actually there is no buffer at all.
Kind of missed interrupt.
--
Evgeniy Polyakov
^ permalink raw reply
* [PATCH v2] bonding: extend bond_arp_send_all to bridge devices
From: Chris J Arges @ 2012-11-05 21:28 UTC (permalink / raw)
To: Chris J Arges; +Cc: Jay Vosburgh, Andy Gospodarek, netdev, linux-kernel
In-Reply-To: <1352146448-8921-1-git-send-email-chris.j.arges@canonical.com>
[-- Attachment #1: Type: text/plain, Size: 1640 bytes --]
On 11/05/2012 02:14 PM, Chris J Arges wrote:
> ARP monitoring does not work when we have a network in the
> following configuration:
>
> eth0----+ +----bond0.100----br0-100---{+virtual machines
> | |
> +----bond0----+----br0---(fixed IP)->--{LAN arp_ip_target}
> | |
> eth1----+ +----bond0.200----br0-200---{+virtual machines
>
> This patch extends bond_arp_send_all to also check if a device
> is also in a bridge.
>
> This is related to the following issues:
> http://launchpad.net/bugs/736226
> http://bugzilla.kernel.org/show_bug.cgi?id=31822
>
> Thanks to help from Andy Gospodarek.
>
> Signed-off-by: Chris J Arges <chris.j.arges@canonical.com>
> ---
> drivers/net/bonding/bond_main.c | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
> index 6c284d1..f11a2e1 100644
> --- a/drivers/net/bonding/bond_main.c
> +++ b/drivers/net/bonding/bond_main.c
> @@ -2670,6 +2670,19 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
> continue;
> }
>
> + /*
> + * Check if the target is part of a bridge.
> + */
> + if (rt->dst.dev->priv_flags & IFF_EBRIDGE) {
> + printk("bondbridge: %s->priv_flags has IFF_EBRIDGE\n", rt->dst.dev->name);
> +
> + /* Send ARP request */
> + bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], bond->master_ip, 0);
> + printk("bondbridge: bond_arp_send %d\n", 0);
> +
> + continue;
> + }
> +
> if (net_ratelimit()) {
> pr_warning("%s: no path to arp_ip_target %pI4 via rt.dev %s\n",
> bond->dev->name, &targets[i],
>
[-- Attachment #2: 0001-bonding-extend-bond_arp_send_all-to-bridge-devices.patch --]
[-- Type: text/x-patch, Size: 1703 bytes --]
>From 859cbc745670491f716663f6309df7674b38bb33 Mon Sep 17 00:00:00 2001
From: Chris J Arges <chris.j.arges@canonical.com>
Date: Thu, 6 Sep 2012 14:17:40 -0500
Subject: [PATCH v2] bonding: extend bond_arp_send_all to bridge devices
ARP monitoring does not work when we have a network in the
following configuration:
eth0----+ +----bond0.100----br0-100---{+virtual machines
| |
+----bond0----+----br0---(fixed IP)->--{LAN arp_ip_target}
| |
eth1----+ +----bond0.200----br0-200---{+virtual machines
This patch extends bond_arp_send_all to also check if a device
is also in a bridge.
This is related to the following issues:
http://launchpad.net/bugs/736226
http://bugzilla.kernel.org/show_bug.cgi?id=31822
Thanks to help from Andy Gospodarek.
Signed-off-by: Chris J Arges <chris.j.arges@canonical.com>
---
drivers/net/bonding/bond_main.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index b2530b0..cb791f6 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2708,6 +2708,18 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
continue;
}
+ /*
+ * Check if the target is part of a bridge.
+ */
+ if (rt->dst.dev->priv_flags & IFF_EBRIDGE) {
+ /* Confirm addr is part of the bridge */
+ addr = bond_confirm_addr(rt->dst.dev, targets[i], 0);
+
+ /* Send ARP request */
+ bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], addr, 0);
+ continue;
+ }
+
if (net_ratelimit()) {
pr_warning("%s: no path to arp_ip_target %pI4 via rt.dev %s\n",
bond->dev->name, &targets[i],
--
1.7.9.5
^ permalink raw reply related
* [PATCH v3 net-next] bonding: extend bond_arp_send_all to bridge devices
From: Chris J Arges @ 2012-11-05 22:30 UTC (permalink / raw)
To: Jay Vosburgh, Andy Gospodarek; +Cc: netdev, linux-kernel, Chris J Arges
ARP monitoring does not work when we have a network in the
following configuration:
eth0----+ +----bond0.100----br0-100---{+virtual machines
| |
+----bond0----+----br0---(fixed IP)->--{LAN arp_ip_target}
| |
eth1----+ +----bond0.200----br0-200---{+virtual machines
This patch extends bond_arp_send_all to check if a device
is also in a bridge.
This is related to the following issues:
http://launchpad.net/bugs/736226
http://bugzilla.kernel.org/show_bug.cgi?id=31822
Thanks to help from Andy Gospodarek <andy@greyhouse.net>.
Signed-off-by: Chris J Arges <chris.j.arges@canonical.com>
---
drivers/net/bonding/bond_main.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index b2530b0..cb791f6 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2708,6 +2708,18 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
continue;
}
+ /*
+ * Check if the target is part of a bridge.
+ */
+ if (rt->dst.dev->priv_flags & IFF_EBRIDGE) {
+ /* Confirm addr is part of the bridge */
+ addr = bond_confirm_addr(rt->dst.dev, targets[i], 0);
+
+ /* Send ARP request */
+ bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], addr, 0);
+ continue;
+ }
+
if (net_ratelimit()) {
pr_warning("%s: no path to arp_ip_target %pI4 via rt.dev %s\n",
bond->dev->name, &targets[i],
--
1.7.9.5
^ permalink raw reply related
* [PATCH V6 0/22] qlcnic: patches for new adapter - Qlogic 83XX CNA
From: Sony Chacko @ 2012-11-05 22:11 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept_NX_Linux_NIC_Driver, Sony Chacko
From: Sony Chacko <sony.chacko@qlogic.com>
> From: David Miller [mailto:davem@davemloft.net]
> Sent: Friday, November 02, 2012 6:28 PM
> To: Sony Chacko
> Cc: netdev; Dept-NX Linux NIC Driver
> Subject: Re: [PATCH V5 04/22] qlcnic: modify PCI and register access routines
>
> This removes the only user of qlcnic_check_vf() but leaves the definition
> around, dangling, and creating a build warning:
>
> drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c:454:1: warning:
> .qlcnic_check_vf. defined but not used [-Wunused-function]
>
>
> Thanks.
I apologize for the above mistake.
We have fixed the above warning and have verified the rest of the patches.
Please apply the updated 22 patch series to net-next.
Thanks,
Sony
^ permalink raw reply
* [PATCH V6 03/22] qlcnic: remove 82xx specific register dump utility
From: Sony Chacko @ 2012-11-05 22:11 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept_NX_Linux_NIC_Driver, Sony Chacko
In-Reply-To: <1352153535-5006-1-git-send-email-sony.chacko@qlogic.com>
From: Sony Chacko <sony.chacko@qlogic.com>
Refactor 82xx driver to support new adapter - Qlogic 83XX CNA
Remove 82xx specific register dump utility
Signed-off-by: Sony Chacko <sony.chacko@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 107 +-----
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c | 466 ----------------------
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 12 -
3 files changed, 1 insertions(+), 584 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
index 44fbc37..b44bf87 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
@@ -103,91 +103,6 @@ qlcnic_issue_cmd(struct qlcnic_adapter *adapter, struct qlcnic_cmd_args *cmd)
}
-static uint32_t qlcnic_temp_checksum(uint32_t *temp_buffer, u16 temp_size)
-{
- uint64_t sum = 0;
- int count = temp_size / sizeof(uint32_t);
- while (count-- > 0)
- sum += *temp_buffer++;
- while (sum >> 32)
- sum = (sum & 0xFFFFFFFF) + (sum >> 32);
- return ~sum;
-}
-
-int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)
-{
- int err, i;
- u16 temp_size;
- void *tmp_addr;
- u32 version, csum, *template, *tmp_buf;
- struct qlcnic_cmd_args cmd;
- struct qlcnic_hardware_context *ahw;
- struct qlcnic_dump_template_hdr *tmpl_hdr, *tmp_tmpl;
- dma_addr_t tmp_addr_t = 0;
-
- ahw = adapter->ahw;
- memset(&cmd, 0, sizeof(cmd));
- cmd.req.cmd = QLCNIC_CDRP_CMD_TEMP_SIZE;
- memset(&cmd.rsp, 1, sizeof(struct _cdrp_cmd));
- qlcnic_issue_cmd(adapter, &cmd);
- if (cmd.rsp.cmd != QLCNIC_RCODE_SUCCESS) {
- dev_info(&adapter->pdev->dev,
- "Can't get template size %d\n", cmd.rsp.cmd);
- err = -EIO;
- return err;
- }
- temp_size = cmd.rsp.arg2;
- version = cmd.rsp.arg3;
- if (!temp_size)
- return -EIO;
-
- tmp_addr = dma_alloc_coherent(&adapter->pdev->dev, temp_size,
- &tmp_addr_t, GFP_KERNEL);
- if (!tmp_addr) {
- dev_err(&adapter->pdev->dev,
- "Can't get memory for FW dump template\n");
- return -ENOMEM;
- }
- memset(&cmd.rsp, 0, sizeof(struct _cdrp_cmd));
- cmd.req.cmd = QLCNIC_CDRP_CMD_GET_TEMP_HDR;
- cmd.req.arg1 = LSD(tmp_addr_t);
- cmd.req.arg2 = MSD(tmp_addr_t);
- cmd.req.arg3 = temp_size;
- qlcnic_issue_cmd(adapter, &cmd);
-
- err = cmd.rsp.cmd;
- if (err != QLCNIC_RCODE_SUCCESS) {
- dev_err(&adapter->pdev->dev,
- "Failed to get mini dump template header %d\n", err);
- err = -EIO;
- goto error;
- }
- tmp_tmpl = tmp_addr;
- csum = qlcnic_temp_checksum((uint32_t *) tmp_addr, temp_size);
- if (csum) {
- dev_err(&adapter->pdev->dev,
- "Template header checksum validation failed\n");
- err = -EIO;
- goto error;
- }
- ahw->fw_dump.tmpl_hdr = vzalloc(temp_size);
- if (!ahw->fw_dump.tmpl_hdr) {
- err = -EIO;
- goto error;
- }
- tmp_buf = tmp_addr;
- template = (u32 *) ahw->fw_dump.tmpl_hdr;
- for (i = 0; i < temp_size/sizeof(u32); i++)
- *template++ = __le32_to_cpu(*tmp_buf++);
-
- tmpl_hdr = ahw->fw_dump.tmpl_hdr;
- tmpl_hdr->drv_cap_mask = QLCNIC_DUMP_MASK_DEF;
- ahw->fw_dump.enable = 1;
-error:
- dma_free_coherent(&adapter->pdev->dev, temp_size, tmp_addr, tmp_addr_t);
- return err;
-}
-
int
qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu)
{
@@ -705,27 +620,7 @@ int qlcnic_get_nic_info(struct qlcnic_adapter *adapter,
qlcnic_issue_cmd(adapter, &cmd);
err = cmd.rsp.cmd;
- if (err == QLCNIC_RCODE_SUCCESS) {
- npar_info->pci_func = le16_to_cpu(nic_info->pci_func);
- npar_info->op_mode = le16_to_cpu(nic_info->op_mode);
- npar_info->phys_port = le16_to_cpu(nic_info->phys_port);
- npar_info->switch_mode = le16_to_cpu(nic_info->switch_mode);
- npar_info->max_tx_ques = le16_to_cpu(nic_info->max_tx_ques);
- npar_info->max_rx_ques = le16_to_cpu(nic_info->max_rx_ques);
- npar_info->min_tx_bw = le16_to_cpu(nic_info->min_tx_bw);
- npar_info->max_tx_bw = le16_to_cpu(nic_info->max_tx_bw);
- npar_info->capabilities = le32_to_cpu(nic_info->capabilities);
- npar_info->max_mtu = le16_to_cpu(nic_info->max_mtu);
-
- dev_info(&adapter->pdev->dev,
- "phy port: %d switch_mode: %d,\n"
- "\tmax_tx_q: %d max_rx_q: %d min_tx_bw: 0x%x,\n"
- "\tmax_tx_bw: 0x%x max_mtu:0x%x, capabilities: 0x%x\n",
- npar_info->phys_port, npar_info->switch_mode,
- npar_info->max_tx_ques, npar_info->max_rx_ques,
- npar_info->min_tx_bw, npar_info->max_tx_bw,
- npar_info->max_mtu, npar_info->capabilities);
- } else {
+ if (err) {
dev_err(&adapter->pdev->dev,
"Failed to get nic info%d\n", err);
err = -EIO;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
index c7a5b3f..0001d05 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
@@ -1320,469 +1320,3 @@ int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate)
return rv;
}
-
-/* FW dump related functions */
-static u32
-qlcnic_dump_crb(struct qlcnic_adapter *adapter, struct qlcnic_dump_entry *entry,
- u32 *buffer)
-{
- int i;
- u32 addr, data;
- struct __crb *crb = &entry->region.crb;
- void __iomem *base = adapter->ahw->pci_base0;
-
- addr = crb->addr;
-
- for (i = 0; i < crb->no_ops; i++) {
- QLCNIC_RD_DUMP_REG(addr, base, &data);
- *buffer++ = cpu_to_le32(addr);
- *buffer++ = cpu_to_le32(data);
- addr += crb->stride;
- }
- return crb->no_ops * 2 * sizeof(u32);
-}
-
-static u32
-qlcnic_dump_ctrl(struct qlcnic_adapter *adapter,
- struct qlcnic_dump_entry *entry, u32 *buffer)
-{
- int i, k, timeout = 0;
- void __iomem *base = adapter->ahw->pci_base0;
- u32 addr, data;
- u8 opcode, no_ops;
- struct __ctrl *ctr = &entry->region.ctrl;
- struct qlcnic_dump_template_hdr *t_hdr = adapter->ahw->fw_dump.tmpl_hdr;
-
- addr = ctr->addr;
- no_ops = ctr->no_ops;
-
- for (i = 0; i < no_ops; i++) {
- k = 0;
- opcode = 0;
- for (k = 0; k < 8; k++) {
- if (!(ctr->opcode & (1 << k)))
- continue;
- switch (1 << k) {
- case QLCNIC_DUMP_WCRB:
- QLCNIC_WR_DUMP_REG(addr, base, ctr->val1);
- break;
- case QLCNIC_DUMP_RWCRB:
- QLCNIC_RD_DUMP_REG(addr, base, &data);
- QLCNIC_WR_DUMP_REG(addr, base, data);
- break;
- case QLCNIC_DUMP_ANDCRB:
- QLCNIC_RD_DUMP_REG(addr, base, &data);
- QLCNIC_WR_DUMP_REG(addr, base,
- (data & ctr->val2));
- break;
- case QLCNIC_DUMP_ORCRB:
- QLCNIC_RD_DUMP_REG(addr, base, &data);
- QLCNIC_WR_DUMP_REG(addr, base,
- (data | ctr->val3));
- break;
- case QLCNIC_DUMP_POLLCRB:
- while (timeout <= ctr->timeout) {
- QLCNIC_RD_DUMP_REG(addr, base, &data);
- if ((data & ctr->val2) == ctr->val1)
- break;
- msleep(1);
- timeout++;
- }
- if (timeout > ctr->timeout) {
- dev_info(&adapter->pdev->dev,
- "Timed out, aborting poll CRB\n");
- return -EINVAL;
- }
- break;
- case QLCNIC_DUMP_RD_SAVE:
- if (ctr->index_a)
- addr = t_hdr->saved_state[ctr->index_a];
- QLCNIC_RD_DUMP_REG(addr, base, &data);
- t_hdr->saved_state[ctr->index_v] = data;
- break;
- case QLCNIC_DUMP_WRT_SAVED:
- if (ctr->index_v)
- data = t_hdr->saved_state[ctr->index_v];
- else
- data = ctr->val1;
- if (ctr->index_a)
- addr = t_hdr->saved_state[ctr->index_a];
- QLCNIC_WR_DUMP_REG(addr, base, data);
- break;
- case QLCNIC_DUMP_MOD_SAVE_ST:
- data = t_hdr->saved_state[ctr->index_v];
- data <<= ctr->shl_val;
- data >>= ctr->shr_val;
- if (ctr->val2)
- data &= ctr->val2;
- data |= ctr->val3;
- data += ctr->val1;
- t_hdr->saved_state[ctr->index_v] = data;
- break;
- default:
- dev_info(&adapter->pdev->dev,
- "Unknown opcode\n");
- break;
- }
- }
- addr += ctr->stride;
- }
- return 0;
-}
-
-static u32
-qlcnic_dump_mux(struct qlcnic_adapter *adapter, struct qlcnic_dump_entry *entry,
- u32 *buffer)
-{
- int loop;
- u32 val, data = 0;
- struct __mux *mux = &entry->region.mux;
- void __iomem *base = adapter->ahw->pci_base0;
-
- val = mux->val;
- for (loop = 0; loop < mux->no_ops; loop++) {
- QLCNIC_WR_DUMP_REG(mux->addr, base, val);
- QLCNIC_RD_DUMP_REG(mux->read_addr, base, &data);
- *buffer++ = cpu_to_le32(val);
- *buffer++ = cpu_to_le32(data);
- val += mux->val_stride;
- }
- return 2 * mux->no_ops * sizeof(u32);
-}
-
-static u32
-qlcnic_dump_que(struct qlcnic_adapter *adapter, struct qlcnic_dump_entry *entry,
- u32 *buffer)
-{
- int i, loop;
- u32 cnt, addr, data, que_id = 0;
- void __iomem *base = adapter->ahw->pci_base0;
- struct __queue *que = &entry->region.que;
-
- addr = que->read_addr;
- cnt = que->read_addr_cnt;
-
- for (loop = 0; loop < que->no_ops; loop++) {
- QLCNIC_WR_DUMP_REG(que->sel_addr, base, que_id);
- addr = que->read_addr;
- for (i = 0; i < cnt; i++) {
- QLCNIC_RD_DUMP_REG(addr, base, &data);
- *buffer++ = cpu_to_le32(data);
- addr += que->read_addr_stride;
- }
- que_id += que->stride;
- }
- return que->no_ops * cnt * sizeof(u32);
-}
-
-static u32
-qlcnic_dump_ocm(struct qlcnic_adapter *adapter, struct qlcnic_dump_entry *entry,
- u32 *buffer)
-{
- int i;
- u32 data;
- void __iomem *addr;
- struct __ocm *ocm = &entry->region.ocm;
-
- addr = adapter->ahw->pci_base0 + ocm->read_addr;
- for (i = 0; i < ocm->no_ops; i++) {
- data = readl(addr);
- *buffer++ = cpu_to_le32(data);
- addr += ocm->read_addr_stride;
- }
- return ocm->no_ops * sizeof(u32);
-}
-
-static u32
-qlcnic_read_rom(struct qlcnic_adapter *adapter, struct qlcnic_dump_entry *entry,
- u32 *buffer)
-{
- int i, count = 0;
- u32 fl_addr, size, val, lck_val, addr;
- struct __mem *rom = &entry->region.mem;
- void __iomem *base = adapter->ahw->pci_base0;
-
- fl_addr = rom->addr;
- size = rom->size/4;
-lock_try:
- lck_val = readl(base + QLCNIC_FLASH_SEM2_LK);
- if (!lck_val && count < MAX_CTL_CHECK) {
- msleep(10);
- count++;
- goto lock_try;
- }
- writel(adapter->ahw->pci_func, (base + QLCNIC_FLASH_LOCK_ID));
- for (i = 0; i < size; i++) {
- addr = fl_addr & 0xFFFF0000;
- QLCNIC_WR_DUMP_REG(FLASH_ROM_WINDOW, base, addr);
- addr = LSW(fl_addr) + FLASH_ROM_DATA;
- QLCNIC_RD_DUMP_REG(addr, base, &val);
- fl_addr += 4;
- *buffer++ = cpu_to_le32(val);
- }
- readl(base + QLCNIC_FLASH_SEM2_ULK);
- return rom->size;
-}
-
-static u32
-qlcnic_dump_l1_cache(struct qlcnic_adapter *adapter,
- struct qlcnic_dump_entry *entry, u32 *buffer)
-{
- int i;
- u32 cnt, val, data, addr;
- void __iomem *base = adapter->ahw->pci_base0;
- struct __cache *l1 = &entry->region.cache;
-
- val = l1->init_tag_val;
-
- for (i = 0; i < l1->no_ops; i++) {
- QLCNIC_WR_DUMP_REG(l1->addr, base, val);
- QLCNIC_WR_DUMP_REG(l1->ctrl_addr, base, LSW(l1->ctrl_val));
- addr = l1->read_addr;
- cnt = l1->read_addr_num;
- while (cnt) {
- QLCNIC_RD_DUMP_REG(addr, base, &data);
- *buffer++ = cpu_to_le32(data);
- addr += l1->read_addr_stride;
- cnt--;
- }
- val += l1->stride;
- }
- return l1->no_ops * l1->read_addr_num * sizeof(u32);
-}
-
-static u32
-qlcnic_dump_l2_cache(struct qlcnic_adapter *adapter,
- struct qlcnic_dump_entry *entry, u32 *buffer)
-{
- int i;
- u32 cnt, val, data, addr;
- u8 poll_mask, poll_to, time_out = 0;
- void __iomem *base = adapter->ahw->pci_base0;
- struct __cache *l2 = &entry->region.cache;
-
- val = l2->init_tag_val;
- poll_mask = LSB(MSW(l2->ctrl_val));
- poll_to = MSB(MSW(l2->ctrl_val));
-
- for (i = 0; i < l2->no_ops; i++) {
- QLCNIC_WR_DUMP_REG(l2->addr, base, val);
- if (LSW(l2->ctrl_val))
- QLCNIC_WR_DUMP_REG(l2->ctrl_addr, base,
- LSW(l2->ctrl_val));
- if (!poll_mask)
- goto skip_poll;
- do {
- QLCNIC_RD_DUMP_REG(l2->ctrl_addr, base, &data);
- if (!(data & poll_mask))
- break;
- msleep(1);
- time_out++;
- } while (time_out <= poll_to);
-
- if (time_out > poll_to) {
- dev_err(&adapter->pdev->dev,
- "Timeout exceeded in %s, aborting dump\n",
- __func__);
- return -EINVAL;
- }
-skip_poll:
- addr = l2->read_addr;
- cnt = l2->read_addr_num;
- while (cnt) {
- QLCNIC_RD_DUMP_REG(addr, base, &data);
- *buffer++ = cpu_to_le32(data);
- addr += l2->read_addr_stride;
- cnt--;
- }
- val += l2->stride;
- }
- return l2->no_ops * l2->read_addr_num * sizeof(u32);
-}
-
-static u32
-qlcnic_read_memory(struct qlcnic_adapter *adapter,
- struct qlcnic_dump_entry *entry, u32 *buffer)
-{
- u32 addr, data, test, ret = 0;
- int i, reg_read;
- struct __mem *mem = &entry->region.mem;
- void __iomem *base = adapter->ahw->pci_base0;
-
- reg_read = mem->size;
- addr = mem->addr;
- /* check for data size of multiple of 16 and 16 byte alignment */
- if ((addr & 0xf) || (reg_read%16)) {
- dev_info(&adapter->pdev->dev,
- "Unaligned memory addr:0x%x size:0x%x\n",
- addr, reg_read);
- return -EINVAL;
- }
-
- mutex_lock(&adapter->ahw->mem_lock);
-
- while (reg_read != 0) {
- QLCNIC_WR_DUMP_REG(MIU_TEST_ADDR_LO, base, addr);
- QLCNIC_WR_DUMP_REG(MIU_TEST_ADDR_HI, base, 0);
- QLCNIC_WR_DUMP_REG(MIU_TEST_CTR, base,
- TA_CTL_ENABLE | TA_CTL_START);
-
- for (i = 0; i < MAX_CTL_CHECK; i++) {
- QLCNIC_RD_DUMP_REG(MIU_TEST_CTR, base, &test);
- if (!(test & TA_CTL_BUSY))
- break;
- }
- if (i == MAX_CTL_CHECK) {
- if (printk_ratelimit()) {
- dev_err(&adapter->pdev->dev,
- "failed to read through agent\n");
- ret = -EINVAL;
- goto out;
- }
- }
- for (i = 0; i < 4; i++) {
- QLCNIC_RD_DUMP_REG(MIU_TEST_READ_DATA[i], base, &data);
- *buffer++ = cpu_to_le32(data);
- }
- addr += 16;
- reg_read -= 16;
- ret += 16;
- }
-out:
- mutex_unlock(&adapter->ahw->mem_lock);
- return mem->size;
-}
-
-static u32
-qlcnic_dump_nop(struct qlcnic_adapter *adapter,
- struct qlcnic_dump_entry *entry, u32 *buffer)
-{
- entry->hdr.flags |= QLCNIC_DUMP_SKIP;
- return 0;
-}
-
-struct qlcnic_dump_operations fw_dump_ops[] = {
- { QLCNIC_DUMP_NOP, qlcnic_dump_nop },
- { QLCNIC_DUMP_READ_CRB, qlcnic_dump_crb },
- { QLCNIC_DUMP_READ_MUX, qlcnic_dump_mux },
- { QLCNIC_DUMP_QUEUE, qlcnic_dump_que },
- { QLCNIC_DUMP_BRD_CONFIG, qlcnic_read_rom },
- { QLCNIC_DUMP_READ_OCM, qlcnic_dump_ocm },
- { QLCNIC_DUMP_PEG_REG, qlcnic_dump_ctrl },
- { QLCNIC_DUMP_L1_DTAG, qlcnic_dump_l1_cache },
- { QLCNIC_DUMP_L1_ITAG, qlcnic_dump_l1_cache },
- { QLCNIC_DUMP_L1_DATA, qlcnic_dump_l1_cache },
- { QLCNIC_DUMP_L1_INST, qlcnic_dump_l1_cache },
- { QLCNIC_DUMP_L2_DTAG, qlcnic_dump_l2_cache },
- { QLCNIC_DUMP_L2_ITAG, qlcnic_dump_l2_cache },
- { QLCNIC_DUMP_L2_DATA, qlcnic_dump_l2_cache },
- { QLCNIC_DUMP_L2_INST, qlcnic_dump_l2_cache },
- { QLCNIC_DUMP_READ_ROM, qlcnic_read_rom },
- { QLCNIC_DUMP_READ_MEM, qlcnic_read_memory },
- { QLCNIC_DUMP_READ_CTRL, qlcnic_dump_ctrl },
- { QLCNIC_DUMP_TLHDR, qlcnic_dump_nop },
- { QLCNIC_DUMP_RDEND, qlcnic_dump_nop },
-};
-
-/* Walk the template and collect dump for each entry in the dump template */
-static int
-qlcnic_valid_dump_entry(struct device *dev, struct qlcnic_dump_entry *entry,
- u32 size)
-{
- int ret = 1;
- if (size != entry->hdr.cap_size) {
- dev_info(dev,
- "Invalidate dump, Type:%d\tMask:%d\tSize:%dCap_size:%d\n",
- entry->hdr.type, entry->hdr.mask, size, entry->hdr.cap_size);
- dev_info(dev, "Aborting further dump capture\n");
- ret = 0;
- }
- return ret;
-}
-
-int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
-{
- u32 *buffer;
- char mesg[64];
- char *msg[] = {mesg, NULL};
- int i, k, ops_cnt, ops_index, dump_size = 0;
- u32 entry_offset, dump, no_entries, buf_offset = 0;
- struct qlcnic_dump_entry *entry;
- struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
- struct qlcnic_dump_template_hdr *tmpl_hdr = fw_dump->tmpl_hdr;
-
- if (fw_dump->clr) {
- dev_info(&adapter->pdev->dev,
- "Previous dump not cleared, not capturing dump\n");
- return -EIO;
- }
- /* Calculate the size for dump data area only */
- for (i = 2, k = 1; (i & QLCNIC_DUMP_MASK_MAX); i <<= 1, k++)
- if (i & tmpl_hdr->drv_cap_mask)
- dump_size += tmpl_hdr->cap_sizes[k];
- if (!dump_size)
- return -EIO;
-
- fw_dump->data = vzalloc(dump_size);
- if (!fw_dump->data) {
- dev_info(&adapter->pdev->dev,
- "Unable to allocate (%d KB) for fw dump\n",
- dump_size/1024);
- return -ENOMEM;
- }
- buffer = fw_dump->data;
- fw_dump->size = dump_size;
- no_entries = tmpl_hdr->num_entries;
- ops_cnt = ARRAY_SIZE(fw_dump_ops);
- entry_offset = tmpl_hdr->offset;
- tmpl_hdr->sys_info[0] = QLCNIC_DRIVER_VERSION;
- tmpl_hdr->sys_info[1] = adapter->fw_version;
-
- for (i = 0; i < no_entries; i++) {
- entry = (void *)tmpl_hdr + entry_offset;
- if (!(entry->hdr.mask & tmpl_hdr->drv_cap_mask)) {
- entry->hdr.flags |= QLCNIC_DUMP_SKIP;
- entry_offset += entry->hdr.offset;
- continue;
- }
- /* Find the handler for this entry */
- ops_index = 0;
- while (ops_index < ops_cnt) {
- if (entry->hdr.type == fw_dump_ops[ops_index].opcode)
- break;
- ops_index++;
- }
- if (ops_index == ops_cnt) {
- dev_info(&adapter->pdev->dev,
- "Invalid entry type %d, exiting dump\n",
- entry->hdr.type);
- goto error;
- }
- /* Collect dump for this entry */
- dump = fw_dump_ops[ops_index].handler(adapter, entry, buffer);
- if (dump && !qlcnic_valid_dump_entry(&adapter->pdev->dev, entry,
- dump))
- entry->hdr.flags |= QLCNIC_DUMP_SKIP;
- buf_offset += entry->hdr.cap_size;
- entry_offset += entry->hdr.offset;
- buffer = fw_dump->data + buf_offset;
- }
- if (dump_size != buf_offset) {
- dev_info(&adapter->pdev->dev,
- "Captured(%d) and expected size(%d) do not match\n",
- buf_offset, dump_size);
- goto error;
- } else {
- fw_dump->clr = 1;
- snprintf(mesg, sizeof(mesg), "FW_DUMP=%s",
- adapter->netdev->name);
- dev_info(&adapter->pdev->dev, "Dump data, %d bytes captured\n",
- fw_dump->size);
- /* Send a udev event to notify availability of FW dump */
- kobject_uevent_env(&adapter->pdev->dev.kobj, KOBJ_CHANGE, msg);
- return 0;
- }
-error:
- vfree(fw_dump->data);
- return -EINVAL;
-}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 5ee1ce0..328088f 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -571,9 +571,6 @@ qlcnic_check_options(struct qlcnic_adapter *adapter)
adapter->fw_version > prev_fw_version) {
if (fw_dump->tmpl_hdr)
vfree(fw_dump->tmpl_hdr);
- if (!qlcnic_fw_cmd_get_minidump_temp(adapter))
- dev_info(&pdev->dev,
- "Supports FW dump capability\n");
}
}
@@ -2297,15 +2294,6 @@ skip_ack_check:
qlcnic_api_unlock(adapter);
- rtnl_lock();
- if (adapter->ahw->fw_dump.enable &&
- (adapter->flags & QLCNIC_FW_RESET_OWNER)) {
- QLCDB(adapter, DRV, "Take FW dump\n");
- qlcnic_dump_fw(adapter);
- adapter->flags |= QLCNIC_FW_HANG;
- }
- rtnl_unlock();
-
adapter->flags &= ~QLCNIC_FW_RESET_OWNER;
if (!adapter->nic_ops->start_firmware(adapter)) {
qlcnic_schedule_work(adapter, qlcnic_attach_work, 0);
--
1.7.1
^ permalink raw reply related
* [PATCH V6 04/22] qlcnic: modify PCI and register access routines
From: Sony Chacko @ 2012-11-05 22:11 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept_NX_Linux_NIC_Driver, Sony Chacko
In-Reply-To: <1352153535-5006-1-git-send-email-sony.chacko@qlogic.com>
From: Sony Chacko <sony.chacko@qlogic.com>
Refactor 82xx driver to support new adapter - Qlogic 83XX CNA
Update PCI and hardware access routines
Signed-off-by: Anirban Chakraborty <anirban.chakraborty@qlogic.com>
Signed-off-by: Sony Chacko <sony.chacko@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 7 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h | 40 ++--
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c | 286 +++++++++++++---------
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 63 +++--
4 files changed, 227 insertions(+), 169 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index d7bba35..3ec57fc 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -585,6 +585,7 @@ struct qlcnic_recv_context {
/* HW context creation */
#define QLCNIC_OS_CRB_RETRY_COUNT 4000
+
#define QLCNIC_CDRP_SIGNATURE_MAKE(pcifn, version) \
(((pcifn) & 0xff) | (((version) & 0xff) << 8) | (0xcafe << 16))
@@ -1438,7 +1439,7 @@ struct qlcnic_cmd_args {
int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter);
int qlcnic_fw_cmd_set_port(struct qlcnic_adapter *adapter, u32 config);
-u32 qlcnic_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong off);
+int qlcnic_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong off);
int qlcnic_hw_write_wx_2M(struct qlcnic_adapter *, ulong off, u32 data);
int qlcnic_pci_mem_write_2M(struct qlcnic_adapter *, u64 off, u64 data);
int qlcnic_pci_mem_read_2M(struct qlcnic_adapter *, u64 off, u64 *data);
@@ -1450,6 +1451,7 @@ void qlcnic_pci_camqm_write_2M(struct qlcnic_adapter *, u64, u64);
#define QLCRD32(adapter, off) \
(qlcnic_hw_read_wx_2M(adapter, off))
+
#define QLCWR32(adapter, off, val) \
(qlcnic_hw_write_wx_2M(adapter, off, val))
@@ -1502,7 +1504,7 @@ int qlcnic_rom_fast_read_words(struct qlcnic_adapter *adapter, int addr,
int qlcnic_alloc_sw_resources(struct qlcnic_adapter *adapter);
void qlcnic_free_sw_resources(struct qlcnic_adapter *adapter);
-void __iomem *qlcnic_get_ioaddr(struct qlcnic_adapter *, u32);
+void __iomem *qlcnic_get_ioaddr(struct qlcnic_hardware_context *, u32);
int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter);
void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter);
@@ -1574,7 +1576,6 @@ int qlcnic_get_eswitch_stats(struct qlcnic_adapter *, const u8, u8,
struct __qlcnic_esw_statistics *);
int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, u8, u8, u8);
int qlcnic_get_mac_stats(struct qlcnic_adapter *, struct qlcnic_mac_statistics *);
-
extern int qlcnic_config_tso;
void qlcnic_fetch_mac(struct qlcnic_adapter *, u32, u32, u8, u8 *);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h
index 28a6b28..ac85816 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h
@@ -832,55 +832,63 @@ enum {
#define LSD(x) ((uint32_t)((uint64_t)(x)))
#define MSD(x) ((uint32_t)((((uint64_t)(x)) >> 16) >> 16))
+#define MIU_TEST_AGT_BASE (0x90)
+#define QLCNIC_MS_CTRL 0x41000090
+#define QLCNIC_MS_ADDR_LO 0x41000094
+#define QLCNIC_MS_ADDR_HI 0x41000098
+#define QLCNIC_MS_WRTDATA_LO 0x410000A0
+#define QLCNIC_MS_WRTDATA_HI 0x410000A4
+#define QLCNIC_MS_WRTDATA_ULO 0x410000B0
+#define QLCNIC_MS_WRTDATA_UHI 0x410000B4
+#define QLCNIC_MS_RDDATA_LO 0x410000A8
+#define QLCNIC_MS_RDDATA_HI 0x410000AC
+#define QLCNIC_MS_RDDATA_ULO 0x410000B8
+#define QLCNIC_MS_RDDATA_UHI 0x410000BC
+#define QLC_TA_WRITE_ENABLE (TA_CTL_ENABLE | TA_CTL_WRITE)
+#define QLC_TA_WRITE_START (TA_CTL_START | TA_CTL_ENABLE | TA_CTL_WRITE)
+#define QLC_TA_START_ENABLE (TA_CTL_START | TA_CTL_ENABLE)
+
#define QLCNIC_LEGACY_INTR_CONFIG \
{ \
{ \
.int_vec_bit = PCIX_INT_VECTOR_BIT_F0, \
.tgt_status_reg = ISR_INT_TARGET_STATUS, \
- .tgt_mask_reg = ISR_INT_TARGET_MASK, \
- .pci_int_reg = ISR_MSI_INT_TRIGGER(0) }, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK, }, \
\
{ \
.int_vec_bit = PCIX_INT_VECTOR_BIT_F1, \
.tgt_status_reg = ISR_INT_TARGET_STATUS_F1, \
- .tgt_mask_reg = ISR_INT_TARGET_MASK_F1, \
- .pci_int_reg = ISR_MSI_INT_TRIGGER(1) }, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F1, }, \
\
{ \
.int_vec_bit = PCIX_INT_VECTOR_BIT_F2, \
.tgt_status_reg = ISR_INT_TARGET_STATUS_F2, \
- .tgt_mask_reg = ISR_INT_TARGET_MASK_F2, \
- .pci_int_reg = ISR_MSI_INT_TRIGGER(2) }, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F2, }, \
\
{ \
.int_vec_bit = PCIX_INT_VECTOR_BIT_F3, \
.tgt_status_reg = ISR_INT_TARGET_STATUS_F3, \
- .tgt_mask_reg = ISR_INT_TARGET_MASK_F3, \
- .pci_int_reg = ISR_MSI_INT_TRIGGER(3) }, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F3, }, \
\
{ \
.int_vec_bit = PCIX_INT_VECTOR_BIT_F4, \
.tgt_status_reg = ISR_INT_TARGET_STATUS_F4, \
- .tgt_mask_reg = ISR_INT_TARGET_MASK_F4, \
- .pci_int_reg = ISR_MSI_INT_TRIGGER(4) }, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F4, }, \
\
{ \
.int_vec_bit = PCIX_INT_VECTOR_BIT_F5, \
.tgt_status_reg = ISR_INT_TARGET_STATUS_F5, \
- .tgt_mask_reg = ISR_INT_TARGET_MASK_F5, \
- .pci_int_reg = ISR_MSI_INT_TRIGGER(5) }, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F5, }, \
\
{ \
.int_vec_bit = PCIX_INT_VECTOR_BIT_F6, \
.tgt_status_reg = ISR_INT_TARGET_STATUS_F6, \
- .tgt_mask_reg = ISR_INT_TARGET_MASK_F6, \
- .pci_int_reg = ISR_MSI_INT_TRIGGER(6) }, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F6, }, \
\
{ \
.int_vec_bit = PCIX_INT_VECTOR_BIT_F7, \
.tgt_status_reg = ISR_INT_TARGET_STATUS_F7, \
- .tgt_mask_reg = ISR_INT_TARGET_MASK_F7, \
- .pci_int_reg = ISR_MSI_INT_TRIGGER(7) }, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F7, }, \
}
/* NIU REGS */
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
index 0001d05..9e38099 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
@@ -6,6 +6,7 @@
*/
#include "qlcnic.h"
+#include "qlcnic_hdr.h"
#include <linux/slab.h>
#include <net/ip.h>
@@ -22,6 +23,15 @@
#define CRB_HI(off) ((crb_hub_agt[CRB_BLK(off)] << 20) | ((off) & 0xf0000))
#define CRB_INDIRECT_2M (0x1e0000UL)
+struct qlcnic_ms_reg_ctrl {
+ u32 ocm_window;
+ u32 control;
+ u32 hi;
+ u32 low;
+ u32 rd[4];
+ u32 wd[4];
+ u64 off;
+};
#ifndef readq
static inline u64 readq(void __iomem *addr)
@@ -266,10 +276,44 @@ static const unsigned crb_hub_agt[64] = {
0,
};
+static const u32 msi_tgt_status[8] = {
+ ISR_INT_TARGET_STATUS, ISR_INT_TARGET_STATUS_F1,
+ ISR_INT_TARGET_STATUS_F2, ISR_INT_TARGET_STATUS_F3,
+ ISR_INT_TARGET_STATUS_F4, ISR_INT_TARGET_STATUS_F5,
+ ISR_INT_TARGET_STATUS_F6, ISR_INT_TARGET_STATUS_F7
+};
+
/* PCI Windowing for DDR regions. */
#define QLCNIC_PCIE_SEM_TIMEOUT 10000
+static void qlcnic_read_window_reg(u32 addr, void __iomem *bar0, u32 *data)
+{
+ u32 dest;
+ void __iomem *val;
+
+ dest = addr & 0xFFFF0000;
+ val = bar0 + QLCNIC_FW_DUMP_REG1;
+ writel(dest, val);
+ readl(val);
+ val = bar0 + QLCNIC_FW_DUMP_REG2 + LSW(addr);
+ *data = readl(val);
+}
+
+static void qlcnic_write_window_reg(u32 addr, void __iomem *bar0, u32 data)
+{
+ u32 dest;
+ void __iomem *val;
+
+ dest = addr & 0xFFFF0000;
+ val = bar0 + QLCNIC_FW_DUMP_REG1;
+ writel(dest, val);
+ readl(val);
+ val = bar0 + QLCNIC_FW_DUMP_REG2 + LSW(addr);
+ writel(data, val);
+ readl(val);
+}
+
int
qlcnic_pcie_sem_lock(struct qlcnic_adapter *adapter, int sem, u32 id_reg)
{
@@ -300,6 +344,23 @@ qlcnic_pcie_sem_unlock(struct qlcnic_adapter *adapter, int sem)
QLCRD32(adapter, QLCNIC_PCIE_REG(PCIE_SEM_UNLOCK(sem)));
}
+int qlcnic_ind_rd(struct qlcnic_adapter *adapter, u32 addr)
+{
+ u32 data;
+
+ if (qlcnic_82xx_check(adapter))
+ qlcnic_read_window_reg(addr, adapter->ahw->pci_base0, &data);
+ else
+ return -EIO;
+ return data;
+}
+
+void qlcnic_ind_wr(struct qlcnic_adapter *adapter, u32 addr, u32 data)
+{
+ if (qlcnic_82xx_check(adapter))
+ qlcnic_write_window_reg(addr, adapter->ahw->pci_base0, data);
+}
+
static int
qlcnic_send_cmd_descs(struct qlcnic_adapter *adapter,
struct cmd_desc_type0 *cmd_desc_arr, int nr_desc)
@@ -863,9 +924,8 @@ int qlcnic_set_features(struct net_device *netdev, netdev_features_t features)
* 0 if no window access is needed. 'off' is set to 2M addr
* In: 'off' is offset from base in 128M pci map
*/
-static int
-qlcnic_pci_get_crb_addr_2M(struct qlcnic_adapter *adapter,
- ulong off, void __iomem **addr)
+static int qlcnic_pci_get_crb_addr_2M(struct qlcnic_hardware_context *ahw,
+ ulong off, void __iomem **addr)
{
const struct crb_128M_2M_sub_block_map *m;
@@ -880,7 +940,7 @@ qlcnic_pci_get_crb_addr_2M(struct qlcnic_adapter *adapter,
m = &crb_128M_2M_map[CRB_BLK(off)].sub_block[CRB_SUBBLK(off)];
if (m->valid && (m->start_128M <= off) && (m->end_128M > off)) {
- *addr = adapter->ahw->pci_base0 + m->start_2M +
+ *addr = ahw->pci_base0 + m->start_2M +
(off - m->start_128M);
return 0;
}
@@ -888,7 +948,7 @@ qlcnic_pci_get_crb_addr_2M(struct qlcnic_adapter *adapter,
/*
* Not in direct map, use crb window
*/
- *addr = adapter->ahw->pci_base0 + CRB_INDIRECT_2M + (off & MASK(16));
+ *addr = ahw->pci_base0 + CRB_INDIRECT_2M + (off & MASK(16));
return 1;
}
@@ -922,14 +982,13 @@ qlcnic_pci_set_crbwindow_2M(struct qlcnic_adapter *adapter, ulong off)
return 0;
}
-int
-qlcnic_hw_write_wx_2M(struct qlcnic_adapter *adapter, ulong off, u32 data)
+int qlcnic_hw_write_wx_2M(struct qlcnic_adapter *adapter, ulong off, u32 data)
{
unsigned long flags;
int rv;
void __iomem *addr = NULL;
- rv = qlcnic_pci_get_crb_addr_2M(adapter, off, &addr);
+ rv = qlcnic_pci_get_crb_addr_2M(adapter->ahw, off, &addr);
if (rv == 0) {
writel(data, addr);
@@ -954,15 +1013,14 @@ qlcnic_hw_write_wx_2M(struct qlcnic_adapter *adapter, ulong off, u32 data)
return -EIO;
}
-u32
-qlcnic_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong off)
+int qlcnic_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong off)
{
unsigned long flags;
int rv;
u32 data = -1;
void __iomem *addr = NULL;
- rv = qlcnic_pci_get_crb_addr_2M(adapter, off, &addr);
+ rv = qlcnic_pci_get_crb_addr_2M(adapter->ahw, off, &addr);
if (rv == 0)
return readl(addr);
@@ -985,46 +1043,28 @@ qlcnic_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong off)
}
-void __iomem *
-qlcnic_get_ioaddr(struct qlcnic_adapter *adapter, u32 offset)
+void __iomem *qlcnic_get_ioaddr(struct qlcnic_hardware_context *ahw,
+ u32 offset)
{
void __iomem *addr = NULL;
- WARN_ON(qlcnic_pci_get_crb_addr_2M(adapter, offset, &addr));
+ WARN_ON(qlcnic_pci_get_crb_addr_2M(ahw, offset, &addr));
return addr;
}
-
-static int
-qlcnic_pci_set_window_2M(struct qlcnic_adapter *adapter,
- u64 addr, u32 *start)
-{
- u32 window;
-
- window = OCM_WIN_P3P(addr);
-
- writel(window, adapter->ahw->ocm_win_crb);
- /* read back to flush */
- readl(adapter->ahw->ocm_win_crb);
-
- *start = QLCNIC_PCI_OCM0_2M + GET_MEM_OFFS_2M(addr);
- return 0;
-}
-
-static int
-qlcnic_pci_mem_access_direct(struct qlcnic_adapter *adapter, u64 off,
- u64 *data, int op)
+static int qlcnic_pci_mem_access_direct(struct qlcnic_adapter *adapter,
+ u32 window, u64 off, u64 *data, int op)
{
void __iomem *addr;
- int ret;
u32 start;
mutex_lock(&adapter->ahw->mem_lock);
- ret = qlcnic_pci_set_window_2M(adapter, off, &start);
- if (ret != 0)
- goto unlock;
+ writel(window, adapter->ahw->ocm_win_crb);
+ /* read back to flush */
+ readl(adapter->ahw->ocm_win_crb);
+ start = QLCNIC_PCI_OCM0_2M + off;
addr = adapter->ahw->pci_base0 + start;
@@ -1033,14 +1073,16 @@ qlcnic_pci_mem_access_direct(struct qlcnic_adapter *adapter, u64 off,
else /* write */
writeq(*data, addr);
-unlock:
- mutex_unlock(&adapter->ahw->mem_lock);
+ /* Set window to 0 */
+ writel(0, adapter->ahw->ocm_win_crb);
+ readl(adapter->ahw->ocm_win_crb);
- return ret;
+ mutex_unlock(&adapter->ahw->mem_lock);
+ return 0;
}
-void
-qlcnic_pci_camqm_read_2M(struct qlcnic_adapter *adapter, u64 off, u64 *data)
+void qlcnic_pci_camqm_read_2M(struct qlcnic_adapter *adapter, u64 off,
+ u64 *data)
{
void __iomem *addr = adapter->ahw->pci_base0 +
QLCNIC_PCI_CAMQM_2M_BASE + (off - QLCNIC_PCI_CAMQM);
@@ -1050,8 +1092,8 @@ qlcnic_pci_camqm_read_2M(struct qlcnic_adapter *adapter, u64 off, u64 *data)
mutex_unlock(&adapter->ahw->mem_lock);
}
-void
-qlcnic_pci_camqm_write_2M(struct qlcnic_adapter *adapter, u64 off, u64 data)
+void qlcnic_pci_camqm_write_2M(struct qlcnic_adapter *adapter, u64 off,
+ u64 data)
{
void __iomem *addr = adapter->ahw->pci_base0 +
QLCNIC_PCI_CAMQM_2M_BASE + (off - QLCNIC_PCI_CAMQM);
@@ -1063,52 +1105,72 @@ qlcnic_pci_camqm_write_2M(struct qlcnic_adapter *adapter, u64 off, u64 data)
#define MAX_CTL_CHECK 1000
-int
-qlcnic_pci_mem_write_2M(struct qlcnic_adapter *adapter,
- u64 off, u64 data)
+/* Set MS memory control data for different adapters */
+static void qlcnic_set_ms_controls(struct qlcnic_adapter *adapter, u64 off,
+ struct qlcnic_ms_reg_ctrl *ms)
+{
+ ms->control = QLCNIC_MS_CTRL;
+ ms->low = QLCNIC_MS_ADDR_LO;
+ ms->hi = QLCNIC_MS_ADDR_HI;
+ if (off & 0xf) {
+ ms->wd[0] = QLCNIC_MS_WRTDATA_LO;
+ ms->rd[0] = QLCNIC_MS_RDDATA_LO;
+ ms->wd[1] = QLCNIC_MS_WRTDATA_HI;
+ ms->rd[1] = QLCNIC_MS_RDDATA_HI;
+ ms->wd[2] = QLCNIC_MS_WRTDATA_ULO;
+ ms->wd[3] = QLCNIC_MS_WRTDATA_UHI;
+ ms->rd[2] = QLCNIC_MS_RDDATA_ULO;
+ ms->rd[3] = QLCNIC_MS_RDDATA_UHI;
+ } else {
+ ms->wd[0] = QLCNIC_MS_WRTDATA_ULO;
+ ms->rd[0] = QLCNIC_MS_RDDATA_ULO;
+ ms->wd[1] = QLCNIC_MS_WRTDATA_UHI;
+ ms->rd[1] = QLCNIC_MS_RDDATA_UHI;
+ ms->wd[2] = QLCNIC_MS_WRTDATA_LO;
+ ms->wd[3] = QLCNIC_MS_WRTDATA_HI;
+ ms->rd[2] = QLCNIC_MS_RDDATA_LO;
+ ms->rd[3] = QLCNIC_MS_RDDATA_HI;
+ }
+
+ ms->ocm_window = OCM_WIN_P3P(off);
+ ms->off = GET_MEM_OFFS_2M(off);
+}
+
+int qlcnic_pci_mem_write_2M(struct qlcnic_adapter *adapter, u64 off, u64 data)
{
- int i, j, ret;
+ int j, ret = 0;
u32 temp, off8;
- void __iomem *mem_crb;
+ struct qlcnic_ms_reg_ctrl ms;
/* Only 64-bit aligned access */
if (off & 7)
return -EIO;
- /* P3 onward, test agent base for MIU and SIU is same */
- if (ADDR_IN_RANGE(off, QLCNIC_ADDR_QDR_NET,
- QLCNIC_ADDR_QDR_NET_MAX)) {
- mem_crb = qlcnic_get_ioaddr(adapter,
- QLCNIC_CRB_QDR_NET+MIU_TEST_AGT_BASE);
- goto correct;
- }
+ memset(&ms, 0, sizeof(struct qlcnic_ms_reg_ctrl));
+ if (!(ADDR_IN_RANGE(off, QLCNIC_ADDR_QDR_NET,
+ QLCNIC_ADDR_QDR_NET_MAX) ||
+ ADDR_IN_RANGE(off, QLCNIC_ADDR_DDR_NET,
+ QLCNIC_ADDR_DDR_NET_MAX)))
+ return -EIO;
- if (ADDR_IN_RANGE(off, QLCNIC_ADDR_DDR_NET, QLCNIC_ADDR_DDR_NET_MAX)) {
- mem_crb = qlcnic_get_ioaddr(adapter,
- QLCNIC_CRB_DDR_NET+MIU_TEST_AGT_BASE);
- goto correct;
- }
+ qlcnic_set_ms_controls(adapter, off, &ms);
if (ADDR_IN_RANGE(off, QLCNIC_ADDR_OCM0, QLCNIC_ADDR_OCM0_MAX))
- return qlcnic_pci_mem_access_direct(adapter, off, &data, 1);
-
- return -EIO;
+ return qlcnic_pci_mem_access_direct(adapter, ms.ocm_window,
+ ms.off, &data, 1);
-correct:
off8 = off & ~0xf;
mutex_lock(&adapter->ahw->mem_lock);
- writel(off8, (mem_crb + MIU_TEST_AGT_ADDR_LO));
- writel(0, (mem_crb + MIU_TEST_AGT_ADDR_HI));
+ qlcnic_ind_wr(adapter, ms.low, off8);
+ qlcnic_ind_wr(adapter, ms.hi, 0);
- i = 0;
- writel(TA_CTL_ENABLE, (mem_crb + TEST_AGT_CTRL));
- writel((TA_CTL_START | TA_CTL_ENABLE),
- (mem_crb + TEST_AGT_CTRL));
+ qlcnic_ind_wr(adapter, ms.control, TA_CTL_ENABLE);
+ qlcnic_ind_wr(adapter, ms.control, QLC_TA_START_ENABLE);
for (j = 0; j < MAX_CTL_CHECK; j++) {
- temp = readl(mem_crb + TEST_AGT_CTRL);
+ temp = qlcnic_ind_rd(adapter, ms.control);
if ((temp & TA_CTL_BUSY) == 0)
break;
}
@@ -1118,24 +1180,18 @@ correct:
goto done;
}
- i = (off & 0xf) ? 0 : 2;
- writel(readl(mem_crb + MIU_TEST_AGT_RDDATA(i)),
- mem_crb + MIU_TEST_AGT_WRDATA(i));
- writel(readl(mem_crb + MIU_TEST_AGT_RDDATA(i+1)),
- mem_crb + MIU_TEST_AGT_WRDATA(i+1));
- i = (off & 0xf) ? 2 : 0;
-
- writel(data & 0xffffffff,
- mem_crb + MIU_TEST_AGT_WRDATA(i));
- writel((data >> 32) & 0xffffffff,
- mem_crb + MIU_TEST_AGT_WRDATA(i+1));
+ /* This is the modify part of read-modify-write */
+ qlcnic_ind_wr(adapter, ms.wd[0], qlcnic_ind_rd(adapter, ms.rd[0]));
+ qlcnic_ind_wr(adapter, ms.wd[1], qlcnic_ind_rd(adapter, ms.rd[1]));
+ /* This is the write part of read-modify-write */
+ qlcnic_ind_wr(adapter, ms.wd[2], data & 0xffffffff);
+ qlcnic_ind_wr(adapter, ms.wd[3], (data >> 32) & 0xffffffff);
- writel((TA_CTL_ENABLE | TA_CTL_WRITE), (mem_crb + TEST_AGT_CTRL));
- writel((TA_CTL_START | TA_CTL_ENABLE | TA_CTL_WRITE),
- (mem_crb + TEST_AGT_CTRL));
+ qlcnic_ind_wr(adapter, ms.control, QLC_TA_WRITE_ENABLE);
+ qlcnic_ind_wr(adapter, ms.control, QLC_TA_WRITE_START);
for (j = 0; j < MAX_CTL_CHECK; j++) {
- temp = readl(mem_crb + TEST_AGT_CTRL);
+ temp = qlcnic_ind_rd(adapter, ms.control);
if ((temp & TA_CTL_BUSY) == 0)
break;
}
@@ -1154,52 +1210,41 @@ done:
return ret;
}
-int
-qlcnic_pci_mem_read_2M(struct qlcnic_adapter *adapter,
- u64 off, u64 *data)
+int qlcnic_pci_mem_read_2M(struct qlcnic_adapter *adapter, u64 off, u64 *data)
{
int j, ret;
u32 temp, off8;
u64 val;
- void __iomem *mem_crb;
+ struct qlcnic_ms_reg_ctrl ms;
/* Only 64-bit aligned access */
if (off & 7)
return -EIO;
+ if (!(ADDR_IN_RANGE(off, QLCNIC_ADDR_QDR_NET,
+ QLCNIC_ADDR_QDR_NET_MAX) ||
+ ADDR_IN_RANGE(off, QLCNIC_ADDR_DDR_NET,
+ QLCNIC_ADDR_DDR_NET_MAX)))
+ return -EIO;
- /* P3 onward, test agent base for MIU and SIU is same */
- if (ADDR_IN_RANGE(off, QLCNIC_ADDR_QDR_NET,
- QLCNIC_ADDR_QDR_NET_MAX)) {
- mem_crb = qlcnic_get_ioaddr(adapter,
- QLCNIC_CRB_QDR_NET+MIU_TEST_AGT_BASE);
- goto correct;
- }
-
- if (ADDR_IN_RANGE(off, QLCNIC_ADDR_DDR_NET, QLCNIC_ADDR_DDR_NET_MAX)) {
- mem_crb = qlcnic_get_ioaddr(adapter,
- QLCNIC_CRB_DDR_NET+MIU_TEST_AGT_BASE);
- goto correct;
- }
+ memset(&ms, 0, sizeof(struct qlcnic_ms_reg_ctrl));
+ qlcnic_set_ms_controls(adapter, off, &ms);
- if (ADDR_IN_RANGE(off, QLCNIC_ADDR_OCM0, QLCNIC_ADDR_OCM0_MAX)) {
- return qlcnic_pci_mem_access_direct(adapter,
- off, data, 0);
- }
+ if (ADDR_IN_RANGE(off, QLCNIC_ADDR_OCM0, QLCNIC_ADDR_OCM0_MAX))
+ return qlcnic_pci_mem_access_direct(adapter, ms.ocm_window,
+ ms.off, data, 0);
- return -EIO;
+ mutex_lock(&adapter->ahw->mem_lock);
-correct:
off8 = off & ~0xf;
- mutex_lock(&adapter->ahw->mem_lock);
+ qlcnic_ind_wr(adapter, ms.low, off8);
+ qlcnic_ind_wr(adapter, ms.hi, 0);
- writel(off8, (mem_crb + MIU_TEST_AGT_ADDR_LO));
- writel(0, (mem_crb + MIU_TEST_AGT_ADDR_HI));
- writel(TA_CTL_ENABLE, (mem_crb + TEST_AGT_CTRL));
- writel((TA_CTL_START | TA_CTL_ENABLE), (mem_crb + TEST_AGT_CTRL));
+ qlcnic_ind_wr(adapter, ms.control, TA_CTL_ENABLE);
+ qlcnic_ind_wr(adapter, ms.control, QLC_TA_START_ENABLE);
for (j = 0; j < MAX_CTL_CHECK; j++) {
- temp = readl(mem_crb + TEST_AGT_CTRL);
+ temp = qlcnic_ind_rd(adapter, ms.control);
if ((temp & TA_CTL_BUSY) == 0)
break;
}
@@ -1210,13 +1255,10 @@ correct:
"failed to read through agent\n");
ret = -EIO;
} else {
- off8 = MIU_TEST_AGT_RDDATA_LO;
- if (off & 0xf)
- off8 = MIU_TEST_AGT_RDDATA_UPPER_LO;
- temp = readl(mem_crb + off8 + 4);
+ temp = qlcnic_ind_rd(adapter, ms.rd[3]);
val = (u64)temp << 32;
- val |= readl(mem_crb + off8);
+ val |= qlcnic_ind_rd(adapter, ms.rd[2]);
*data = val;
ret = 0;
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 328088f..a40bd05 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -292,32 +292,31 @@ static int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
return err;
}
-
static void qlcnic_enable_msi_legacy(struct qlcnic_adapter *adapter)
{
+ u32 offset, mask_reg;
const struct qlcnic_legacy_intr_set *legacy_intrp;
+ struct qlcnic_hardware_context *ahw = adapter->ahw;
struct pci_dev *pdev = adapter->pdev;
if (use_msi && !pci_enable_msi(pdev)) {
adapter->flags |= QLCNIC_MSI_ENABLED;
- adapter->tgt_status_reg = qlcnic_get_ioaddr(adapter,
- msi_tgt_status[adapter->ahw->pci_func]);
+ offset = msi_tgt_status[adapter->ahw->pci_func];
+ adapter->tgt_status_reg = qlcnic_get_ioaddr(adapter->ahw,
+ offset);
dev_info(&pdev->dev, "using msi interrupts\n");
adapter->msix_entries[0].vector = pdev->irq;
return;
}
legacy_intrp = &legacy_intr[adapter->ahw->pci_func];
-
adapter->ahw->int_vec_bit = legacy_intrp->int_vec_bit;
- adapter->tgt_status_reg = qlcnic_get_ioaddr(adapter,
- legacy_intrp->tgt_status_reg);
- adapter->tgt_mask_reg = qlcnic_get_ioaddr(adapter,
- legacy_intrp->tgt_mask_reg);
- adapter->isr_int_vec = qlcnic_get_ioaddr(adapter, ISR_INT_VECTOR);
-
- adapter->crb_int_state_reg = qlcnic_get_ioaddr(adapter,
- ISR_INT_STATE_REG);
+ offset = legacy_intrp->tgt_status_reg;
+ adapter->tgt_status_reg = qlcnic_get_ioaddr(ahw, offset);
+ mask_reg = legacy_intrp->tgt_mask_reg;
+ adapter->tgt_mask_reg = qlcnic_get_ioaddr(ahw, mask_reg);
+ adapter->isr_int_vec = qlcnic_get_ioaddr(ahw, ISR_INT_VECTOR);
+ adapter->crb_int_state_reg = qlcnic_get_ioaddr(ahw, ISR_INT_STATE_REG);
dev_info(&pdev->dev, "using legacy interrupts\n");
adapter->msix_entries[0].vector = pdev->irq;
}
@@ -489,20 +488,32 @@ qlcnic_check_vf(struct qlcnic_adapter *adapter)
adapter->nic_ops = &qlcnic_ops;
}
-static int
-qlcnic_setup_pci_map(struct qlcnic_adapter *adapter)
+#define QLCNIC_82XX_BAR0_LENGTH 0x00200000UL
+static void qlcnic_get_bar_length(u32 dev_id, ulong *bar)
+{
+ switch (dev_id) {
+ case PCI_DEVICE_ID_QLOGIC_QLE824X:
+ *bar = QLCNIC_82XX_BAR0_LENGTH;
+ break;
+ default:
+ *bar = 0;
+ }
+}
+
+static int qlcnic_setup_pci_map(struct pci_dev *pdev,
+ struct qlcnic_hardware_context *ahw)
{
+ u32 offset;
void __iomem *mem_ptr0 = NULL;
resource_size_t mem_base;
- unsigned long mem_len, pci_len0 = 0;
-
- struct pci_dev *pdev = adapter->pdev;
+ unsigned long mem_len, pci_len0 = 0, bar0_len;
/* remap phys address */
mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */
mem_len = pci_resource_len(pdev, 0);
- if (mem_len == QLCNIC_PCI_2MB_SIZE) {
+ qlcnic_get_bar_length(pdev->device, &bar0_len);
+ if (mem_len >= bar0_len) {
mem_ptr0 = pci_ioremap_bar(pdev, 0);
if (mem_ptr0 == NULL) {
@@ -515,15 +526,10 @@ qlcnic_setup_pci_map(struct qlcnic_adapter *adapter)
}
dev_info(&pdev->dev, "%dMB memory map\n", (int)(mem_len>>20));
-
- adapter->ahw->pci_base0 = mem_ptr0;
- adapter->ahw->pci_len0 = pci_len0;
-
- qlcnic_check_vf(adapter);
-
- adapter->ahw->ocm_win_crb = qlcnic_get_ioaddr(adapter,
- QLCNIC_PCIX_PS_REG(PCIX_OCM_WINDOW_REG(
- adapter->ahw->pci_func)));
+ ahw->pci_base0 = mem_ptr0;
+ ahw->pci_len0 = pci_len0;
+ offset = QLCNIC_PCIX_PS_REG(PCIX_OCM_WINDOW_REG(ahw->pci_func));
+ qlcnic_get_ioaddr(ahw, offset);
return 0;
}
@@ -1563,9 +1569,10 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
spin_lock_init(&adapter->tx_clean_lock);
INIT_LIST_HEAD(&adapter->mac_list);
- err = qlcnic_setup_pci_map(adapter);
+ err = qlcnic_setup_pci_map(pdev, adapter->ahw);
if (err)
goto err_out_free_hw;
+ qlcnic_check_vf(adapter);
/* This will be reset for mezz cards */
adapter->portnum = adapter->ahw->pci_func;
--
1.7.1
^ permalink raw reply related
* [PATCH V6 02/22] qlcnic: create seperate file for data path routines
From: Sony Chacko @ 2012-11-05 22:11 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept_NX_Linux_NIC_Driver, Sony Chacko
In-Reply-To: <1352153535-5006-1-git-send-email-sony.chacko@qlogic.com>
From: Sony Chacko <sony.chacko@qlogic.com>
Refactor 82xx driver to support new adapter - Qlogic 83XX CNA
Signed-off-by: Anirban Chakraborty <anirban.chakraborty@qlogic.com>
Signed-off-by: Sony Chacko <sony.chacko@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/Makefile | 2 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 50 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c | 635 -----------
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c | 1213 ++++++++++++++++++++++
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 725 +------------
5 files changed, 1317 insertions(+), 1308 deletions(-)
create mode 100644 drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
diff --git a/drivers/net/ethernet/qlogic/qlcnic/Makefile b/drivers/net/ethernet/qlogic/qlcnic/Makefile
index ddba83e..a26ee17 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/Makefile
+++ b/drivers/net/ethernet/qlogic/qlcnic/Makefile
@@ -5,4 +5,4 @@
obj-$(CONFIG_QLCNIC) := qlcnic.o
qlcnic-y := qlcnic_hw.o qlcnic_main.o qlcnic_init.o \
- qlcnic_ethtool.o qlcnic_ctx.o
+ qlcnic_ethtool.o qlcnic_ctx.o qlcnic_io.o
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 9dd20b6..d7bba35 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -510,6 +510,8 @@ struct qlcnic_adapter_stats {
u64 null_rxbuf;
u64 rx_dma_map_error;
u64 tx_dma_map_error;
+ u64 mac_filter_limit_overrun;
+ u64 spurious_intr;
};
/*
@@ -548,12 +550,15 @@ struct qlcnic_host_sds_ring {
} ____cacheline_internodealigned_in_smp;
struct qlcnic_host_tx_ring {
+ int irq;
u16 ctx_id;
u32 producer;
u32 sw_consumer;
u32 num_desc;
void __iomem *crb_cmd_producer;
struct cmd_desc_type0 *desc_head;
+ struct qlcnic_adapter *adapter;
+ struct napi_struct napi;
struct qlcnic_cmd_buffer *cmd_buf_arr;
__le32 *hw_consumer;
@@ -1000,7 +1005,8 @@ struct qlcnic_filter {
struct qlcnic_filter_hash {
struct hlist_head *fhead;
u8 fnum;
- u8 fmax;
+ u16 fmax;
+ u16 fbucket_size;
};
struct qlcnic_adapter {
@@ -1510,8 +1516,8 @@ void qlcnic_release_tx_buffers(struct qlcnic_adapter *adapter);
int qlcnic_check_fw_status(struct qlcnic_adapter *adapter);
void qlcnic_watchdog_task(struct work_struct *work);
-void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter,
- struct qlcnic_host_rds_ring *rds_ring);
+void qlcnic_post_rx_buffers(struct qlcnic_adapter *,
+ struct qlcnic_host_rds_ring *, u8);
int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max);
void qlcnic_set_multi(struct net_device *netdev);
void qlcnic_free_mac_list(struct qlcnic_adapter *adapter);
@@ -1532,7 +1538,6 @@ int qlcnic_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable);
int qlcnic_send_lro_cleanup(struct qlcnic_adapter *adapter);
void qlcnic_update_cmd_producer(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring);
-void qlcnic_fetch_mac(struct qlcnic_adapter *, u32, u32, u8, u8 *);
void qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring);
void qlcnic_clear_lb_mode(struct qlcnic_adapter *adapter);
int qlcnic_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode);
@@ -1569,8 +1574,19 @@ int qlcnic_get_eswitch_stats(struct qlcnic_adapter *, const u8, u8,
struct __qlcnic_esw_statistics *);
int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, u8, u8, u8);
int qlcnic_get_mac_stats(struct qlcnic_adapter *, struct qlcnic_mac_statistics *);
+
extern int qlcnic_config_tso;
+void qlcnic_fetch_mac(struct qlcnic_adapter *, u32, u32, u8, u8 *);
+int qlcnic_napi_add(struct qlcnic_adapter *, struct net_device *);
+void qlcnic_napi_del(struct qlcnic_adapter *adapter);
+void qlcnic_napi_enable(struct qlcnic_adapter *adapter);
+void qlcnic_napi_disable(struct qlcnic_adapter *adapter);
+int qlcnic_alloc_sds_rings(struct qlcnic_recv_context *, int);
+void qlcnic_free_sds_rings(struct qlcnic_recv_context *);
+void qlcnic_free_tx_rings(struct qlcnic_adapter *);
+int qlcnic_alloc_tx_rings(struct qlcnic_adapter *, struct net_device *);
+
/*
* QLOGIC Board information
*/
@@ -1617,6 +1633,20 @@ static inline u32 qlcnic_tx_avail(struct qlcnic_host_tx_ring *tx_ring)
tx_ring->producer;
}
+static inline void qlcnic_disable_int(struct qlcnic_host_sds_ring *sds_ring)
+{
+ writel(0, sds_ring->crb_intr_mask);
+}
+
+static inline void qlcnic_enable_int(struct qlcnic_host_sds_ring *sds_ring)
+{
+ struct qlcnic_adapter *adapter = sds_ring->adapter;
+
+ writel(0x1, sds_ring->crb_intr_mask);
+ if (!QLCNIC_IS_MSI_FAMILY(adapter))
+ writel(0xfbff, adapter->tgt_mask_reg);
+}
+
extern const struct ethtool_ops qlcnic_ethtool_ops;
extern const struct ethtool_ops qlcnic_ethtool_failed_ops;
@@ -1626,11 +1656,21 @@ struct qlcnic_nic_template {
int (*start_firmware) (struct qlcnic_adapter *);
};
+
+/* List of PCI device IDs */
+#define PCI_DEVICE_ID_QLOGIC_QLE824X 0x8020
+#define PCI_DEVICE_ID_QLOGIC_QLE834X 0x8030
+
+static inline bool qlcnic_82xx_check(struct qlcnic_adapter *adapter)
+{
+ unsigned short device = adapter->pdev->device;
+ return (device == PCI_DEVICE_ID_QLOGIC_QLE824X) ? true : false;
+}
+
#define QLCDB(adapter, lvl, _fmt, _args...) do { \
if (NETIF_MSG_##lvl & adapter->ahw->msg_enable) \
printk(KERN_INFO "%s: %s: " _fmt, \
dev_name(&adapter->pdev->dev), \
__func__, ##_args); \
} while (0)
-
#endif /* __QLCNIC_H_ */
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c
index 18f28b2..cd7a1fe 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c
@@ -25,10 +25,6 @@ static unsigned int crb_addr_xform[QLCNIC_MAX_CRB_XFORM];
#define QLCNIC_ADDR_ERROR (0xffffffff)
-static void
-qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter,
- struct qlcnic_host_rds_ring *rds_ring);
-
static int
qlcnic_check_fw_hearbeat(struct qlcnic_adapter *adapter);
@@ -1319,640 +1315,9 @@ next:
}
}
-
void
qlcnic_release_firmware(struct qlcnic_adapter *adapter)
{
release_firmware(adapter->fw);
adapter->fw = NULL;
}
-
-static void
-qlcnic_handle_linkevent(struct qlcnic_adapter *adapter,
- struct qlcnic_fw_msg *msg)
-{
- u32 cable_OUI;
- u16 cable_len;
- u16 link_speed;
- u8 link_status, module, duplex, autoneg;
- u8 lb_status = 0;
- struct net_device *netdev = adapter->netdev;
-
- adapter->ahw->has_link_events = 1;
-
- cable_OUI = msg->body[1] & 0xffffffff;
- cable_len = (msg->body[1] >> 32) & 0xffff;
- link_speed = (msg->body[1] >> 48) & 0xffff;
-
- link_status = msg->body[2] & 0xff;
- duplex = (msg->body[2] >> 16) & 0xff;
- autoneg = (msg->body[2] >> 24) & 0xff;
- lb_status = (msg->body[2] >> 32) & 0x3;
-
- module = (msg->body[2] >> 8) & 0xff;
- if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE)
- dev_info(&netdev->dev, "unsupported cable: OUI 0x%x, "
- "length %d\n", cable_OUI, cable_len);
- else if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLELEN)
- dev_info(&netdev->dev, "unsupported cable length %d\n",
- cable_len);
-
- if (!link_status && (lb_status == QLCNIC_ILB_MODE ||
- lb_status == QLCNIC_ELB_MODE))
- adapter->ahw->loopback_state |= QLCNIC_LINKEVENT;
-
- qlcnic_advert_link_change(adapter, link_status);
-
- if (duplex == LINKEVENT_FULL_DUPLEX)
- adapter->ahw->link_duplex = DUPLEX_FULL;
- else
- adapter->ahw->link_duplex = DUPLEX_HALF;
-
- adapter->ahw->module_type = module;
- adapter->ahw->link_autoneg = autoneg;
-
- if (link_status) {
- adapter->ahw->link_speed = link_speed;
- } else {
- adapter->ahw->link_speed = SPEED_UNKNOWN;
- adapter->ahw->link_duplex = DUPLEX_UNKNOWN;
- }
-}
-
-static void
-qlcnic_handle_fw_message(int desc_cnt, int index,
- struct qlcnic_host_sds_ring *sds_ring)
-{
- struct qlcnic_fw_msg msg;
- struct status_desc *desc;
- struct qlcnic_adapter *adapter;
- struct device *dev;
- int i = 0, opcode, ret;
-
- while (desc_cnt > 0 && i < 8) {
- desc = &sds_ring->desc_head[index];
- msg.words[i++] = le64_to_cpu(desc->status_desc_data[0]);
- msg.words[i++] = le64_to_cpu(desc->status_desc_data[1]);
-
- index = get_next_index(index, sds_ring->num_desc);
- desc_cnt--;
- }
-
- adapter = sds_ring->adapter;
- dev = &adapter->pdev->dev;
- opcode = qlcnic_get_nic_msg_opcode(msg.body[0]);
-
- switch (opcode) {
- case QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE:
- qlcnic_handle_linkevent(adapter, &msg);
- break;
- case QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK:
- ret = (u32)(msg.body[1]);
- switch (ret) {
- case 0:
- adapter->ahw->loopback_state |= QLCNIC_LB_RESPONSE;
- break;
- case 1:
- dev_info(dev, "loopback already in progress\n");
- adapter->ahw->diag_cnt = -QLCNIC_TEST_IN_PROGRESS;
- break;
- case 2:
- dev_info(dev, "loopback cable is not connected\n");
- adapter->ahw->diag_cnt = -QLCNIC_LB_CABLE_NOT_CONN;
- break;
- default:
- dev_info(dev, "loopback configure request failed,"
- " ret %x\n", ret);
- adapter->ahw->diag_cnt = -QLCNIC_UNDEFINED_ERROR;
- break;
- }
- break;
- default:
- break;
- }
-}
-
-static int
-qlcnic_alloc_rx_skb(struct qlcnic_adapter *adapter,
- struct qlcnic_host_rds_ring *rds_ring,
- struct qlcnic_rx_buffer *buffer)
-{
- struct sk_buff *skb;
- dma_addr_t dma;
- struct pci_dev *pdev = adapter->pdev;
-
- skb = netdev_alloc_skb(adapter->netdev, rds_ring->skb_size);
- if (!skb) {
- adapter->stats.skb_alloc_failure++;
- return -ENOMEM;
- }
-
- skb_reserve(skb, NET_IP_ALIGN);
-
- dma = pci_map_single(pdev, skb->data,
- rds_ring->dma_size, PCI_DMA_FROMDEVICE);
-
- if (pci_dma_mapping_error(pdev, dma)) {
- adapter->stats.rx_dma_map_error++;
- dev_kfree_skb_any(skb);
- return -ENOMEM;
- }
-
- buffer->skb = skb;
- buffer->dma = dma;
-
- return 0;
-}
-
-static struct sk_buff *qlcnic_process_rxbuf(struct qlcnic_adapter *adapter,
- struct qlcnic_host_rds_ring *rds_ring, u16 index, u16 cksum)
-{
- struct qlcnic_rx_buffer *buffer;
- struct sk_buff *skb;
-
- buffer = &rds_ring->rx_buf_arr[index];
-
- if (unlikely(buffer->skb == NULL)) {
- WARN_ON(1);
- return NULL;
- }
-
- pci_unmap_single(adapter->pdev, buffer->dma, rds_ring->dma_size,
- PCI_DMA_FROMDEVICE);
-
- skb = buffer->skb;
-
- if (likely((adapter->netdev->features & NETIF_F_RXCSUM) &&
- (cksum == STATUS_CKSUM_OK || cksum == STATUS_CKSUM_LOOP))) {
- adapter->stats.csummed++;
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- } else {
- skb_checksum_none_assert(skb);
- }
-
- buffer->skb = NULL;
-
- return skb;
-}
-
-static inline int
-qlcnic_check_rx_tagging(struct qlcnic_adapter *adapter, struct sk_buff *skb,
- u16 *vlan_tag)
-{
- struct ethhdr *eth_hdr;
-
- if (!__vlan_get_tag(skb, vlan_tag)) {
- eth_hdr = (struct ethhdr *) skb->data;
- memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2);
- skb_pull(skb, VLAN_HLEN);
- }
- if (!adapter->pvid)
- return 0;
-
- if (*vlan_tag == adapter->pvid) {
- /* Outer vlan tag. Packet should follow non-vlan path */
- *vlan_tag = 0xffff;
- return 0;
- }
- if (adapter->flags & QLCNIC_TAGGING_ENABLED)
- return 0;
-
- return -EINVAL;
-}
-
-static struct qlcnic_rx_buffer *
-qlcnic_process_rcv(struct qlcnic_adapter *adapter,
- struct qlcnic_host_sds_ring *sds_ring,
- int ring, u64 sts_data0)
-{
- struct net_device *netdev = adapter->netdev;
- struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
- struct qlcnic_rx_buffer *buffer;
- struct sk_buff *skb;
- struct qlcnic_host_rds_ring *rds_ring;
- int index, length, cksum, pkt_offset;
- u16 vid = 0xffff;
-
- if (unlikely(ring >= adapter->max_rds_rings))
- return NULL;
-
- rds_ring = &recv_ctx->rds_rings[ring];
-
- index = qlcnic_get_sts_refhandle(sts_data0);
- if (unlikely(index >= rds_ring->num_desc))
- return NULL;
-
- buffer = &rds_ring->rx_buf_arr[index];
-
- length = qlcnic_get_sts_totallength(sts_data0);
- cksum = qlcnic_get_sts_status(sts_data0);
- pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0);
-
- skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum);
- if (!skb)
- return buffer;
-
- if (length > rds_ring->skb_size)
- skb_put(skb, rds_ring->skb_size);
- else
- skb_put(skb, length);
-
- if (pkt_offset)
- skb_pull(skb, pkt_offset);
-
- if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) {
- adapter->stats.rxdropped++;
- dev_kfree_skb(skb);
- return buffer;
- }
-
- skb->protocol = eth_type_trans(skb, netdev);
-
- if (vid != 0xffff)
- __vlan_hwaccel_put_tag(skb, vid);
-
- napi_gro_receive(&sds_ring->napi, skb);
-
- adapter->stats.rx_pkts++;
- adapter->stats.rxbytes += length;
-
- return buffer;
-}
-
-#define QLC_TCP_HDR_SIZE 20
-#define QLC_TCP_TS_OPTION_SIZE 12
-#define QLC_TCP_TS_HDR_SIZE (QLC_TCP_HDR_SIZE + QLC_TCP_TS_OPTION_SIZE)
-
-static struct qlcnic_rx_buffer *
-qlcnic_process_lro(struct qlcnic_adapter *adapter,
- struct qlcnic_host_sds_ring *sds_ring,
- int ring, u64 sts_data0, u64 sts_data1)
-{
- struct net_device *netdev = adapter->netdev;
- struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
- struct qlcnic_rx_buffer *buffer;
- struct sk_buff *skb;
- struct qlcnic_host_rds_ring *rds_ring;
- struct iphdr *iph;
- struct tcphdr *th;
- bool push, timestamp;
- int l2_hdr_offset, l4_hdr_offset;
- int index;
- u16 lro_length, length, data_offset;
- u32 seq_number;
- u16 vid = 0xffff;
-
- if (unlikely(ring > adapter->max_rds_rings))
- return NULL;
-
- rds_ring = &recv_ctx->rds_rings[ring];
-
- index = qlcnic_get_lro_sts_refhandle(sts_data0);
- if (unlikely(index > rds_ring->num_desc))
- return NULL;
-
- buffer = &rds_ring->rx_buf_arr[index];
-
- timestamp = qlcnic_get_lro_sts_timestamp(sts_data0);
- lro_length = qlcnic_get_lro_sts_length(sts_data0);
- l2_hdr_offset = qlcnic_get_lro_sts_l2_hdr_offset(sts_data0);
- l4_hdr_offset = qlcnic_get_lro_sts_l4_hdr_offset(sts_data0);
- push = qlcnic_get_lro_sts_push_flag(sts_data0);
- seq_number = qlcnic_get_lro_sts_seq_number(sts_data1);
-
- skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK);
- if (!skb)
- return buffer;
-
- if (timestamp)
- data_offset = l4_hdr_offset + QLC_TCP_TS_HDR_SIZE;
- else
- data_offset = l4_hdr_offset + QLC_TCP_HDR_SIZE;
-
- skb_put(skb, lro_length + data_offset);
-
- skb_pull(skb, l2_hdr_offset);
-
- if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) {
- adapter->stats.rxdropped++;
- dev_kfree_skb(skb);
- return buffer;
- }
-
- skb->protocol = eth_type_trans(skb, netdev);
-
- iph = (struct iphdr *)skb->data;
- th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
-
- length = (iph->ihl << 2) + (th->doff << 2) + lro_length;
- iph->tot_len = htons(length);
- iph->check = 0;
- iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
- th->psh = push;
- th->seq = htonl(seq_number);
-
- length = skb->len;
-
- if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP)
- skb_shinfo(skb)->gso_size = qlcnic_get_lro_sts_mss(sts_data1);
-
- if (vid != 0xffff)
- __vlan_hwaccel_put_tag(skb, vid);
- netif_receive_skb(skb);
-
- adapter->stats.lro_pkts++;
- adapter->stats.lrobytes += length;
-
- return buffer;
-}
-
-int
-qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max)
-{
- struct qlcnic_adapter *adapter = sds_ring->adapter;
- struct list_head *cur;
- struct status_desc *desc;
- struct qlcnic_rx_buffer *rxbuf;
- u64 sts_data0, sts_data1;
-
- int count = 0;
- int opcode, ring, desc_cnt;
- u32 consumer = sds_ring->consumer;
-
- while (count < max) {
- desc = &sds_ring->desc_head[consumer];
- sts_data0 = le64_to_cpu(desc->status_desc_data[0]);
-
- if (!(sts_data0 & STATUS_OWNER_HOST))
- break;
-
- desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0);
- opcode = qlcnic_get_sts_opcode(sts_data0);
-
- switch (opcode) {
- case QLCNIC_RXPKT_DESC:
- case QLCNIC_OLD_RXPKT_DESC:
- case QLCNIC_SYN_OFFLOAD:
- ring = qlcnic_get_sts_type(sts_data0);
- rxbuf = qlcnic_process_rcv(adapter, sds_ring,
- ring, sts_data0);
- break;
- case QLCNIC_LRO_DESC:
- ring = qlcnic_get_lro_sts_type(sts_data0);
- sts_data1 = le64_to_cpu(desc->status_desc_data[1]);
- rxbuf = qlcnic_process_lro(adapter, sds_ring,
- ring, sts_data0, sts_data1);
- break;
- case QLCNIC_RESPONSE_DESC:
- qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring);
- default:
- goto skip;
- }
-
- WARN_ON(desc_cnt > 1);
-
- if (likely(rxbuf))
- list_add_tail(&rxbuf->list, &sds_ring->free_list[ring]);
- else
- adapter->stats.null_rxbuf++;
-
-skip:
- for (; desc_cnt > 0; desc_cnt--) {
- desc = &sds_ring->desc_head[consumer];
- desc->status_desc_data[0] =
- cpu_to_le64(STATUS_OWNER_PHANTOM);
- consumer = get_next_index(consumer, sds_ring->num_desc);
- }
- count++;
- }
-
- for (ring = 0; ring < adapter->max_rds_rings; ring++) {
- struct qlcnic_host_rds_ring *rds_ring =
- &adapter->recv_ctx->rds_rings[ring];
-
- if (!list_empty(&sds_ring->free_list[ring])) {
- list_for_each(cur, &sds_ring->free_list[ring]) {
- rxbuf = list_entry(cur,
- struct qlcnic_rx_buffer, list);
- qlcnic_alloc_rx_skb(adapter, rds_ring, rxbuf);
- }
- spin_lock(&rds_ring->lock);
- list_splice_tail_init(&sds_ring->free_list[ring],
- &rds_ring->free_list);
- spin_unlock(&rds_ring->lock);
- }
-
- qlcnic_post_rx_buffers_nodb(adapter, rds_ring);
- }
-
- if (count) {
- sds_ring->consumer = consumer;
- writel(consumer, sds_ring->crb_sts_consumer);
- }
-
- return count;
-}
-
-void
-qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter,
- struct qlcnic_host_rds_ring *rds_ring)
-{
- struct rcv_desc *pdesc;
- struct qlcnic_rx_buffer *buffer;
- int count = 0;
- u32 producer;
- struct list_head *head;
-
- producer = rds_ring->producer;
-
- head = &rds_ring->free_list;
- while (!list_empty(head)) {
-
- buffer = list_entry(head->next, struct qlcnic_rx_buffer, list);
-
- if (!buffer->skb) {
- if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer))
- break;
- }
-
- count++;
- list_del(&buffer->list);
-
- /* make a rcv descriptor */
- pdesc = &rds_ring->desc_head[producer];
- pdesc->addr_buffer = cpu_to_le64(buffer->dma);
- pdesc->reference_handle = cpu_to_le16(buffer->ref_handle);
- pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size);
-
- producer = get_next_index(producer, rds_ring->num_desc);
- }
-
- if (count) {
- rds_ring->producer = producer;
- writel((producer-1) & (rds_ring->num_desc-1),
- rds_ring->crb_rcv_producer);
- }
-}
-
-static void
-qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter,
- struct qlcnic_host_rds_ring *rds_ring)
-{
- struct rcv_desc *pdesc;
- struct qlcnic_rx_buffer *buffer;
- int count = 0;
- uint32_t producer;
- struct list_head *head;
-
- if (!spin_trylock(&rds_ring->lock))
- return;
-
- producer = rds_ring->producer;
-
- head = &rds_ring->free_list;
- while (!list_empty(head)) {
-
- buffer = list_entry(head->next, struct qlcnic_rx_buffer, list);
-
- if (!buffer->skb) {
- if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer))
- break;
- }
-
- count++;
- list_del(&buffer->list);
-
- /* make a rcv descriptor */
- pdesc = &rds_ring->desc_head[producer];
- pdesc->reference_handle = cpu_to_le16(buffer->ref_handle);
- pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size);
- pdesc->addr_buffer = cpu_to_le64(buffer->dma);
-
- producer = get_next_index(producer, rds_ring->num_desc);
- }
-
- if (count) {
- rds_ring->producer = producer;
- writel((producer - 1) & (rds_ring->num_desc - 1),
- rds_ring->crb_rcv_producer);
- }
- spin_unlock(&rds_ring->lock);
-}
-
-static void dump_skb(struct sk_buff *skb, struct qlcnic_adapter *adapter)
-{
- int i;
- unsigned char *data = skb->data;
-
- printk(KERN_INFO "\n");
- for (i = 0; i < skb->len; i++) {
- QLCDB(adapter, DRV, "%02x ", data[i]);
- if ((i & 0x0f) == 8)
- printk(KERN_INFO "\n");
- }
-}
-
-void qlcnic_process_rcv_diag(struct qlcnic_adapter *adapter,
- struct qlcnic_host_sds_ring *sds_ring,
- int ring, u64 sts_data0)
-{
- struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
- struct sk_buff *skb;
- struct qlcnic_host_rds_ring *rds_ring;
- int index, length, cksum, pkt_offset;
-
- if (unlikely(ring >= adapter->max_rds_rings))
- return;
-
- rds_ring = &recv_ctx->rds_rings[ring];
-
- index = qlcnic_get_sts_refhandle(sts_data0);
- length = qlcnic_get_sts_totallength(sts_data0);
- if (unlikely(index >= rds_ring->num_desc))
- return;
-
- cksum = qlcnic_get_sts_status(sts_data0);
- pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0);
-
- skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum);
- if (!skb)
- return;
-
- if (length > rds_ring->skb_size)
- skb_put(skb, rds_ring->skb_size);
- else
- skb_put(skb, length);
-
- if (pkt_offset)
- skb_pull(skb, pkt_offset);
-
- if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr))
- adapter->ahw->diag_cnt++;
- else
- dump_skb(skb, adapter);
-
- dev_kfree_skb_any(skb);
- adapter->stats.rx_pkts++;
- adapter->stats.rxbytes += length;
-
- return;
-}
-
-void
-qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring)
-{
- struct qlcnic_adapter *adapter = sds_ring->adapter;
- struct status_desc *desc;
- u64 sts_data0;
- int ring, opcode, desc_cnt;
-
- u32 consumer = sds_ring->consumer;
-
- desc = &sds_ring->desc_head[consumer];
- sts_data0 = le64_to_cpu(desc->status_desc_data[0]);
-
- if (!(sts_data0 & STATUS_OWNER_HOST))
- return;
-
- desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0);
- opcode = qlcnic_get_sts_opcode(sts_data0);
- switch (opcode) {
- case QLCNIC_RESPONSE_DESC:
- qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring);
- break;
- default:
- ring = qlcnic_get_sts_type(sts_data0);
- qlcnic_process_rcv_diag(adapter, sds_ring, ring, sts_data0);
- break;
- }
-
- for (; desc_cnt > 0; desc_cnt--) {
- desc = &sds_ring->desc_head[consumer];
- desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM);
- consumer = get_next_index(consumer, sds_ring->num_desc);
- }
-
- sds_ring->consumer = consumer;
- writel(consumer, sds_ring->crb_sts_consumer);
-}
-
-void
-qlcnic_fetch_mac(struct qlcnic_adapter *adapter, u32 off1, u32 off2,
- u8 alt_mac, u8 *mac)
-{
- u32 mac_low, mac_high;
- int i;
-
- mac_low = off1;
- mac_high = off2;
-
- if (alt_mac) {
- mac_low |= (mac_low >> 16) | (mac_high << 16);
- mac_high >>= 16;
- }
-
- for (i = 0; i < 2; i++)
- mac[i] = (u8)(mac_high >> ((1 - i) * 8));
- for (i = 2; i < 6; i++)
- mac[i] = (u8)(mac_low >> ((5 - i) * 8));
-}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
new file mode 100644
index 0000000..aff714a
--- /dev/null
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
@@ -0,0 +1,1213 @@
+#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
+#include <net/ip.h>
+#include <linux/ipv6.h>
+
+#include "qlcnic.h"
+
+#define QLCNIC_TCP_HDR_SIZE 20
+#define QLCNIC_TCP_TS_OPTION_SIZE 12
+#define QLCNIC_FETCH_RING_ID(handle) ((handle) >> 63)
+#define QLCNIC_DESC_OWNER_FW cpu_to_le64(STATUS_OWNER_PHANTOM)
+
+#define QLCNIC_TCP_TS_HDR_SIZE (QLCNIC_TCP_HDR_SIZE + QLCNIC_TCP_TS_OPTION_SIZE)
+
+struct sk_buff *qlcnic_process_rxbuf(struct qlcnic_adapter *,
+ struct qlcnic_host_rds_ring *, u16, u16);
+
+static inline u8 qlcnic_mac_hash(u64 mac)
+{
+ return (u8)((mac & 0xff) ^ ((mac >> 40) & 0xff));
+}
+
+static inline u32 qlcnic_get_ref_handle(struct qlcnic_adapter *adapter,
+ u16 handle, u8 ring_id)
+{
+ if (adapter->pdev->device == PCI_DEVICE_ID_QLOGIC_QLE834X)
+ return handle | (ring_id << 15);
+ else
+ return handle;
+}
+
+void qlcnic_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr,
+ __le16 vlan_id)
+{
+ struct cmd_desc_type0 *hwdesc;
+ struct qlcnic_nic_req *req;
+ struct qlcnic_mac_req *mac_req;
+ struct qlcnic_vlan_req *vlan_req;
+ struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring;
+ u32 producer;
+ u64 word;
+
+ producer = tx_ring->producer;
+ hwdesc = &tx_ring->desc_head[tx_ring->producer];
+
+ req = (struct qlcnic_nic_req *)hwdesc;
+ memset(req, 0, sizeof(struct qlcnic_nic_req));
+ req->qhdr = cpu_to_le64(QLCNIC_REQUEST << 23);
+
+ word = QLCNIC_MAC_EVENT | ((u64)(adapter->portnum) << 16);
+ req->req_hdr = cpu_to_le64(word);
+
+ mac_req = (struct qlcnic_mac_req *)&(req->words[0]);
+ mac_req->op = vlan_id ? QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_ADD;
+ memcpy(mac_req->mac_addr, &uaddr, ETH_ALEN);
+
+ vlan_req = (struct qlcnic_vlan_req *)&req->words[1];
+ vlan_req->vlan_id = vlan_id;
+
+ tx_ring->producer = get_next_index(producer, tx_ring->num_desc);
+ smp_mb();
+}
+
+static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
+ struct qlcnic_host_tx_ring *tx_ring,
+ struct cmd_desc_type0 *first_desc,
+ struct sk_buff *skb)
+{
+ struct qlcnic_filter *fil, *tmp_fil;
+ struct hlist_node *tmp_hnode, *n;
+ struct hlist_head *head;
+ struct net_device *netdev = adapter->netdev;
+ struct ethhdr *phdr = (struct ethhdr *)(skb->data);
+ u64 src_addr = 0;
+ __le16 vlan_id = 0;
+ u8 hindex;
+
+ if (!compare_ether_addr(phdr->h_source, adapter->mac_addr))
+ return;
+
+ if (adapter->fhash.fnum >= adapter->fhash.fmax) {
+ adapter->stats.mac_filter_limit_overrun++;
+ netdev_info(netdev, "Can not add more than %d mac addresses\n",
+ adapter->fhash.fmax);
+ return;
+ }
+
+ /* Only NPAR capable devices support vlan based learning */
+ if (adapter->flags & QLCNIC_ESWITCH_ENABLED)
+ vlan_id = first_desc->vlan_TCI;
+ memcpy(&src_addr, phdr->h_source, ETH_ALEN);
+ hindex = qlcnic_mac_hash(src_addr) & (adapter->fhash.fbucket_size - 1);
+ head = &(adapter->fhash.fhead[hindex]);
+
+ hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) {
+ if (!memcmp(tmp_fil->faddr, &src_addr, ETH_ALEN) &&
+ tmp_fil->vlan_id == vlan_id) {
+ if (jiffies > (QLCNIC_READD_AGE * HZ + tmp_fil->ftime))
+ qlcnic_change_filter(adapter, &src_addr,
+ vlan_id);
+ tmp_fil->ftime = jiffies;
+ return;
+ }
+ }
+
+ fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC);
+ if (!fil)
+ return;
+
+ qlcnic_change_filter(adapter, &src_addr, vlan_id);
+ fil->ftime = jiffies;
+ fil->vlan_id = vlan_id;
+ memcpy(fil->faddr, &src_addr, ETH_ALEN);
+ spin_lock(&adapter->mac_learn_lock);
+ hlist_add_head(&(fil->fnode), head);
+ adapter->fhash.fnum++;
+ spin_unlock(&adapter->mac_learn_lock);
+}
+
+static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter,
+ struct cmd_desc_type0 *first_desc, struct sk_buff *skb)
+{
+ u8 opcode = 0, hdr_len = 0;
+ u16 flags = 0, vlan_tci = 0;
+ int copied, offset, copy_len;
+ struct cmd_desc_type0 *hwdesc;
+ struct vlan_ethhdr *vh;
+ struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring;
+ u16 protocol = ntohs(skb->protocol);
+ u32 producer = tx_ring->producer;
+
+ if (protocol == ETH_P_8021Q) {
+ vh = (struct vlan_ethhdr *)skb->data;
+ flags = FLAGS_VLAN_TAGGED;
+ vlan_tci = vh->h_vlan_TCI;
+ protocol = ntohs(vh->h_vlan_encapsulated_proto);
+ } else if (vlan_tx_tag_present(skb)) {
+ flags = FLAGS_VLAN_OOB;
+ vlan_tci = vlan_tx_tag_get(skb);
+ }
+ if (unlikely(adapter->pvid)) {
+ if (vlan_tci && !(adapter->flags & QLCNIC_TAGGING_ENABLED))
+ return -EIO;
+ if (vlan_tci && (adapter->flags & QLCNIC_TAGGING_ENABLED))
+ goto set_flags;
+
+ flags = FLAGS_VLAN_OOB;
+ vlan_tci = adapter->pvid;
+ }
+set_flags:
+ qlcnic_set_tx_vlan_tci(first_desc, vlan_tci);
+ qlcnic_set_tx_flags_opcode(first_desc, flags, opcode);
+
+ if (*(skb->data) & BIT_0) {
+ flags |= BIT_0;
+ memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN);
+ }
+ opcode = TX_ETHER_PKT;
+
+ if ((adapter->netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) &&
+ skb_shinfo(skb)->gso_size > 0) {
+ hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
+ first_desc->total_hdr_length = hdr_len;
+ opcode = (protocol == ETH_P_IPV6) ? TX_TCP_LSO6 : TX_TCP_LSO;
+ /*
+ * For LSO, we need to copy the MAC/IP/TCP headers into
+ * the descriptor ring
+ */
+ copied = 0;
+ offset = 2;
+ if (flags & FLAGS_VLAN_OOB) {
+ first_desc->total_hdr_length += VLAN_HLEN;
+ first_desc->tcp_hdr_offset = VLAN_HLEN;
+ first_desc->ip_hdr_offset = VLAN_HLEN;
+ /* Only in case of TSO on vlan device */
+ flags |= FLAGS_VLAN_TAGGED;
+
+ /* Create a TSO vlan header template for firmware */
+ hwdesc = &tx_ring->desc_head[producer];
+ tx_ring->cmd_buf_arr[producer].skb = NULL;
+
+ copy_len = min((int)sizeof(struct cmd_desc_type0) -
+ offset, hdr_len + VLAN_HLEN);
+ vh = (struct vlan_ethhdr *)((char *)hwdesc + 2);
+ skb_copy_from_linear_data(skb, vh, 12);
+ vh->h_vlan_proto = htons(ETH_P_8021Q);
+ vh->h_vlan_TCI = htons(vlan_tci);
+ skb_copy_from_linear_data_offset(skb, 12,
+ (char *)vh + 16,
+ copy_len - 16);
+ copied = copy_len - VLAN_HLEN;
+ offset = 0;
+ producer = get_next_index(producer, tx_ring->num_desc);
+ }
+ while (copied < hdr_len) {
+ copy_len = min((int)sizeof(struct cmd_desc_type0) -
+ offset, (hdr_len - copied));
+ hwdesc = &tx_ring->desc_head[producer];
+ tx_ring->cmd_buf_arr[producer].skb = NULL;
+ skb_copy_from_linear_data_offset(skb, copied,
+ (char *)hwdesc +
+ offset, copy_len);
+ copied += copy_len;
+ offset = 0;
+ producer = get_next_index(producer, tx_ring->num_desc);
+ }
+ tx_ring->producer = producer;
+ smp_mb();
+ adapter->stats.lso_frames++;
+ } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ u8 l4proto;
+
+ if (protocol == ETH_P_IP) {
+ l4proto = ip_hdr(skb)->protocol;
+ if (l4proto == IPPROTO_TCP)
+ opcode = TX_TCP_PKT;
+ else if (l4proto == IPPROTO_UDP)
+ opcode = TX_UDP_PKT;
+ } else if (protocol == ETH_P_IPV6) {
+ l4proto = ipv6_hdr(skb)->nexthdr;
+ if (l4proto == IPPROTO_TCP)
+ opcode = TX_TCPV6_PKT;
+ else if (l4proto == IPPROTO_UDP)
+ opcode = TX_UDPV6_PKT;
+ }
+ }
+ first_desc->tcp_hdr_offset += skb_transport_offset(skb);
+ first_desc->ip_hdr_offset += skb_network_offset(skb);
+ qlcnic_set_tx_flags_opcode(first_desc, flags, opcode);
+
+ return 0;
+}
+
+static int qlcnic_map_tx_skb(struct pci_dev *pdev, struct sk_buff *skb,
+ struct qlcnic_cmd_buffer *pbuf)
+{
+ struct qlcnic_skb_frag *nf;
+ struct skb_frag_struct *frag;
+ int i, nr_frags;
+ dma_addr_t map;
+
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ nf = &pbuf->frag_array[0];
+
+ map = pci_map_single(pdev, skb->data,
+ skb_headlen(skb), PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(pdev, map))
+ goto out_err;
+
+ nf->dma = map;
+ nf->length = skb_headlen(skb);
+
+ for (i = 0; i < nr_frags; i++) {
+ frag = &skb_shinfo(skb)->frags[i];
+ nf = &pbuf->frag_array[i + 1];
+
+ map = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&pdev->dev, map))
+ goto unwind;
+ nf->dma = map;
+ nf->length = frag->size;
+ }
+ return 0;
+
+unwind:
+ while (--i >= 0) {
+ nf = &pbuf->frag_array[i + 1];
+ pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE);
+ }
+
+ nf = &pbuf->frag_array[0];
+ pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE);
+
+out_err:
+ return -ENOMEM;
+}
+
+static void qlcnic_unmap_buffers(struct pci_dev *pdev, struct sk_buff *skb,
+ struct qlcnic_cmd_buffer *pbuf)
+{
+ struct qlcnic_skb_frag *nf = &pbuf->frag_array[0];
+ int nr_frags = skb_shinfo(skb)->nr_frags;
+ int i;
+
+ for (i = 0; i < nr_frags; i++) {
+ nf = &pbuf->frag_array[i + 1];
+ pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE);
+ }
+
+ nf = &pbuf->frag_array[0];
+ pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE);
+ pbuf->skb = NULL;
+}
+
+static inline void qlcnic_clear_cmddesc(u64 *desc)
+{
+ desc[0] = 0ULL;
+ desc[2] = 0ULL;
+ desc[7] = 0ULL;
+}
+
+netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring;
+ struct qlcnic_cmd_buffer *pbuf;
+ struct qlcnic_skb_frag *buffrag;
+ struct cmd_desc_type0 *hwdesc, *first_desc;
+ struct pci_dev *pdev;
+ struct ethhdr *phdr;
+ int frag_count, i, k, delta = 0;
+ u32 producer;
+ u32 num_txd = tx_ring->num_desc;
+
+ if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
+ netif_stop_queue(netdev);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (adapter->flags & QLCNIC_MACSPOOF) {
+ phdr = (struct ethhdr *)skb->data;
+ if (compare_ether_addr(phdr->h_source, adapter->mac_addr))
+ goto drop_packet;
+ }
+
+ frag_count = skb_shinfo(skb)->nr_frags + 1;
+ /* 14 frags supported for normal packet and
+ * 32 frags supported for TSO packet
+ */
+ if (!skb_is_gso(skb) && frag_count > QLCNIC_MAX_FRAGS_PER_TX) {
+ for (i = 0; i < (frag_count - QLCNIC_MAX_FRAGS_PER_TX); i++)
+ delta += skb_shinfo(skb)->frags[i].size;
+
+ if (!__pskb_pull_tail(skb, delta))
+ goto drop_packet;
+
+ frag_count = 1 + skb_shinfo(skb)->nr_frags;
+ }
+
+ if (unlikely(qlcnic_tx_avail(tx_ring) <= TX_STOP_THRESH)) {
+ netif_stop_queue(netdev);
+ if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) {
+ netif_start_queue(netdev);
+ } else {
+ adapter->stats.xmit_off++;
+ return NETDEV_TX_BUSY;
+ }
+ }
+
+ producer = tx_ring->producer;
+ pbuf = &tx_ring->cmd_buf_arr[producer];
+ pdev = adapter->pdev;
+ first_desc = &tx_ring->desc_head[producer];
+ hwdesc = &tx_ring->desc_head[producer];
+ qlcnic_clear_cmddesc((u64 *)hwdesc);
+ if (qlcnic_map_tx_skb(pdev, skb, pbuf)) {
+ adapter->stats.tx_dma_map_error++;
+ goto drop_packet;
+ }
+
+ pbuf->skb = skb;
+ pbuf->frag_count = frag_count;
+ qlcnic_set_tx_frags_len(first_desc, frag_count, skb->len);
+ qlcnic_set_tx_port(first_desc, adapter->portnum);
+
+ for (i = 0; i < frag_count; i++) {
+ k = i % 4;
+ if ((k == 0) && (i > 0)) {
+ /* move to next desc */
+ producer = get_next_index(producer, num_txd);
+ hwdesc = &tx_ring->desc_head[producer];
+ qlcnic_clear_cmddesc((u64 *)hwdesc);
+ tx_ring->cmd_buf_arr[producer].skb = NULL;
+ }
+
+ buffrag = &pbuf->frag_array[i];
+ hwdesc->buffer_length[k] = cpu_to_le16(buffrag->length);
+ switch (k) {
+ case 0:
+ hwdesc->addr_buffer1 = cpu_to_le64(buffrag->dma);
+ break;
+ case 1:
+ hwdesc->addr_buffer2 = cpu_to_le64(buffrag->dma);
+ break;
+ case 2:
+ hwdesc->addr_buffer3 = cpu_to_le64(buffrag->dma);
+ break;
+ case 3:
+ hwdesc->addr_buffer4 = cpu_to_le64(buffrag->dma);
+ break;
+ }
+ }
+ tx_ring->producer = get_next_index(producer, num_txd);
+ smp_mb();
+
+ if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb)))
+ goto unwind_buff;
+
+ if (adapter->mac_learn)
+ qlcnic_send_filter(adapter, tx_ring, first_desc, skb);
+
+ adapter->stats.txbytes += skb->len;
+ adapter->stats.xmitcalled++;
+ qlcnic_update_cmd_producer(adapter, tx_ring);
+
+ return NETDEV_TX_OK;
+
+unwind_buff:
+ qlcnic_unmap_buffers(pdev, skb, pbuf);
+drop_packet:
+ adapter->stats.txdropped++;
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+}
+
+void qlcnic_advert_link_change(struct qlcnic_adapter *adapter, int linkup)
+{
+ struct net_device *netdev = adapter->netdev;
+ if (adapter->ahw->linkup && !linkup) {
+ netdev_info(netdev, "NIC Link is down\n");
+ adapter->ahw->linkup = 0;
+ if (netif_running(netdev)) {
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+ }
+ } else if (!adapter->ahw->linkup && linkup) {
+ netdev_info(netdev, "NIC Link is up\n");
+ adapter->ahw->linkup = 1;
+ if (netif_running(netdev)) {
+ netif_carrier_on(netdev);
+ netif_wake_queue(netdev);
+ }
+ }
+}
+
+static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter,
+ struct qlcnic_host_tx_ring *tx_ring,
+ int budget)
+{
+ u32 sw_consumer, hw_consumer;
+ int i, done, count = 0;
+ struct qlcnic_cmd_buffer *buffer;
+ struct pci_dev *pdev = adapter->pdev;
+ struct net_device *netdev = adapter->netdev;
+ struct qlcnic_skb_frag *frag;
+
+ if (!spin_trylock(&adapter->tx_clean_lock))
+ return 1;
+
+ sw_consumer = tx_ring->sw_consumer;
+ hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));
+
+ while (sw_consumer != hw_consumer) {
+ buffer = &tx_ring->cmd_buf_arr[sw_consumer];
+ if (buffer->skb) {
+ frag = &buffer->frag_array[0];
+ pci_unmap_single(pdev, frag->dma, frag->length,
+ PCI_DMA_TODEVICE);
+ frag->dma = 0ULL;
+ for (i = 1; i < buffer->frag_count; i++) {
+ frag++;
+ pci_unmap_page(pdev, frag->dma, frag->length,
+ PCI_DMA_TODEVICE);
+ frag->dma = 0ULL;
+ }
+ adapter->stats.xmitfinished++;
+ dev_kfree_skb_any(buffer->skb);
+ buffer->skb = NULL;
+ }
+
+ sw_consumer = get_next_index(sw_consumer, tx_ring->num_desc);
+ if (++count >= budget)
+ break;
+ }
+
+ if (count && netif_running(netdev)) {
+ tx_ring->sw_consumer = sw_consumer;
+ smp_mb();
+ if (netif_queue_stopped(netdev) && netif_carrier_ok(netdev)) {
+ if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) {
+ netif_wake_queue(netdev);
+ adapter->stats.xmit_on++;
+ }
+ }
+ adapter->tx_timeo_cnt = 0;
+ }
+ /*
+ * If everything is freed up to consumer then check if the ring is full
+ * If the ring is full then check if more needs to be freed and
+ * schedule the call back again.
+ *
+ * This happens when there are 2 CPUs. One could be freeing and the
+ * other filling it. If the ring is full when we get out of here and
+ * the card has already interrupted the host then the host can miss the
+ * interrupt.
+ *
+ * There is still a possible race condition and the host could miss an
+ * interrupt. The card has to take care of this.
+ */
+ hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));
+ done = (sw_consumer == hw_consumer);
+ spin_unlock(&adapter->tx_clean_lock);
+
+ return done;
+}
+
+int qlcnic_poll(struct napi_struct *napi, int budget)
+{
+ int tx_complete, work_done;
+ struct qlcnic_host_sds_ring *sds_ring;
+ struct qlcnic_adapter *adapter;
+
+ sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
+ adapter = sds_ring->adapter;
+ tx_complete = qlcnic_process_cmd_ring(adapter, adapter->tx_ring,
+ budget);
+ work_done = qlcnic_process_rcv_ring(sds_ring, budget);
+ if ((work_done < budget) && tx_complete) {
+ napi_complete(&sds_ring->napi);
+ if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
+ qlcnic_enable_int(sds_ring);
+ }
+
+ return work_done;
+}
+
+int qlcnic_rx_poll(struct napi_struct *napi, int budget)
+{
+ int work_done;
+ struct qlcnic_host_sds_ring *sds_ring;
+ struct qlcnic_adapter *adapter;
+
+ sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
+ adapter = sds_ring->adapter;
+
+ work_done = qlcnic_process_rcv_ring(sds_ring, budget);
+ if (work_done < budget) {
+ napi_complete(&sds_ring->napi);
+ if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
+ qlcnic_enable_int(sds_ring);
+ }
+
+ return work_done;
+}
+
+static void qlcnic_handle_linkevent(struct qlcnic_adapter *adapter,
+ struct qlcnic_fw_msg *msg)
+{
+ u32 cable_OUI;
+ u16 cable_len, link_speed;
+ u8 link_status, module, duplex, autoneg, lb_status = 0;
+ struct net_device *netdev = adapter->netdev;
+
+ adapter->ahw->has_link_events = 1;
+ cable_OUI = msg->body[1] & 0xffffffff;
+ cable_len = (msg->body[1] >> 32) & 0xffff;
+ link_speed = (msg->body[1] >> 48) & 0xffff;
+
+ link_status = msg->body[2] & 0xff;
+ duplex = (msg->body[2] >> 16) & 0xff;
+ autoneg = (msg->body[2] >> 24) & 0xff;
+ lb_status = (msg->body[2] >> 32) & 0x3;
+
+ module = (msg->body[2] >> 8) & 0xff;
+ if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE)
+ dev_info(&netdev->dev,
+ "unsupported cable: OUI 0x%x, length %d\n",
+ cable_OUI, cable_len);
+ else if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLELEN)
+ dev_info(&netdev->dev, "unsupported cable length %d\n",
+ cable_len);
+
+ if (!link_status && (lb_status == QLCNIC_ILB_MODE ||
+ lb_status == QLCNIC_ELB_MODE))
+ adapter->ahw->loopback_state |= QLCNIC_LINKEVENT;
+
+ qlcnic_advert_link_change(adapter, link_status);
+
+ if (duplex == LINKEVENT_FULL_DUPLEX)
+ adapter->ahw->link_duplex = DUPLEX_FULL;
+ else
+ adapter->ahw->link_duplex = DUPLEX_HALF;
+
+ adapter->ahw->module_type = module;
+ adapter->ahw->link_autoneg = autoneg;
+ adapter->ahw->link_speed = link_speed;
+}
+
+static void qlcnic_handle_fw_message(int desc_cnt, int index,
+ struct qlcnic_host_sds_ring *sds_ring)
+{
+ struct qlcnic_fw_msg msg;
+ struct status_desc *desc;
+ struct qlcnic_adapter *adapter;
+ struct device *dev;
+ int i = 0, opcode, ret;
+
+ while (desc_cnt > 0 && i < 8) {
+ desc = &sds_ring->desc_head[index];
+ msg.words[i++] = le64_to_cpu(desc->status_desc_data[0]);
+ msg.words[i++] = le64_to_cpu(desc->status_desc_data[1]);
+
+ index = get_next_index(index, sds_ring->num_desc);
+ desc_cnt--;
+ }
+
+ adapter = sds_ring->adapter;
+ dev = &adapter->pdev->dev;
+ opcode = qlcnic_get_nic_msg_opcode(msg.body[0]);
+ switch (opcode) {
+ case QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE:
+ qlcnic_handle_linkevent(adapter, &msg);
+ break;
+ case QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK:
+ ret = (u32)(msg.body[1]);
+ switch (ret) {
+ case 0:
+ adapter->ahw->loopback_state |= QLCNIC_LB_RESPONSE;
+ break;
+ case 1:
+ dev_info(dev, "loopback already in progress\n");
+ adapter->ahw->diag_cnt = -QLCNIC_TEST_IN_PROGRESS;
+ break;
+ case 2:
+ dev_info(dev, "loopback cable is not connected\n");
+ adapter->ahw->diag_cnt = -QLCNIC_LB_CABLE_NOT_CONN;
+ break;
+ default:
+ dev_info(dev,
+ "configure loopback request failed, ret %x\n",
+ ret);
+ adapter->ahw->diag_cnt = -QLCNIC_UNDEFINED_ERROR;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int qlcnic_alloc_rx_skb(struct qlcnic_adapter *adapter,
+ struct qlcnic_host_rds_ring *rds_ring,
+ struct qlcnic_rx_buffer *buffer)
+{
+ struct sk_buff *skb;
+ dma_addr_t dma;
+ struct pci_dev *pdev = adapter->pdev;
+
+ skb = dev_alloc_skb(rds_ring->skb_size);
+ if (!skb) {
+ adapter->stats.skb_alloc_failure++;
+ return -ENOMEM;
+ }
+
+ skb_reserve(skb, NET_IP_ALIGN);
+ dma = pci_map_single(pdev, skb->data,
+ rds_ring->dma_size, PCI_DMA_FROMDEVICE);
+
+ if (pci_dma_mapping_error(pdev, dma)) {
+ adapter->stats.rx_dma_map_error++;
+ dev_kfree_skb_any(skb);
+ return -ENOMEM;
+ }
+
+ buffer->skb = skb;
+ buffer->dma = dma;
+
+ return 0;
+}
+
+struct sk_buff *qlcnic_process_rxbuf(struct qlcnic_adapter *adapter,
+ struct qlcnic_host_rds_ring *ring,
+ u16 index, u16 cksum)
+{
+ struct qlcnic_rx_buffer *buffer;
+ struct sk_buff *skb;
+
+ buffer = &ring->rx_buf_arr[index];
+ if (unlikely(buffer->skb == NULL)) {
+ WARN_ON(1);
+ return NULL;
+ }
+
+ pci_unmap_single(adapter->pdev, buffer->dma, ring->dma_size,
+ PCI_DMA_FROMDEVICE);
+
+ skb = buffer->skb;
+ if (likely((adapter->netdev->features & NETIF_F_RXCSUM) &&
+ (cksum == STATUS_CKSUM_OK || cksum == STATUS_CKSUM_LOOP))) {
+ adapter->stats.csummed++;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ } else {
+ skb_checksum_none_assert(skb);
+ }
+
+ skb->dev = adapter->netdev;
+ buffer->skb = NULL;
+
+ return skb;
+}
+
+inline int qlcnic_check_rx_tagging(struct qlcnic_adapter *adapter,
+ struct sk_buff *skb, u16 *vlan_tag)
+{
+ struct ethhdr *eth_hdr;
+
+ if (!__vlan_get_tag(skb, vlan_tag)) {
+ eth_hdr = (struct ethhdr *)skb->data;
+ memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2);
+ skb_pull(skb, VLAN_HLEN);
+ }
+ if (!adapter->pvid)
+ return 0;
+
+ if (*vlan_tag == adapter->pvid) {
+ /* Outer vlan tag. Packet should follow non-vlan path */
+ *vlan_tag = 0xffff;
+ return 0;
+ }
+ if (adapter->flags & QLCNIC_TAGGING_ENABLED)
+ return 0;
+
+ return -EINVAL;
+}
+
+static struct qlcnic_rx_buffer *
+qlcnic_process_rcv(struct qlcnic_adapter *adapter,
+ struct qlcnic_host_sds_ring *sds_ring, int ring,
+ u64 sts_data0)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
+ struct qlcnic_rx_buffer *buffer;
+ struct sk_buff *skb;
+ struct qlcnic_host_rds_ring *rds_ring;
+ int index, length, cksum, pkt_offset;
+ u16 vid = 0xffff;
+
+ if (unlikely(ring >= adapter->max_rds_rings))
+ return NULL;
+
+ rds_ring = &recv_ctx->rds_rings[ring];
+
+ index = qlcnic_get_sts_refhandle(sts_data0);
+ if (unlikely(index >= rds_ring->num_desc))
+ return NULL;
+
+ buffer = &rds_ring->rx_buf_arr[index];
+
+ length = qlcnic_get_sts_totallength(sts_data0);
+ cksum = qlcnic_get_sts_status(sts_data0);
+ pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0);
+
+ skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum);
+ if (!skb)
+ return buffer;
+
+ if (length > rds_ring->skb_size)
+ skb_put(skb, rds_ring->skb_size);
+ else
+ skb_put(skb, length);
+
+ if (pkt_offset)
+ skb_pull(skb, pkt_offset);
+
+ if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) {
+ adapter->stats.rxdropped++;
+ dev_kfree_skb(skb);
+ return buffer;
+ }
+
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ if (vid != 0xffff)
+ __vlan_hwaccel_put_tag(skb, vid);
+
+ napi_gro_receive(&sds_ring->napi, skb);
+
+ adapter->stats.rx_pkts++;
+ adapter->stats.rxbytes += length;
+
+ return buffer;
+}
+
+static struct qlcnic_rx_buffer *
+qlcnic_process_lro(struct qlcnic_adapter *adapter,
+ struct qlcnic_host_sds_ring *sds_ring,
+ int ring, u64 sts_data0, u64 sts_data1)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
+ struct qlcnic_rx_buffer *buffer;
+ struct sk_buff *skb;
+ struct qlcnic_host_rds_ring *rds_ring;
+ struct iphdr *iph;
+ struct tcphdr *th;
+ bool push, timestamp;
+ int index, l2_hdr_offset, l4_hdr_offset;
+ u16 lro_length, length, data_offset, vid = 0xffff;
+ u32 seq_number;
+
+ if (unlikely(ring > adapter->max_rds_rings))
+ return NULL;
+
+ rds_ring = &recv_ctx->rds_rings[ring];
+ index = qlcnic_get_lro_sts_refhandle(sts_data0);
+
+ if (unlikely(index > rds_ring->num_desc))
+ return NULL;
+
+ buffer = &rds_ring->rx_buf_arr[index];
+ timestamp = qlcnic_get_lro_sts_timestamp(sts_data0);
+ lro_length = qlcnic_get_lro_sts_length(sts_data0);
+ l2_hdr_offset = qlcnic_get_lro_sts_l2_hdr_offset(sts_data0);
+ l4_hdr_offset = qlcnic_get_lro_sts_l4_hdr_offset(sts_data0);
+ push = qlcnic_get_lro_sts_push_flag(sts_data0);
+ seq_number = qlcnic_get_lro_sts_seq_number(sts_data1);
+
+ skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK);
+ if (!skb)
+ return buffer;
+
+ if (timestamp)
+ data_offset = l4_hdr_offset + QLCNIC_TCP_TS_HDR_SIZE;
+ else
+ data_offset = l4_hdr_offset + QLCNIC_TCP_HDR_SIZE;
+
+ skb_put(skb, lro_length + data_offset);
+ skb_pull(skb, l2_hdr_offset);
+
+ if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) {
+ adapter->stats.rxdropped++;
+ dev_kfree_skb(skb);
+ return buffer;
+ }
+
+ skb->protocol = eth_type_trans(skb, netdev);
+ iph = (struct iphdr *)skb->data;
+ th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
+ length = (iph->ihl << 2) + (th->doff << 2) + lro_length;
+ iph->tot_len = htons(length);
+ iph->check = 0;
+ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+ th->psh = push;
+ th->seq = htonl(seq_number);
+ length = skb->len;
+
+ if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP)
+ skb_shinfo(skb)->gso_size = qlcnic_get_lro_sts_mss(sts_data1);
+
+ if (vid != 0xffff)
+ __vlan_hwaccel_put_tag(skb, vid);
+ netif_receive_skb(skb);
+
+ adapter->stats.lro_pkts++;
+ adapter->stats.lrobytes += length;
+
+ return buffer;
+}
+
+int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max)
+{
+ struct qlcnic_host_rds_ring *rds_ring;
+ struct qlcnic_adapter *adapter = sds_ring->adapter;
+ struct list_head *cur;
+ struct status_desc *desc;
+ struct qlcnic_rx_buffer *rxbuf;
+ int opcode, desc_cnt, count = 0;
+ u64 sts_data0, sts_data1;
+ u8 ring;
+ u32 consumer = sds_ring->consumer;
+
+ while (count < max) {
+ desc = &sds_ring->desc_head[consumer];
+ sts_data0 = le64_to_cpu(desc->status_desc_data[0]);
+
+ if (!(sts_data0 & STATUS_OWNER_HOST))
+ break;
+
+ desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0);
+ opcode = qlcnic_get_sts_opcode(sts_data0);
+ switch (opcode) {
+ case QLCNIC_RXPKT_DESC:
+ case QLCNIC_OLD_RXPKT_DESC:
+ case QLCNIC_SYN_OFFLOAD:
+ ring = qlcnic_get_sts_type(sts_data0);
+ rxbuf = qlcnic_process_rcv(adapter, sds_ring, ring,
+ sts_data0);
+ break;
+ case QLCNIC_LRO_DESC:
+ ring = qlcnic_get_lro_sts_type(sts_data0);
+ sts_data1 = le64_to_cpu(desc->status_desc_data[1]);
+ rxbuf = qlcnic_process_lro(adapter, sds_ring, ring,
+ sts_data0, sts_data1);
+ break;
+ case QLCNIC_RESPONSE_DESC:
+ qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring);
+ default:
+ goto skip;
+ }
+ WARN_ON(desc_cnt > 1);
+
+ if (likely(rxbuf))
+ list_add_tail(&rxbuf->list, &sds_ring->free_list[ring]);
+ else
+ adapter->stats.null_rxbuf++;
+skip:
+ for (; desc_cnt > 0; desc_cnt--) {
+ desc = &sds_ring->desc_head[consumer];
+ desc->status_desc_data[0] = QLCNIC_DESC_OWNER_FW;
+ consumer = get_next_index(consumer, sds_ring->num_desc);
+ }
+ count++;
+ }
+
+ for (ring = 0; ring < adapter->max_rds_rings; ring++) {
+ rds_ring = &adapter->recv_ctx->rds_rings[ring];
+ if (!list_empty(&sds_ring->free_list[ring])) {
+ list_for_each(cur, &sds_ring->free_list[ring]) {
+ rxbuf = list_entry(cur, struct qlcnic_rx_buffer,
+ list);
+ qlcnic_alloc_rx_skb(adapter, rds_ring, rxbuf);
+ }
+ spin_lock(&rds_ring->lock);
+ list_splice_tail_init(&sds_ring->free_list[ring],
+ &rds_ring->free_list);
+ spin_unlock(&rds_ring->lock);
+ }
+ }
+
+ if (count) {
+ sds_ring->consumer = consumer;
+ writel(consumer, sds_ring->crb_sts_consumer);
+ }
+
+ return count;
+}
+
+void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter,
+ struct qlcnic_host_rds_ring *rds_ring, u8 ring_id)
+{
+ struct rcv_desc *pdesc;
+ struct qlcnic_rx_buffer *buffer;
+ int count = 0;
+ u32 producer, handle;
+ struct list_head *head;
+
+ producer = rds_ring->producer;
+ head = &rds_ring->free_list;
+
+ while (!list_empty(head)) {
+ buffer = list_entry(head->next, struct qlcnic_rx_buffer, list);
+ if (!buffer->skb) {
+ if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer))
+ break;
+ }
+
+ count++;
+ list_del(&buffer->list);
+
+ /* make a rcv descriptor */
+ pdesc = &rds_ring->desc_head[producer];
+ pdesc->addr_buffer = cpu_to_le64(buffer->dma);
+ handle = qlcnic_get_ref_handle(adapter, buffer->ref_handle,
+ ring_id);
+ pdesc->reference_handle = cpu_to_le16(handle);
+ pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size);
+ producer = get_next_index(producer, rds_ring->num_desc);
+ }
+
+ if (count) {
+ rds_ring->producer = producer;
+ writel((producer - 1) & (rds_ring->num_desc - 1),
+ rds_ring->crb_rcv_producer);
+ }
+}
+
+void qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter,
+ struct qlcnic_host_rds_ring *rds_ring,
+ u8 ring_id)
+{
+ struct rcv_desc *pdesc;
+ struct qlcnic_rx_buffer *buffer;
+ int count = 0;
+ uint32_t producer, handle;
+ struct list_head *head;
+
+ if (!spin_trylock(&rds_ring->lock))
+ return;
+
+ producer = rds_ring->producer;
+ head = &rds_ring->free_list;
+ while (!list_empty(head)) {
+ buffer = list_entry(head->next, struct qlcnic_rx_buffer, list);
+
+ if (!buffer->skb) {
+ if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer))
+ break;
+ }
+ count++;
+ list_del(&buffer->list);
+
+ /* make a rcv descriptor */
+ pdesc = &rds_ring->desc_head[producer];
+ handle = qlcnic_get_ref_handle(adapter,
+ buffer->ref_handle, ring_id);
+ pdesc->reference_handle = cpu_to_le16(handle);
+ pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size);
+ pdesc->addr_buffer = cpu_to_le64(buffer->dma);
+ producer = get_next_index(producer, rds_ring->num_desc);
+ }
+ if (count) {
+ rds_ring->producer = producer;
+ writel((producer - 1) & (rds_ring->num_desc - 1),
+ rds_ring->crb_rcv_producer);
+ }
+ spin_unlock(&rds_ring->lock);
+}
+
+static void dump_skb(struct sk_buff *skb, struct qlcnic_adapter *adapter)
+{
+ int i;
+ unsigned char *data = skb->data;
+
+ pr_info("\n");
+ for (i = 0; i < skb->len; i++) {
+ QLCDB(adapter, DRV, "%02x ", data[i]);
+ if ((i & 0x0f) == 8)
+ pr_info("\n");
+ }
+}
+
+static void qlcnic_process_rcv_diag(struct qlcnic_adapter *adapter,
+ struct qlcnic_host_sds_ring *sds_ring,
+ int ring, u64 sts_data0)
+{
+ struct sk_buff *skb;
+ struct qlcnic_host_rds_ring *rds_ring;
+ int index, length, cksum, pkt_offset;
+ struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
+
+ if (unlikely(ring >= adapter->max_rds_rings))
+ return;
+
+ rds_ring = &recv_ctx->rds_rings[ring];
+
+ index = qlcnic_get_sts_refhandle(sts_data0);
+ length = qlcnic_get_sts_totallength(sts_data0);
+ if (unlikely(index >= rds_ring->num_desc))
+ return;
+
+ cksum = qlcnic_get_sts_status(sts_data0);
+ pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0);
+
+ skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum);
+ if (!skb)
+ return;
+
+ if (length > rds_ring->skb_size)
+ skb_put(skb, rds_ring->skb_size);
+ else
+ skb_put(skb, length);
+
+ if (pkt_offset)
+ skb_pull(skb, pkt_offset);
+
+ if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr))
+ adapter->ahw->diag_cnt++;
+ else
+ dump_skb(skb, adapter);
+
+ dev_kfree_skb_any(skb);
+ adapter->stats.rx_pkts++;
+ adapter->stats.rxbytes += length;
+
+ return;
+}
+
+void qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring)
+{
+ struct qlcnic_adapter *adapter = sds_ring->adapter;
+ struct status_desc *desc;
+ u64 sts_data0;
+ int ring, opcode, desc_cnt;
+
+ u32 consumer = sds_ring->consumer;
+
+ desc = &sds_ring->desc_head[consumer];
+ sts_data0 = le64_to_cpu(desc->status_desc_data[0]);
+
+ if (!(sts_data0 & STATUS_OWNER_HOST))
+ return;
+
+ desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0);
+ opcode = qlcnic_get_sts_opcode(sts_data0);
+ switch (opcode) {
+ case QLCNIC_RESPONSE_DESC:
+ qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring);
+ break;
+ default:
+ ring = qlcnic_get_sts_type(sts_data0);
+ qlcnic_process_rcv_diag(adapter, sds_ring, ring, sts_data0);
+ break;
+ }
+
+ for (; desc_cnt > 0; desc_cnt--) {
+ desc = &sds_ring->desc_head[consumer];
+ desc->status_desc_data[0] = QLCNIC_DESC_OWNER_FW;
+ consumer = get_next_index(consumer, sds_ring->num_desc);
+ }
+
+ sds_ring->consumer = consumer;
+ writel(consumer, sds_ring->crb_sts_consumer);
+}
+
+void qlcnic_fetch_mac(struct qlcnic_adapter *adapter, u32 off1, u32 off2,
+ u8 alt_mac, u8 *mac)
+{
+ u32 mac_low, mac_high;
+ int i;
+
+ mac_low = off1;
+ mac_high = off2;
+
+ if (alt_mac) {
+ mac_low |= (mac_low >> 16) | (mac_high << 16);
+ mac_high >>= 16;
+ }
+
+ for (i = 0; i < 2; i++)
+ mac[i] = (u8)(mac_high >> ((1 - i) * 8));
+ for (i = 2; i < 6; i++)
+ mac[i] = (u8)(mac_low >> ((5 - i) * 8));
+}
+
+int qlcnic_napi_add(struct qlcnic_adapter *adapter, struct net_device *netdev)
+{
+ int ring, max_sds_rings;
+ struct qlcnic_host_sds_ring *sds_ring;
+ struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
+
+ if (qlcnic_alloc_sds_rings(recv_ctx, adapter->max_sds_rings))
+ return -ENOMEM;
+
+ max_sds_rings = adapter->max_sds_rings;
+
+ for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ sds_ring = &recv_ctx->sds_rings[ring];
+ if (ring == adapter->max_sds_rings - 1)
+ netif_napi_add(netdev, &sds_ring->napi, qlcnic_poll,
+ QLCNIC_NETDEV_WEIGHT / max_sds_rings);
+ else
+ netif_napi_add(netdev, &sds_ring->napi, qlcnic_rx_poll,
+ QLCNIC_NETDEV_WEIGHT * 2);
+ }
+
+ if (qlcnic_alloc_tx_rings(adapter, netdev)) {
+ qlcnic_free_sds_rings(recv_ctx);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void qlcnic_napi_del(struct qlcnic_adapter *adapter)
+{
+ int ring;
+ struct qlcnic_host_sds_ring *sds_ring;
+ struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
+
+ for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ sds_ring = &recv_ctx->sds_rings[ring];
+ netif_napi_del(&sds_ring->napi);
+ }
+
+ qlcnic_free_sds_rings(adapter->recv_ctx);
+ qlcnic_free_tx_rings(adapter);
+}
+
+void qlcnic_napi_enable(struct qlcnic_adapter *adapter)
+{
+ int ring;
+ struct qlcnic_host_sds_ring *sds_ring;
+ struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
+
+ if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
+ return;
+
+ for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ sds_ring = &recv_ctx->sds_rings[ring];
+ napi_enable(&sds_ring->napi);
+ qlcnic_enable_int(sds_ring);
+ }
+}
+
+void qlcnic_napi_disable(struct qlcnic_adapter *adapter)
+{
+ int ring;
+ struct qlcnic_host_sds_ring *sds_ring;
+ struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
+
+ if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
+ return;
+
+ for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ sds_ring = &recv_ctx->sds_rings[ring];
+ qlcnic_disable_int(sds_ring);
+ napi_synchronize(&sds_ring->napi);
+ napi_disable(&sds_ring->napi);
+ }
+}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 64dae83..5ee1ce0 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -66,8 +66,6 @@ static void qlcnic_fw_poll_work(struct work_struct *work);
static void qlcnic_schedule_work(struct qlcnic_adapter *adapter,
work_func_t func, int delay);
static void qlcnic_cancel_fw_work(struct qlcnic_adapter *adapter);
-static int qlcnic_poll(struct napi_struct *napi, int budget);
-static int qlcnic_rx_poll(struct napi_struct *napi, int budget);
#ifdef CONFIG_NET_POLL_CONTROLLER
static void qlcnic_poll_controller(struct net_device *netdev);
#endif
@@ -132,23 +130,7 @@ static const u32 msi_tgt_status[8] = {
static const
struct qlcnic_legacy_intr_set legacy_intr[] = QLCNIC_LEGACY_INTR_CONFIG;
-static inline void qlcnic_disable_int(struct qlcnic_host_sds_ring *sds_ring)
-{
- writel(0, sds_ring->crb_intr_mask);
-}
-
-static inline void qlcnic_enable_int(struct qlcnic_host_sds_ring *sds_ring)
-{
- struct qlcnic_adapter *adapter = sds_ring->adapter;
-
- writel(0x1, sds_ring->crb_intr_mask);
-
- if (!QLCNIC_IS_MSI_FAMILY(adapter))
- writel(0xfbff, adapter->tgt_mask_reg);
-}
-
-static int
-qlcnic_alloc_sds_rings(struct qlcnic_recv_context *recv_ctx, int count)
+int qlcnic_alloc_sds_rings(struct qlcnic_recv_context *recv_ctx, int count)
{
int size = sizeof(struct qlcnic_host_sds_ring) * count;
@@ -157,8 +139,7 @@ qlcnic_alloc_sds_rings(struct qlcnic_recv_context *recv_ctx, int count)
return recv_ctx->sds_rings == NULL;
}
-static void
-qlcnic_free_sds_rings(struct qlcnic_recv_context *recv_ctx)
+void qlcnic_free_sds_rings(struct qlcnic_recv_context *recv_ctx)
{
if (recv_ctx->sds_rings != NULL)
kfree(recv_ctx->sds_rings);
@@ -166,80 +147,6 @@ qlcnic_free_sds_rings(struct qlcnic_recv_context *recv_ctx)
recv_ctx->sds_rings = NULL;
}
-static int
-qlcnic_napi_add(struct qlcnic_adapter *adapter, struct net_device *netdev)
-{
- int ring;
- struct qlcnic_host_sds_ring *sds_ring;
- struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
-
- if (qlcnic_alloc_sds_rings(recv_ctx, adapter->max_sds_rings))
- return -ENOMEM;
-
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
- sds_ring = &recv_ctx->sds_rings[ring];
-
- if (ring == adapter->max_sds_rings - 1)
- netif_napi_add(netdev, &sds_ring->napi, qlcnic_poll,
- QLCNIC_NETDEV_WEIGHT/adapter->max_sds_rings);
- else
- netif_napi_add(netdev, &sds_ring->napi,
- qlcnic_rx_poll, QLCNIC_NETDEV_WEIGHT*2);
- }
-
- return 0;
-}
-
-static void
-qlcnic_napi_del(struct qlcnic_adapter *adapter)
-{
- int ring;
- struct qlcnic_host_sds_ring *sds_ring;
- struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
-
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
- sds_ring = &recv_ctx->sds_rings[ring];
- netif_napi_del(&sds_ring->napi);
- }
-
- qlcnic_free_sds_rings(adapter->recv_ctx);
-}
-
-static void
-qlcnic_napi_enable(struct qlcnic_adapter *adapter)
-{
- int ring;
- struct qlcnic_host_sds_ring *sds_ring;
- struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
-
- if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
- return;
-
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
- sds_ring = &recv_ctx->sds_rings[ring];
- napi_enable(&sds_ring->napi);
- qlcnic_enable_int(sds_ring);
- }
-}
-
-static void
-qlcnic_napi_disable(struct qlcnic_adapter *adapter)
-{
- int ring;
- struct qlcnic_host_sds_ring *sds_ring;
- struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
-
- if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
- return;
-
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
- sds_ring = &recv_ctx->sds_rings[ring];
- qlcnic_disable_int(sds_ring);
- napi_synchronize(&sds_ring->napi);
- napi_disable(&sds_ring->napi);
- }
-}
-
static void qlcnic_clear_stats(struct qlcnic_adapter *adapter)
{
memset(&adapter->stats, 0, sizeof(adapter->stats));
@@ -1136,8 +1043,6 @@ static int
__qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev)
{
int ring;
- u32 capab2;
-
struct qlcnic_host_rds_ring *rds_ring;
if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
@@ -1145,21 +1050,16 @@ __qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev)
if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
return 0;
+
if (qlcnic_set_eswitch_port_config(adapter))
return -EIO;
- if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) {
- capab2 = QLCRD32(adapter, CRB_FW_CAPABILITIES_2);
- if (capab2 & QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG)
- adapter->flags |= QLCNIC_FW_LRO_MSS_CAP;
- }
-
if (qlcnic_fw_create_ctx(adapter))
return -EIO;
for (ring = 0; ring < adapter->max_rds_rings; ring++) {
rds_ring = &adapter->recv_ctx->rds_rings[ring];
- qlcnic_post_rx_buffers(adapter, rds_ring);
+ qlcnic_post_rx_buffers(adapter, rds_ring, ring);
}
qlcnic_set_multi(netdev);
@@ -1177,10 +1077,10 @@ __qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev)
qlcnic_napi_enable(adapter);
- qlcnic_linkevent_request(adapter, 1);
-
adapter->ahw->reset_context = 0;
set_bit(__QLCNIC_DEV_UP, &adapter->state);
+
+ qlcnic_linkevent_request(adapter, 1);
return 0;
}
@@ -1410,13 +1310,15 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test)
for (ring = 0; ring < adapter->max_rds_rings; ring++) {
rds_ring = &adapter->recv_ctx->rds_rings[ring];
- qlcnic_post_rx_buffers(adapter, rds_ring);
+ qlcnic_post_rx_buffers(adapter, rds_ring, ring);
}
if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) {
for (ring = 0; ring < adapter->max_sds_rings; ring++) {
sds_ring = &adapter->recv_ctx->sds_rings[ring];
- qlcnic_enable_int(sds_ring);
+ if (qlcnic_82xx_check(adapter))
+ qlcnic_enable_int(sds_ring);
+
}
}
@@ -1559,6 +1461,54 @@ qlcnic_alloc_msix_entries(struct qlcnic_adapter *adapter, u16 count)
return -ENOMEM;
}
+void qlcnic_free_tx_rings(struct qlcnic_adapter *adapter)
+{
+ int ring;
+ struct qlcnic_host_tx_ring *tx_ring;
+
+ for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ tx_ring = &adapter->tx_ring[ring];
+ if (tx_ring && tx_ring->cmd_buf_arr != NULL) {
+ vfree(tx_ring->cmd_buf_arr);
+ tx_ring->cmd_buf_arr = NULL;
+ }
+ }
+ if (adapter->tx_ring != NULL)
+ kfree(adapter->tx_ring);
+}
+
+int qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter,
+ struct net_device *netdev)
+{
+ int ring, size;
+ struct qlcnic_host_tx_ring *tx_ring;
+ struct qlcnic_cmd_buffer *cmd_buf_arr;
+
+ size = adapter->max_drv_tx_rings * sizeof(struct qlcnic_host_tx_ring);
+ tx_ring = kzalloc(size, GFP_KERNEL);
+ if (tx_ring == NULL) {
+ dev_err(&netdev->dev, "failed to allocate tx rings\n");
+ return -ENOMEM;
+ }
+ adapter->tx_ring = tx_ring;
+
+ for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ tx_ring = &adapter->tx_ring[ring];
+ tx_ring->num_desc = adapter->num_txd;
+ tx_ring->txq = netdev_get_tx_queue(netdev, ring);
+ cmd_buf_arr = vzalloc(TX_BUFF_RINGSIZE(tx_ring));
+ if (cmd_buf_arr == NULL) {
+ dev_err(&netdev->dev,
+ "failed to allocate cmd buffer ring\n");
+ qlcnic_free_tx_rings(adapter);
+ return -ENOMEM;
+ }
+ memset(cmd_buf_arr, 0, TX_BUFF_RINGSIZE(tx_ring));
+ tx_ring->cmd_buf_arr = cmd_buf_arr;
+ }
+
+ return 0;
+}
static int __devinit
qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
@@ -1927,428 +1877,6 @@ static void qlcnic_free_lb_filters_mem(struct qlcnic_adapter *adapter)
adapter->fhash.fmax = 0;
}
-static void qlcnic_change_filter(struct qlcnic_adapter *adapter,
- u64 uaddr, __le16 vlan_id, struct qlcnic_host_tx_ring *tx_ring)
-{
- struct cmd_desc_type0 *hwdesc;
- struct qlcnic_nic_req *req;
- struct qlcnic_mac_req *mac_req;
- struct qlcnic_vlan_req *vlan_req;
- u32 producer;
- u64 word;
-
- producer = tx_ring->producer;
- hwdesc = &tx_ring->desc_head[tx_ring->producer];
-
- req = (struct qlcnic_nic_req *)hwdesc;
- memset(req, 0, sizeof(struct qlcnic_nic_req));
- req->qhdr = cpu_to_le64(QLCNIC_REQUEST << 23);
-
- word = QLCNIC_MAC_EVENT | ((u64)(adapter->portnum) << 16);
- req->req_hdr = cpu_to_le64(word);
-
- mac_req = (struct qlcnic_mac_req *)&(req->words[0]);
- mac_req->op = vlan_id ? QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_ADD;
- memcpy(mac_req->mac_addr, &uaddr, ETH_ALEN);
-
- vlan_req = (struct qlcnic_vlan_req *)&req->words[1];
- vlan_req->vlan_id = vlan_id;
-
- tx_ring->producer = get_next_index(producer, tx_ring->num_desc);
- smp_mb();
-}
-
-#define QLCNIC_MAC_HASH(MAC)\
- ((((MAC) & 0x70000) >> 0x10) | (((MAC) & 0x70000000000ULL) >> 0x25))
-
-static void
-qlcnic_send_filter(struct qlcnic_adapter *adapter,
- struct qlcnic_host_tx_ring *tx_ring,
- struct cmd_desc_type0 *first_desc,
- struct sk_buff *skb)
-{
- struct ethhdr *phdr = (struct ethhdr *)(skb->data);
- struct qlcnic_filter *fil, *tmp_fil;
- struct hlist_node *tmp_hnode, *n;
- struct hlist_head *head;
- u64 src_addr = 0;
- __le16 vlan_id = 0;
- u8 hindex;
-
- if (ether_addr_equal(phdr->h_source, adapter->mac_addr))
- return;
-
- if (adapter->fhash.fnum >= adapter->fhash.fmax)
- return;
-
- /* Only NPAR capable devices support vlan based learning*/
- if (adapter->flags & QLCNIC_ESWITCH_ENABLED)
- vlan_id = first_desc->vlan_TCI;
- memcpy(&src_addr, phdr->h_source, ETH_ALEN);
- hindex = QLCNIC_MAC_HASH(src_addr) & (QLCNIC_LB_MAX_FILTERS - 1);
- head = &(adapter->fhash.fhead[hindex]);
-
- hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) {
- if (!memcmp(tmp_fil->faddr, &src_addr, ETH_ALEN) &&
- tmp_fil->vlan_id == vlan_id) {
-
- if (jiffies >
- (QLCNIC_READD_AGE * HZ + tmp_fil->ftime))
- qlcnic_change_filter(adapter, src_addr, vlan_id,
- tx_ring);
- tmp_fil->ftime = jiffies;
- return;
- }
- }
-
- fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC);
- if (!fil)
- return;
-
- qlcnic_change_filter(adapter, src_addr, vlan_id, tx_ring);
-
- fil->ftime = jiffies;
- fil->vlan_id = vlan_id;
- memcpy(fil->faddr, &src_addr, ETH_ALEN);
- spin_lock(&adapter->mac_learn_lock);
- hlist_add_head(&(fil->fnode), head);
- adapter->fhash.fnum++;
- spin_unlock(&adapter->mac_learn_lock);
-}
-
-static int
-qlcnic_tx_pkt(struct qlcnic_adapter *adapter,
- struct cmd_desc_type0 *first_desc,
- struct sk_buff *skb)
-{
- u8 opcode = 0, hdr_len = 0;
- u16 flags = 0, vlan_tci = 0;
- int copied, offset, copy_len;
- struct cmd_desc_type0 *hwdesc;
- struct vlan_ethhdr *vh;
- struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring;
- u16 protocol = ntohs(skb->protocol);
- u32 producer = tx_ring->producer;
-
- if (protocol == ETH_P_8021Q) {
- vh = (struct vlan_ethhdr *)skb->data;
- flags = FLAGS_VLAN_TAGGED;
- vlan_tci = vh->h_vlan_TCI;
- protocol = ntohs(vh->h_vlan_encapsulated_proto);
- } else if (vlan_tx_tag_present(skb)) {
- flags = FLAGS_VLAN_OOB;
- vlan_tci = vlan_tx_tag_get(skb);
- }
- if (unlikely(adapter->pvid)) {
- if (vlan_tci && !(adapter->flags & QLCNIC_TAGGING_ENABLED))
- return -EIO;
- if (vlan_tci && (adapter->flags & QLCNIC_TAGGING_ENABLED))
- goto set_flags;
-
- flags = FLAGS_VLAN_OOB;
- vlan_tci = adapter->pvid;
- }
-set_flags:
- qlcnic_set_tx_vlan_tci(first_desc, vlan_tci);
- qlcnic_set_tx_flags_opcode(first_desc, flags, opcode);
-
- if (*(skb->data) & BIT_0) {
- flags |= BIT_0;
- memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN);
- }
- opcode = TX_ETHER_PKT;
- if ((adapter->netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) &&
- skb_shinfo(skb)->gso_size > 0) {
-
- hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
-
- first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
- first_desc->total_hdr_length = hdr_len;
-
- opcode = (protocol == ETH_P_IPV6) ? TX_TCP_LSO6 : TX_TCP_LSO;
-
- /* For LSO, we need to copy the MAC/IP/TCP headers into
- * the descriptor ring */
- copied = 0;
- offset = 2;
-
- if (flags & FLAGS_VLAN_OOB) {
- first_desc->total_hdr_length += VLAN_HLEN;
- first_desc->tcp_hdr_offset = VLAN_HLEN;
- first_desc->ip_hdr_offset = VLAN_HLEN;
- /* Only in case of TSO on vlan device */
- flags |= FLAGS_VLAN_TAGGED;
-
- /* Create a TSO vlan header template for firmware */
-
- hwdesc = &tx_ring->desc_head[producer];
- tx_ring->cmd_buf_arr[producer].skb = NULL;
-
- copy_len = min((int)sizeof(struct cmd_desc_type0) -
- offset, hdr_len + VLAN_HLEN);
-
- vh = (struct vlan_ethhdr *)((char *) hwdesc + 2);
- skb_copy_from_linear_data(skb, vh, 12);
- vh->h_vlan_proto = htons(ETH_P_8021Q);
- vh->h_vlan_TCI = htons(vlan_tci);
-
- skb_copy_from_linear_data_offset(skb, 12,
- (char *)vh + 16, copy_len - 16);
-
- copied = copy_len - VLAN_HLEN;
- offset = 0;
-
- producer = get_next_index(producer, tx_ring->num_desc);
- }
-
- while (copied < hdr_len) {
-
- copy_len = min((int)sizeof(struct cmd_desc_type0) -
- offset, (hdr_len - copied));
-
- hwdesc = &tx_ring->desc_head[producer];
- tx_ring->cmd_buf_arr[producer].skb = NULL;
-
- skb_copy_from_linear_data_offset(skb, copied,
- (char *) hwdesc + offset, copy_len);
-
- copied += copy_len;
- offset = 0;
-
- producer = get_next_index(producer, tx_ring->num_desc);
- }
-
- tx_ring->producer = producer;
- smp_mb();
- adapter->stats.lso_frames++;
-
- } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
- u8 l4proto;
-
- if (protocol == ETH_P_IP) {
- l4proto = ip_hdr(skb)->protocol;
-
- if (l4proto == IPPROTO_TCP)
- opcode = TX_TCP_PKT;
- else if (l4proto == IPPROTO_UDP)
- opcode = TX_UDP_PKT;
- } else if (protocol == ETH_P_IPV6) {
- l4proto = ipv6_hdr(skb)->nexthdr;
-
- if (l4proto == IPPROTO_TCP)
- opcode = TX_TCPV6_PKT;
- else if (l4proto == IPPROTO_UDP)
- opcode = TX_UDPV6_PKT;
- }
- }
- first_desc->tcp_hdr_offset += skb_transport_offset(skb);
- first_desc->ip_hdr_offset += skb_network_offset(skb);
- qlcnic_set_tx_flags_opcode(first_desc, flags, opcode);
-
- return 0;
-}
-
-static int
-qlcnic_map_tx_skb(struct pci_dev *pdev,
- struct sk_buff *skb, struct qlcnic_cmd_buffer *pbuf)
-{
- struct qlcnic_skb_frag *nf;
- struct skb_frag_struct *frag;
- int i, nr_frags;
- dma_addr_t map;
-
- nr_frags = skb_shinfo(skb)->nr_frags;
- nf = &pbuf->frag_array[0];
-
- map = pci_map_single(pdev, skb->data,
- skb_headlen(skb), PCI_DMA_TODEVICE);
- if (pci_dma_mapping_error(pdev, map))
- goto out_err;
-
- nf->dma = map;
- nf->length = skb_headlen(skb);
-
- for (i = 0; i < nr_frags; i++) {
- frag = &skb_shinfo(skb)->frags[i];
- nf = &pbuf->frag_array[i+1];
-
- map = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag),
- DMA_TO_DEVICE);
- if (dma_mapping_error(&pdev->dev, map))
- goto unwind;
-
- nf->dma = map;
- nf->length = skb_frag_size(frag);
- }
-
- return 0;
-
-unwind:
- while (--i >= 0) {
- nf = &pbuf->frag_array[i+1];
- pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE);
- }
-
- nf = &pbuf->frag_array[0];
- pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE);
-
-out_err:
- return -ENOMEM;
-}
-
-static void
-qlcnic_unmap_buffers(struct pci_dev *pdev, struct sk_buff *skb,
- struct qlcnic_cmd_buffer *pbuf)
-{
- struct qlcnic_skb_frag *nf = &pbuf->frag_array[0];
- int nr_frags = skb_shinfo(skb)->nr_frags;
- int i;
-
- for (i = 0; i < nr_frags; i++) {
- nf = &pbuf->frag_array[i+1];
- pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE);
- }
-
- nf = &pbuf->frag_array[0];
- pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE);
- pbuf->skb = NULL;
-}
-
-static inline void
-qlcnic_clear_cmddesc(u64 *desc)
-{
- desc[0] = 0ULL;
- desc[2] = 0ULL;
- desc[7] = 0ULL;
-}
-
-netdev_tx_t
-qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
-{
- struct qlcnic_adapter *adapter = netdev_priv(netdev);
- struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring;
- struct qlcnic_cmd_buffer *pbuf;
- struct qlcnic_skb_frag *buffrag;
- struct cmd_desc_type0 *hwdesc, *first_desc;
- struct pci_dev *pdev;
- struct ethhdr *phdr;
- int delta = 0;
- int i, k;
-
- u32 producer;
- int frag_count;
- u32 num_txd = tx_ring->num_desc;
-
- if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
- netif_stop_queue(netdev);
- return NETDEV_TX_BUSY;
- }
-
- if (adapter->flags & QLCNIC_MACSPOOF) {
- phdr = (struct ethhdr *)skb->data;
- if (!ether_addr_equal(phdr->h_source, adapter->mac_addr))
- goto drop_packet;
- }
-
- frag_count = skb_shinfo(skb)->nr_frags + 1;
- /* 14 frags supported for normal packet and
- * 32 frags supported for TSO packet
- */
- if (!skb_is_gso(skb) && frag_count > QLCNIC_MAX_FRAGS_PER_TX) {
-
- for (i = 0; i < (frag_count - QLCNIC_MAX_FRAGS_PER_TX); i++)
- delta += skb_frag_size(&skb_shinfo(skb)->frags[i]);
-
- if (!__pskb_pull_tail(skb, delta))
- goto drop_packet;
-
- frag_count = 1 + skb_shinfo(skb)->nr_frags;
- }
-
- if (unlikely(qlcnic_tx_avail(tx_ring) <= TX_STOP_THRESH)) {
- netif_stop_queue(netdev);
- if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH)
- netif_start_queue(netdev);
- else {
- adapter->stats.xmit_off++;
- return NETDEV_TX_BUSY;
- }
- }
-
- producer = tx_ring->producer;
- pbuf = &tx_ring->cmd_buf_arr[producer];
-
- pdev = adapter->pdev;
-
- first_desc = hwdesc = &tx_ring->desc_head[producer];
- qlcnic_clear_cmddesc((u64 *)hwdesc);
-
- if (qlcnic_map_tx_skb(pdev, skb, pbuf)) {
- adapter->stats.tx_dma_map_error++;
- goto drop_packet;
- }
-
- pbuf->skb = skb;
- pbuf->frag_count = frag_count;
-
- qlcnic_set_tx_frags_len(first_desc, frag_count, skb->len);
- qlcnic_set_tx_port(first_desc, adapter->portnum);
-
- for (i = 0; i < frag_count; i++) {
-
- k = i % 4;
-
- if ((k == 0) && (i > 0)) {
- /* move to next desc.*/
- producer = get_next_index(producer, num_txd);
- hwdesc = &tx_ring->desc_head[producer];
- qlcnic_clear_cmddesc((u64 *)hwdesc);
- tx_ring->cmd_buf_arr[producer].skb = NULL;
- }
-
- buffrag = &pbuf->frag_array[i];
-
- hwdesc->buffer_length[k] = cpu_to_le16(buffrag->length);
- switch (k) {
- case 0:
- hwdesc->addr_buffer1 = cpu_to_le64(buffrag->dma);
- break;
- case 1:
- hwdesc->addr_buffer2 = cpu_to_le64(buffrag->dma);
- break;
- case 2:
- hwdesc->addr_buffer3 = cpu_to_le64(buffrag->dma);
- break;
- case 3:
- hwdesc->addr_buffer4 = cpu_to_le64(buffrag->dma);
- break;
- }
- }
-
- tx_ring->producer = get_next_index(producer, num_txd);
- smp_mb();
-
- if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb)))
- goto unwind_buff;
-
- if (adapter->mac_learn)
- qlcnic_send_filter(adapter, tx_ring, first_desc, skb);
-
- adapter->stats.txbytes += skb->len;
- adapter->stats.xmitcalled++;
-
- qlcnic_update_cmd_producer(adapter, tx_ring);
-
- return NETDEV_TX_OK;
-
-unwind_buff:
- qlcnic_unmap_buffers(pdev, skb, pbuf);
-drop_packet:
- adapter->stats.txdropped++;
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
-}
-
static int qlcnic_check_temp(struct qlcnic_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
@@ -2385,27 +1913,6 @@ static int qlcnic_check_temp(struct qlcnic_adapter *adapter)
return rv;
}
-void qlcnic_advert_link_change(struct qlcnic_adapter *adapter, int linkup)
-{
- struct net_device *netdev = adapter->netdev;
-
- if (adapter->ahw->linkup && !linkup) {
- netdev_info(netdev, "NIC Link is down\n");
- adapter->ahw->linkup = 0;
- if (netif_running(netdev)) {
- netif_carrier_off(netdev);
- netif_stop_queue(netdev);
- }
- } else if (!adapter->ahw->linkup && linkup) {
- netdev_info(netdev, "NIC Link is up\n");
- adapter->ahw->linkup = 1;
- if (netif_running(netdev)) {
- netif_carrier_on(netdev);
- netif_wake_queue(netdev);
- }
- }
-}
-
static void qlcnic_tx_timeout(struct net_device *netdev)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
@@ -2512,122 +2019,6 @@ static irqreturn_t qlcnic_msix_intr(int irq, void *data)
return IRQ_HANDLED;
}
-static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter)
-{
- u32 sw_consumer, hw_consumer;
- int count = 0, i;
- struct qlcnic_cmd_buffer *buffer;
- struct pci_dev *pdev = adapter->pdev;
- struct net_device *netdev = adapter->netdev;
- struct qlcnic_skb_frag *frag;
- int done;
- struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring;
-
- if (!spin_trylock(&adapter->tx_clean_lock))
- return 1;
-
- sw_consumer = tx_ring->sw_consumer;
- hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));
-
- while (sw_consumer != hw_consumer) {
- buffer = &tx_ring->cmd_buf_arr[sw_consumer];
- if (buffer->skb) {
- frag = &buffer->frag_array[0];
- pci_unmap_single(pdev, frag->dma, frag->length,
- PCI_DMA_TODEVICE);
- frag->dma = 0ULL;
- for (i = 1; i < buffer->frag_count; i++) {
- frag++;
- pci_unmap_page(pdev, frag->dma, frag->length,
- PCI_DMA_TODEVICE);
- frag->dma = 0ULL;
- }
-
- adapter->stats.xmitfinished++;
- dev_kfree_skb_any(buffer->skb);
- buffer->skb = NULL;
- }
-
- sw_consumer = get_next_index(sw_consumer, tx_ring->num_desc);
- if (++count >= MAX_STATUS_HANDLE)
- break;
- }
-
- if (count && netif_running(netdev)) {
- tx_ring->sw_consumer = sw_consumer;
-
- smp_mb();
-
- if (netif_queue_stopped(netdev) && netif_carrier_ok(netdev)) {
- if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) {
- netif_wake_queue(netdev);
- adapter->stats.xmit_on++;
- }
- }
- adapter->tx_timeo_cnt = 0;
- }
- /*
- * If everything is freed up to consumer then check if the ring is full
- * If the ring is full then check if more needs to be freed and
- * schedule the call back again.
- *
- * This happens when there are 2 CPUs. One could be freeing and the
- * other filling it. If the ring is full when we get out of here and
- * the card has already interrupted the host then the host can miss the
- * interrupt.
- *
- * There is still a possible race condition and the host could miss an
- * interrupt. The card has to take care of this.
- */
- hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));
- done = (sw_consumer == hw_consumer);
- spin_unlock(&adapter->tx_clean_lock);
-
- return done;
-}
-
-static int qlcnic_poll(struct napi_struct *napi, int budget)
-{
- struct qlcnic_host_sds_ring *sds_ring =
- container_of(napi, struct qlcnic_host_sds_ring, napi);
-
- struct qlcnic_adapter *adapter = sds_ring->adapter;
-
- int tx_complete;
- int work_done;
-
- tx_complete = qlcnic_process_cmd_ring(adapter);
-
- work_done = qlcnic_process_rcv_ring(sds_ring, budget);
-
- if ((work_done < budget) && tx_complete) {
- napi_complete(&sds_ring->napi);
- if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
- qlcnic_enable_int(sds_ring);
- }
-
- return work_done;
-}
-
-static int qlcnic_rx_poll(struct napi_struct *napi, int budget)
-{
- struct qlcnic_host_sds_ring *sds_ring =
- container_of(napi, struct qlcnic_host_sds_ring, napi);
-
- struct qlcnic_adapter *adapter = sds_ring->adapter;
- int work_done;
-
- work_done = qlcnic_process_rcv_ring(sds_ring, budget);
-
- if (work_done < budget) {
- napi_complete(&sds_ring->napi);
- if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
- qlcnic_enable_int(sds_ring);
- }
-
- return work_done;
-}
-
#ifdef CONFIG_NET_POLL_CONTROLLER
static void qlcnic_poll_controller(struct net_device *netdev)
{
--
1.7.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox