Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH tip 0/9] tracing: attach eBPF programs to tracepoints/syscalls/kprobe
From: Namhyung Kim @ 2015-01-22  1:03 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Steven Rostedt, Ingo Molnar, Arnaldo Carvalho de Melo, Jiri Olsa,
	David S. Miller, Daniel Borkmann, Hannes Frederic Sowa,
	Brendan Gregg, Linux API, Network Development, LKML
In-Reply-To: <CAMEtUuxhjef+9QaFccfSyA3DbVJ3H6qP=eu-uZCkku1rVWViSQ@mail.gmail.com>

Hi Alexei,

On Fri, Jan 16, 2015 at 10:57:15AM -0800, Alexei Starovoitov wrote:
> On Fri, Jan 16, 2015 at 7:02 AM, Steven Rostedt <rostedt@goodmis.org> wrote:
> > One last thing. If the ebpf is used for anything but filtering, it
> > should go into the trigger file. The filtering is only a way to say if
> > the event should be recorded or not. But the trigger could do something
> > else (a printk, a stacktrace, etc).
> 
> it does way more than just filtering, but
> invoking program as a trigger is too slow.
> When program is called as soon as tracepoint fires,
> it can fetch other fields, evaluate them, printk some of them,
> optionally dump stack, aggregate into maps.
> We can let it call triggers too, so that user program will
> be able to enable/disable other events.
> I'm not against invoking programs as a trigger, but I don't
> see a use case for it. It's just too slow for production
> analytics that needs to act on huge number of events
> per second.

AFAIK a trigger can be fired before allocating a ring buffer if it
doesn't use the event record (i.e. has filter) or ->post_trigger bit
set (stacktrace).  Please see ftrace_trigger_soft_disabled().

This also makes it keeping events in the soft-disabled state.

Thanks,
Namhyung


> We must minimize the overhead between tracepoint
> firing and program executing, so that programs can
> be used on events like packet receive which will be
> in millions per second. Every nsec counts.
> For example:
> - raw dd if=/dev/zero of=/dev/null
>   does 760 MB/s (on my debug kernel)
> - echo 1 > events/syscalls/sys_enter_write/enable
>   drops it to 400 MB/s
> - echo "echo "count == 123 " > events/syscalls/sys_enter_write/filter
>   drops it even further down to 388 MB/s
> This slowdown is too high for this to be used on a live system.
> - tracex4 that computes histogram of sys_write sizes
>   and stores log2(count) into a map does 580 MB/s
>   This is still not great, but this slowdown is now usable
>   and we can work further on minimizing the overhead.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply

* Re: [PATCH net-next v13 5/5] openvswitch: Add support for unique flow IDs.
From: Joe Stringer @ 2015-01-22  0:45 UTC (permalink / raw)
  To: Pravin Shelar; +Cc: netdev, LKML, dev@openvswitch.org
In-Reply-To: <CALnjE+rWu7HsmjK9O0KEzkgngh7fc62aMp-ecMrAZP_3r8gtSw@mail.gmail.com>

On 21 January 2015 at 15:41, Pravin Shelar <pshelar@nicira.com> wrote:
> On Wed, Jan 21, 2015 at 11:29 AM, Joe Stringer <joestringer@nicira.com> wrote:
>> On 21 January 2015 at 10:31, Pravin Shelar <pshelar@nicira.com> wrote:
>>> On Tue, Jan 20, 2015 at 10:32 AM, Joe Stringer <joestringer@nicira.com> wrote:
>>>> Previously, flows were manipulated by userspace specifying a full,
>>>> unmasked flow key. This adds significant burden onto flow
>>>> serialization/deserialization, particularly when dumping flows.
>>>>
>>>> This patch adds an alternative way to refer to flows using a
>>>> variable-length "unique flow identifier" (UFID). At flow setup time,
>>>> userspace may specify a UFID for a flow, which is stored with the flow
>>>> and inserted into a separate table for lookup, in addition to the
>>>> standard flow table. Flows created using a UFID must be fetched or
>>>> deleted using the UFID.
>>>>
>>>> All flow dump operations may now be made more terse with OVS_UFID_F_*
>>>> flags. For example, the OVS_UFID_F_OMIT_KEY flag allows responses to
>>>> omit the flow key from a datapath operation if the flow has a
>>>> corresponding UFID. This significantly reduces the time spent assembling
>>>> and transacting netlink messages. With all OVS_UFID_F_OMIT_* flags
>>>> enabled, the datapath only returns the UFID and statistics for each flow
>>>> during flow dump, increasing ovs-vswitchd revalidator performance by 40%
>>>> or more.
>>>>
>>>> Signed-off-by: Joe Stringer <joestringer@nicira.com>
>>>
>>> Its almost ready. But I saw minor issues,
>>> few checkpatch.pl failures.
>>> in ovs_flow_cmd_new() we should use unmasked key to lookup in flow
>>> table for legacy case.
>>
>> Thanks for review, I can send out a fresh version soon. Should I
>> resend the whole series or is just a new version of this patch
>> sufficient?
>
> I think you have to send entire series. Dave will not apply series
> patches from different versions.

Thanks. I also realized there were checkpatch issues in the first
patch, so I resent the whole series anyway.

^ permalink raw reply

* [PATCH net-next v14 5/5] openvswitch: Add support for unique flow IDs.
From: Joe Stringer @ 2015-01-22  0:42 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel, dev, pshelar
In-Reply-To: <1421887372-56414-1-git-send-email-joestringer@nicira.com>

Previously, flows were manipulated by userspace specifying a full,
unmasked flow key. This adds significant burden onto flow
serialization/deserialization, particularly when dumping flows.

This patch adds an alternative way to refer to flows using a
variable-length "unique flow identifier" (UFID). At flow setup time,
userspace may specify a UFID for a flow, which is stored with the flow
and inserted into a separate table for lookup, in addition to the
standard flow table. Flows created using a UFID must be fetched or
deleted using the UFID.

All flow dump operations may now be made more terse with OVS_UFID_F_*
flags. For example, the OVS_UFID_F_OMIT_KEY flag allows responses to
omit the flow key from a datapath operation if the flow has a
corresponding UFID. This significantly reduces the time spent assembling
and transacting netlink messages. With all OVS_UFID_F_OMIT_* flags
enabled, the datapath only returns the UFID and statistics for each flow
during flow dump, increasing ovs-vswitchd revalidator performance by 40%
or more.

Signed-off-by: Joe Stringer <joestringer@nicira.com>
---
v14: Perform lookup using unmasked key in legacy case.
     Fix minor checkpatch.pl style violations.
v13: Embed sw_flow_id in sw_flow.
     Malloc unmasked_key for legacy case.
     Fix bug where key is double-serialized in legacy case.
v12: Merge unmasked_key, ufid into 'struct sw_flow_id'.
     Add identifier_is_{ufid,key}() helpers.
     Add should_fill_{key,mask,actions}() helpers.
     Revert rework of ovs_flow_cmd_set() -- requires key,acts.
     Streamline sw_flow_id copy/alloc.
     Use genlmsg_parse() to find ufid flags in dump.
     Handle new_flow case with same ufid, different flow key.
     Calculate exact message length in ovs_flow_cmd_msg_size().
     Limit UFID to between 1-16 octets.
     Check minimum length for UFID in flow_policy.
     Use jhash() for ufid_hash (arch_fast_hash() went away).
     Rebase.
v11: Separate UFID and unmasked key from sw_flow.
     Modify interface to remove nested UFID attributes.
     Only allow UFIDs between 1-256 octets.
     Move UFID nla fetch helpers to flow_netlink.h.
     Perform complete nlmsg_parsing in ovs_flow_cmd_dump().
     Check UFID table for flows with duplicate UFID at flow setup.
     Tidy up mask/key/ufid insertion into flow_table.
     Rebase.
v10: Ignore flow_key in requests if UFID is specified.
     Only allow UFID flows to be indexed by UFID.
     Only allow non-UFID flows to be indexed by unmasked flow key.
     Unite the unmasked_key and ufid+ufid_hash in 'struct sw_flow'.
     Don't periodically rehash the UFID table.
     Resize the UFID table independently from the flow table.
     Modify table_destroy() to iterate once and delete from both tables.
     Fix UFID memory leak in flow_free().
     Remove kernel-only UFIDs for non-UFID cases.
     Rename "OVS_UFID_F_SKIP_*" -> "OVS_UFID_F_OMIT_*"
     Update documentation.
     Rebase.
v9: No change.
v8: Rename UID -> UFID "unique flow identifier".
    Fix null dereference when adding flow without uid or mask.
    If UFID and not match are specified, and lookup fails, return ENOENT.
    Rebase.
v7: Remove OVS_DP_F_INDEX_BY_UID.
    Rework UID serialisation for variable-length UID.
    Log error if uid not specified and OVS_UID_F_SKIP_KEY is set.
    Rebase against "probe" logging changes.
v6: Fix documentation for supporting UIDs between 32-128 bits.
    Minor style fixes.
    Rebase.
v5: No change.
v4: Fix memory leaks.
    Log when triggering the older userspace issue above.
v3: Initial post.
---
 Documentation/networking/openvswitch.txt |   13 ++
 include/uapi/linux/openvswitch.h         |   20 +++
 net/openvswitch/datapath.c               |  207 ++++++++++++++++++++++--------
 net/openvswitch/flow.h                   |   28 +++-
 net/openvswitch/flow_netlink.c           |   68 +++++++++-
 net/openvswitch/flow_netlink.h           |    8 +-
 net/openvswitch/flow_table.c             |  187 ++++++++++++++++++++++-----
 net/openvswitch/flow_table.h             |    8 +-
 8 files changed, 448 insertions(+), 91 deletions(-)

diff --git a/Documentation/networking/openvswitch.txt b/Documentation/networking/openvswitch.txt
index 37c20ee..b3b9ac6 100644
--- a/Documentation/networking/openvswitch.txt
+++ b/Documentation/networking/openvswitch.txt
@@ -131,6 +131,19 @@ performs best-effort detection of overlapping wildcarded flows and may reject
 some but not all of them. However, this behavior may change in future versions.
 
 
+Unique flow identifiers
+-----------------------
+
+An alternative to using the original match portion of a key as the handle for
+flow identification is a unique flow identifier, or "UFID". UFIDs are optional
+for both the kernel and user space program.
+
+User space programs that support UFID are expected to provide it during flow
+setup in addition to the flow, then refer to the flow using the UFID for all
+future operations. The kernel is not required to index flows by the original
+flow key if a UFID is specified.
+
+
 Basic rule for evolving flow keys
 ---------------------------------
 
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index cd8d933..7a8785a 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -459,6 +459,14 @@ struct ovs_key_nd {
  * a wildcarded match. Omitting attribute is treated as wildcarding all
  * corresponding fields. Optional for all requests. If not present,
  * all flow key bits are exact match bits.
+ * @OVS_FLOW_ATTR_UFID: A value between 1-16 octets specifying a unique
+ * identifier for the flow. Causes the flow to be indexed by this value rather
+ * than the value of the %OVS_FLOW_ATTR_KEY attribute. Optional for all
+ * requests. Present in notifications if the flow was created with this
+ * attribute.
+ * @OVS_FLOW_ATTR_UFID_FLAGS: A 32-bit value of OR'd %OVS_UFID_F_*
+ * flags that provide alternative semantics for flow installation and
+ * retrieval. Optional for all requests.
  *
  * These attributes follow the &struct ovs_header within the Generic Netlink
  * payload for %OVS_FLOW_* commands.
@@ -474,12 +482,24 @@ enum ovs_flow_attr {
 	OVS_FLOW_ATTR_MASK,      /* Sequence of OVS_KEY_ATTR_* attributes. */
 	OVS_FLOW_ATTR_PROBE,     /* Flow operation is a feature probe, error
 				  * logging should be suppressed. */
+	OVS_FLOW_ATTR_UFID,      /* Variable length unique flow identifier. */
+	OVS_FLOW_ATTR_UFID_FLAGS,/* u32 of OVS_UFID_F_*. */
 	__OVS_FLOW_ATTR_MAX
 };
 
 #define OVS_FLOW_ATTR_MAX (__OVS_FLOW_ATTR_MAX - 1)
 
 /**
+ * Omit attributes for notifications.
+ *
+ * If a datapath request contains an %OVS_UFID_F_OMIT_* flag, then the datapath
+ * may omit the corresponding %OVS_FLOW_ATTR_* from the response.
+ */
+#define OVS_UFID_F_OMIT_KEY      (1 << 0)
+#define OVS_UFID_F_OMIT_MASK     (1 << 1)
+#define OVS_UFID_F_OMIT_ACTIONS  (1 << 2)
+
+/**
  * enum ovs_sample_attr - Attributes for %OVS_ACTION_ATTR_SAMPLE action.
  * @OVS_SAMPLE_ATTR_PROBABILITY: 32-bit fraction of packets to sample with
  * @OVS_ACTION_ATTR_SAMPLE.  A value of 0 samples no packets, a value of
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 257b975..ae5e77c 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -65,6 +65,8 @@ static struct genl_family dp_packet_genl_family;
 static struct genl_family dp_flow_genl_family;
 static struct genl_family dp_datapath_genl_family;
 
+static const struct nla_policy flow_policy[];
+
 static const struct genl_multicast_group ovs_dp_flow_multicast_group = {
 	.name = OVS_FLOW_MCGROUP,
 };
@@ -662,15 +664,48 @@ static void get_dp_stats(const struct datapath *dp, struct ovs_dp_stats *stats,
 	}
 }
 
-static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
+static bool should_fill_key(const struct sw_flow_id *sfid, uint32_t ufid_flags)
+{
+	return ovs_identifier_is_ufid(sfid) &&
+	       !(ufid_flags & OVS_UFID_F_OMIT_KEY);
+}
+
+static bool should_fill_mask(uint32_t ufid_flags)
+{
+	return !(ufid_flags & OVS_UFID_F_OMIT_MASK);
+}
+
+static bool should_fill_actions(uint32_t ufid_flags)
 {
-	return NLMSG_ALIGN(sizeof(struct ovs_header))
-		+ nla_total_size(ovs_key_attr_size()) /* OVS_FLOW_ATTR_KEY */
-		+ nla_total_size(ovs_key_attr_size()) /* OVS_FLOW_ATTR_MASK */
+	return !(ufid_flags & OVS_UFID_F_OMIT_ACTIONS);
+}
+
+static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts,
+				    const struct sw_flow_id *sfid,
+				    uint32_t ufid_flags)
+{
+	size_t len = NLMSG_ALIGN(sizeof(struct ovs_header));
+
+	/* OVS_FLOW_ATTR_UFID */
+	if (sfid && ovs_identifier_is_ufid(sfid))
+		len += nla_total_size(sfid->ufid_len);
+
+	/* OVS_FLOW_ATTR_KEY */
+	if (!sfid || should_fill_key(sfid, ufid_flags))
+		len += nla_total_size(ovs_key_attr_size());
+
+	/* OVS_FLOW_ATTR_MASK */
+	if (should_fill_mask(ufid_flags))
+		len += nla_total_size(ovs_key_attr_size());
+
+	/* OVS_FLOW_ATTR_ACTIONS */
+	if (should_fill_actions(ufid_flags))
+		len += nla_total_size(acts->actions_len);
+
+	return len
 		+ nla_total_size(sizeof(struct ovs_flow_stats)) /* OVS_FLOW_ATTR_STATS */
 		+ nla_total_size(1) /* OVS_FLOW_ATTR_TCP_FLAGS */
-		+ nla_total_size(8) /* OVS_FLOW_ATTR_USED */
-		+ nla_total_size(acts->actions_len); /* OVS_FLOW_ATTR_ACTIONS */
+		+ nla_total_size(8); /* OVS_FLOW_ATTR_USED */
 }
 
 /* Called with ovs_mutex or RCU read lock. */
@@ -741,7 +776,7 @@ static int ovs_flow_cmd_fill_actions(const struct sw_flow *flow,
 /* Called with ovs_mutex or RCU read lock. */
 static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex,
 				  struct sk_buff *skb, u32 portid,
-				  u32 seq, u32 flags, u8 cmd)
+				  u32 seq, u32 flags, u8 cmd, u32 ufid_flags)
 {
 	const int skb_orig_len = skb->len;
 	struct ovs_header *ovs_header;
@@ -754,21 +789,31 @@ static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex,
 
 	ovs_header->dp_ifindex = dp_ifindex;
 
-	err = ovs_nla_put_unmasked_key(flow, skb);
+	err = ovs_nla_put_identifier(flow, skb);
 	if (err)
 		goto error;
 
-	err = ovs_nla_put_mask(flow, skb);
-	if (err)
-		goto error;
+	if (should_fill_key(&flow->id, ufid_flags)) {
+		err = ovs_nla_put_masked_key(flow, skb);
+		if (err)
+			goto error;
+	}
+
+	if (should_fill_mask(ufid_flags)) {
+		err = ovs_nla_put_mask(flow, skb);
+		if (err)
+			goto error;
+	}
 
 	err = ovs_flow_cmd_fill_stats(flow, skb);
 	if (err)
 		goto error;
 
-	err = ovs_flow_cmd_fill_actions(flow, skb, skb_orig_len);
-	if (err)
-		goto error;
+	if (should_fill_actions(ufid_flags)) {
+		err = ovs_flow_cmd_fill_actions(flow, skb, skb_orig_len);
+		if (err)
+			goto error;
+	}
 
 	genlmsg_end(skb, ovs_header);
 	return 0;
@@ -780,15 +825,19 @@ error:
 
 /* May not be called with RCU read lock. */
 static struct sk_buff *ovs_flow_cmd_alloc_info(const struct sw_flow_actions *acts,
+					       const struct sw_flow_id *sfid,
 					       struct genl_info *info,
-					       bool always)
+					       bool always,
+					       uint32_t ufid_flags)
 {
 	struct sk_buff *skb;
+	size_t len;
 
 	if (!always && !ovs_must_notify(&dp_flow_genl_family, info, 0))
 		return NULL;
 
-	skb = genlmsg_new_unicast(ovs_flow_cmd_msg_size(acts), info, GFP_KERNEL);
+	len = ovs_flow_cmd_msg_size(acts, sfid, ufid_flags);
+	skb = genlmsg_new_unicast(len, info, GFP_KERNEL);
 	if (!skb)
 		return ERR_PTR(-ENOMEM);
 
@@ -799,19 +848,19 @@ static struct sk_buff *ovs_flow_cmd_alloc_info(const struct sw_flow_actions *act
 static struct sk_buff *ovs_flow_cmd_build_info(const struct sw_flow *flow,
 					       int dp_ifindex,
 					       struct genl_info *info, u8 cmd,
-					       bool always)
+					       bool always, u32 ufid_flags)
 {
 	struct sk_buff *skb;
 	int retval;
 
-	skb = ovs_flow_cmd_alloc_info(ovsl_dereference(flow->sf_acts), info,
-				      always);
+	skb = ovs_flow_cmd_alloc_info(ovsl_dereference(flow->sf_acts),
+				      &flow->id, info, always, ufid_flags);
 	if (IS_ERR_OR_NULL(skb))
 		return skb;
 
 	retval = ovs_flow_cmd_fill_info(flow, dp_ifindex, skb,
 					info->snd_portid, info->snd_seq, 0,
-					cmd);
+					cmd, ufid_flags);
 	BUG_ON(retval < 0);
 	return skb;
 }
@@ -820,12 +869,14 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
 {
 	struct nlattr **a = info->attrs;
 	struct ovs_header *ovs_header = info->userhdr;
-	struct sw_flow *flow, *new_flow;
+	struct sw_flow *flow = NULL, *new_flow;
 	struct sw_flow_mask mask;
 	struct sk_buff *reply;
 	struct datapath *dp;
+	struct sw_flow_key key;
 	struct sw_flow_actions *acts;
 	struct sw_flow_match match;
+	u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]);
 	int error;
 	bool log = !a[OVS_FLOW_ATTR_PROBE];
 
@@ -850,13 +901,19 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
 	}
 
 	/* Extract key. */
-	ovs_match_init(&match, &new_flow->unmasked_key, &mask);
+	ovs_match_init(&match, &key, &mask);
 	error = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY],
 				  a[OVS_FLOW_ATTR_MASK], log);
 	if (error)
 		goto err_kfree_flow;
 
-	ovs_flow_mask_key(&new_flow->key, &new_flow->unmasked_key, &mask);
+	ovs_flow_mask_key(&new_flow->key, &key, &mask);
+
+	/* Extract flow identifier. */
+	error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID],
+				       &key, log);
+	if (error)
+		goto err_kfree_flow;
 
 	/* Validate actions. */
 	error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &new_flow->key,
@@ -866,7 +923,8 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
 		goto err_kfree_flow;
 	}
 
-	reply = ovs_flow_cmd_alloc_info(acts, info, false);
+	reply = ovs_flow_cmd_alloc_info(acts, &new_flow->id, info, false,
+					ufid_flags);
 	if (IS_ERR(reply)) {
 		error = PTR_ERR(reply);
 		goto err_kfree_acts;
@@ -878,8 +936,12 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
 		error = -ENODEV;
 		goto err_unlock_ovs;
 	}
+
 	/* Check if this is a duplicate flow */
-	flow = ovs_flow_tbl_lookup(&dp->table, &new_flow->unmasked_key);
+	if (ovs_identifier_is_ufid(&new_flow->id))
+		flow = ovs_flow_tbl_lookup_ufid(&dp->table, &new_flow->id);
+	if (!flow)
+		flow = ovs_flow_tbl_lookup(&dp->table, &key);
 	if (likely(!flow)) {
 		rcu_assign_pointer(new_flow->sf_acts, acts);
 
@@ -895,7 +957,8 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
 						       ovs_header->dp_ifindex,
 						       reply, info->snd_portid,
 						       info->snd_seq, 0,
-						       OVS_FLOW_CMD_NEW);
+						       OVS_FLOW_CMD_NEW,
+						       ufid_flags);
 			BUG_ON(error < 0);
 		}
 		ovs_unlock();
@@ -913,10 +976,15 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
 			error = -EEXIST;
 			goto err_unlock_ovs;
 		}
-		/* The unmasked key has to be the same for flow updates. */
-		if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) {
-			/* Look for any overlapping flow. */
-			flow = ovs_flow_tbl_lookup_exact(&dp->table, &match);
+		/* The flow identifier has to be the same for flow updates.
+		 * Look for any overlapping flow.
+		 */
+		if (unlikely(!ovs_flow_cmp(flow, &match))) {
+			if (ovs_identifier_is_key(&flow->id))
+				flow = ovs_flow_tbl_lookup_exact(&dp->table,
+								 &match);
+			else /* UFID matches but key is different */
+				flow = NULL;
 			if (!flow) {
 				error = -ENOENT;
 				goto err_unlock_ovs;
@@ -931,7 +999,8 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
 						       ovs_header->dp_ifindex,
 						       reply, info->snd_portid,
 						       info->snd_seq, 0,
-						       OVS_FLOW_CMD_NEW);
+						       OVS_FLOW_CMD_NEW,
+						       ufid_flags);
 			BUG_ON(error < 0);
 		}
 		ovs_unlock();
@@ -987,8 +1056,11 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
 	struct datapath *dp;
 	struct sw_flow_actions *old_acts = NULL, *acts = NULL;
 	struct sw_flow_match match;
+	struct sw_flow_id sfid;
+	u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]);
 	int error;
 	bool log = !a[OVS_FLOW_ATTR_PROBE];
+	bool ufid_present;
 
 	/* Extract key. */
 	error = -EINVAL;
@@ -997,6 +1069,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
 		goto error;
 	}
 
+	ufid_present = ovs_nla_get_ufid(&sfid, a[OVS_FLOW_ATTR_UFID], log);
 	ovs_match_init(&match, &key, &mask);
 	error = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY],
 				  a[OVS_FLOW_ATTR_MASK], log);
@@ -1013,7 +1086,8 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
 		}
 
 		/* Can allocate before locking if have acts. */
-		reply = ovs_flow_cmd_alloc_info(acts, info, false);
+		reply = ovs_flow_cmd_alloc_info(acts, &sfid, info, false,
+						ufid_flags);
 		if (IS_ERR(reply)) {
 			error = PTR_ERR(reply);
 			goto err_kfree_acts;
@@ -1027,7 +1101,10 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
 		goto err_unlock_ovs;
 	}
 	/* Check that the flow exists. */
-	flow = ovs_flow_tbl_lookup_exact(&dp->table, &match);
+	if (ufid_present)
+		flow = ovs_flow_tbl_lookup_ufid(&dp->table, &sfid);
+	else
+		flow = ovs_flow_tbl_lookup_exact(&dp->table, &match);
 	if (unlikely(!flow)) {
 		error = -ENOENT;
 		goto err_unlock_ovs;
@@ -1043,13 +1120,16 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
 						       ovs_header->dp_ifindex,
 						       reply, info->snd_portid,
 						       info->snd_seq, 0,
-						       OVS_FLOW_CMD_NEW);
+						       OVS_FLOW_CMD_NEW,
+						       ufid_flags);
 			BUG_ON(error < 0);
 		}
 	} else {
 		/* Could not alloc without acts before locking. */
 		reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex,
-						info, OVS_FLOW_CMD_NEW, false);
+						info, OVS_FLOW_CMD_NEW, false,
+						ufid_flags);
+
 		if (unlikely(IS_ERR(reply))) {
 			error = PTR_ERR(reply);
 			goto err_unlock_ovs;
@@ -1086,17 +1166,22 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
 	struct sw_flow *flow;
 	struct datapath *dp;
 	struct sw_flow_match match;
-	int err;
+	struct sw_flow_id ufid;
+	u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]);
+	int err = 0;
 	bool log = !a[OVS_FLOW_ATTR_PROBE];
+	bool ufid_present;
 
-	if (!a[OVS_FLOW_ATTR_KEY]) {
+	ufid_present = ovs_nla_get_ufid(&ufid, a[OVS_FLOW_ATTR_UFID], log);
+	if (a[OVS_FLOW_ATTR_KEY]) {
+		ovs_match_init(&match, &key, NULL);
+		err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL,
+					log);
+	} else if (!ufid_present) {
 		OVS_NLERR(log,
 			  "Flow get message rejected, Key attribute missing.");
-		return -EINVAL;
+		err = -EINVAL;
 	}
-
-	ovs_match_init(&match, &key, NULL);
-	err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL, log);
 	if (err)
 		return err;
 
@@ -1107,14 +1192,17 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
 		goto unlock;
 	}
 
-	flow = ovs_flow_tbl_lookup_exact(&dp->table, &match);
+	if (ufid_present)
+		flow = ovs_flow_tbl_lookup_ufid(&dp->table, &ufid);
+	else
+		flow = ovs_flow_tbl_lookup_exact(&dp->table, &match);
 	if (!flow) {
 		err = -ENOENT;
 		goto unlock;
 	}
 
 	reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, info,
-					OVS_FLOW_CMD_NEW, true);
+					OVS_FLOW_CMD_NEW, true, ufid_flags);
 	if (IS_ERR(reply)) {
 		err = PTR_ERR(reply);
 		goto unlock;
@@ -1133,13 +1221,17 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
 	struct ovs_header *ovs_header = info->userhdr;
 	struct sw_flow_key key;
 	struct sk_buff *reply;
-	struct sw_flow *flow;
+	struct sw_flow *flow = NULL;
 	struct datapath *dp;
 	struct sw_flow_match match;
+	struct sw_flow_id ufid;
+	u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]);
 	int err;
 	bool log = !a[OVS_FLOW_ATTR_PROBE];
+	bool ufid_present;
 
-	if (likely(a[OVS_FLOW_ATTR_KEY])) {
+	ufid_present = ovs_nla_get_ufid(&ufid, a[OVS_FLOW_ATTR_UFID], log);
+	if (a[OVS_FLOW_ATTR_KEY]) {
 		ovs_match_init(&match, &key, NULL);
 		err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL,
 					log);
@@ -1154,12 +1246,15 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
 		goto unlock;
 	}
 
-	if (unlikely(!a[OVS_FLOW_ATTR_KEY])) {
+	if (unlikely(!a[OVS_FLOW_ATTR_KEY] && !ufid_present)) {
 		err = ovs_flow_tbl_flush(&dp->table);
 		goto unlock;
 	}
 
-	flow = ovs_flow_tbl_lookup_exact(&dp->table, &match);
+	if (ufid_present)
+		flow = ovs_flow_tbl_lookup_ufid(&dp->table, &ufid);
+	else
+		flow = ovs_flow_tbl_lookup_exact(&dp->table, &match);
 	if (unlikely(!flow)) {
 		err = -ENOENT;
 		goto unlock;
@@ -1169,14 +1264,15 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
 	ovs_unlock();
 
 	reply = ovs_flow_cmd_alloc_info((const struct sw_flow_actions __force *) flow->sf_acts,
-					info, false);
+					&flow->id, info, false, ufid_flags);
 	if (likely(reply)) {
 		if (likely(!IS_ERR(reply))) {
 			rcu_read_lock();	/*To keep RCU checker happy. */
 			err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex,
 						     reply, info->snd_portid,
 						     info->snd_seq, 0,
-						     OVS_FLOW_CMD_DEL);
+						     OVS_FLOW_CMD_DEL,
+						     ufid_flags);
 			rcu_read_unlock();
 			BUG_ON(err < 0);
 
@@ -1195,9 +1291,18 @@ unlock:
 
 static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
 {
+	struct nlattr *a[__OVS_FLOW_ATTR_MAX];
 	struct ovs_header *ovs_header = genlmsg_data(nlmsg_data(cb->nlh));
 	struct table_instance *ti;
 	struct datapath *dp;
+	u32 ufid_flags;
+	int err;
+
+	err = genlmsg_parse(cb->nlh, &dp_flow_genl_family, a,
+			    OVS_FLOW_ATTR_MAX, flow_policy);
+	if (err)
+		return err;
+	ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]);
 
 	rcu_read_lock();
 	dp = get_dp_rcu(sock_net(skb->sk), ovs_header->dp_ifindex);
@@ -1220,7 +1325,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
 		if (ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, skb,
 					   NETLINK_CB(cb->skb).portid,
 					   cb->nlh->nlmsg_seq, NLM_F_MULTI,
-					   OVS_FLOW_CMD_NEW) < 0)
+					   OVS_FLOW_CMD_NEW, ufid_flags) < 0)
 			break;
 
 		cb->args[0] = bucket;
@@ -1236,6 +1341,8 @@ static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = {
 	[OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED },
 	[OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG },
 	[OVS_FLOW_ATTR_PROBE] = { .type = NLA_FLAG },
+	[OVS_FLOW_ATTR_UFID] = { .type = NLA_UNSPEC, .len = 1 },
+	[OVS_FLOW_ATTR_UFID_FLAGS] = { .type = NLA_U32 },
 };
 
 static const struct genl_ops dp_flow_genl_ops[] = {
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h
index d3d0a40..a076e44 100644
--- a/net/openvswitch/flow.h
+++ b/net/openvswitch/flow.h
@@ -197,6 +197,16 @@ struct sw_flow_match {
 	struct sw_flow_mask *mask;
 };
 
+#define MAX_UFID_LENGTH 16 /* 128 bits */
+
+struct sw_flow_id {
+	u32 ufid_len;
+	union {
+		u32 ufid[MAX_UFID_LENGTH / 4];
+		struct sw_flow_key *unmasked_key;
+	};
+};
+
 struct sw_flow_actions {
 	struct rcu_head rcu;
 	u32 actions_len;
@@ -213,13 +223,15 @@ struct flow_stats {
 
 struct sw_flow {
 	struct rcu_head rcu;
-	struct hlist_node hash_node[2];
-	u32 hash;
+	struct {
+		struct hlist_node node[2];
+		u32 hash;
+	} flow_table, ufid_table;
 	int stats_last_writer;		/* NUMA-node id of the last writer on
 					 * 'stats[0]'.
 					 */
 	struct sw_flow_key key;
-	struct sw_flow_key unmasked_key;
+	struct sw_flow_id id;
 	struct sw_flow_mask *mask;
 	struct sw_flow_actions __rcu *sf_acts;
 	struct flow_stats __rcu *stats[]; /* One for each NUMA node.  First one
@@ -243,6 +255,16 @@ struct arp_eth_header {
 	unsigned char       ar_tip[4];		/* target IP address        */
 } __packed;
 
+static inline bool ovs_identifier_is_ufid(const struct sw_flow_id *sfid)
+{
+	return sfid->ufid_len;
+}
+
+static inline bool ovs_identifier_is_key(const struct sw_flow_id *sfid)
+{
+	return !ovs_identifier_is_ufid(sfid);
+}
+
 void ovs_flow_stats_update(struct sw_flow *, __be16 tcp_flags,
 			   const struct sk_buff *);
 void ovs_flow_stats_get(const struct sw_flow *, struct ovs_flow_stats *,
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 33751f8..8b9a612 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -1180,6 +1180,59 @@ free_newmask:
 	return err;
 }
 
+static size_t get_ufid_len(const struct nlattr *attr, bool log)
+{
+	size_t len;
+
+	if (!attr)
+		return 0;
+
+	len = nla_len(attr);
+	if (len < 1 || len > MAX_UFID_LENGTH) {
+		OVS_NLERR(log, "ufid size %u bytes exceeds the range (1, %d)",
+			  nla_len(attr), MAX_UFID_LENGTH);
+		return 0;
+	}
+
+	return len;
+}
+
+/* Initializes 'flow->ufid', returning true if 'attr' contains a valid UFID,
+ * or false otherwise.
+ */
+bool ovs_nla_get_ufid(struct sw_flow_id *sfid, const struct nlattr *attr,
+		      bool log)
+{
+	sfid->ufid_len = get_ufid_len(attr, log);
+	if (sfid->ufid_len)
+		memcpy(sfid->ufid, nla_data(attr), sfid->ufid_len);
+
+	return sfid->ufid_len;
+}
+
+int ovs_nla_get_identifier(struct sw_flow_id *sfid, const struct nlattr *ufid,
+			   const struct sw_flow_key *key, bool log)
+{
+	struct sw_flow_key *new_key;
+
+	if (ovs_nla_get_ufid(sfid, ufid, log))
+		return 0;
+
+	/* If UFID was not provided, use unmasked key. */
+	new_key = kmalloc(sizeof(*new_key), GFP_KERNEL);
+	if (!new_key)
+		return -ENOMEM;
+	memcpy(new_key, key, sizeof(*key));
+	sfid->unmasked_key = new_key;
+
+	return 0;
+}
+
+u32 ovs_nla_get_ufid_flags(const struct nlattr *attr)
+{
+	return attr ? nla_get_u32(attr) : 0;
+}
+
 /**
  * ovs_nla_get_flow_metadata - parses Netlink attributes into a flow key.
  * @key: Receives extracted in_port, priority, tun_key and skb_mark.
@@ -1450,9 +1503,20 @@ int ovs_nla_put_key(const struct sw_flow_key *swkey,
 }
 
 /* Called with ovs_mutex or RCU read lock. */
-int ovs_nla_put_unmasked_key(const struct sw_flow *flow, struct sk_buff *skb)
+int ovs_nla_put_identifier(const struct sw_flow *flow, struct sk_buff *skb)
+{
+	if (ovs_identifier_is_ufid(&flow->id))
+		return nla_put(skb, OVS_FLOW_ATTR_UFID, flow->id.ufid_len,
+			       flow->id.ufid);
+
+	return ovs_nla_put_key(flow->id.unmasked_key, flow->id.unmasked_key,
+			       OVS_FLOW_ATTR_KEY, false, skb);
+}
+
+/* Called with ovs_mutex or RCU read lock. */
+int ovs_nla_put_masked_key(const struct sw_flow *flow, struct sk_buff *skb)
 {
-	return ovs_nla_put_key(&flow->unmasked_key, &flow->unmasked_key,
+	return ovs_nla_put_key(&flow->mask->key, &flow->key,
 				OVS_FLOW_ATTR_KEY, false, skb);
 }
 
diff --git a/net/openvswitch/flow_netlink.h b/net/openvswitch/flow_netlink.h
index 9ed09e6..5c3d75b 100644
--- a/net/openvswitch/flow_netlink.h
+++ b/net/openvswitch/flow_netlink.h
@@ -48,7 +48,8 @@ int ovs_nla_put_key(const struct sw_flow_key *, const struct sw_flow_key *,
 int ovs_nla_get_flow_metadata(const struct nlattr *, struct sw_flow_key *,
 			      bool log);
 
-int ovs_nla_put_unmasked_key(const struct sw_flow *flow, struct sk_buff *skb);
+int ovs_nla_put_identifier(const struct sw_flow *flow, struct sk_buff *skb);
+int ovs_nla_put_masked_key(const struct sw_flow *flow, struct sk_buff *skb);
 int ovs_nla_put_mask(const struct sw_flow *flow, struct sk_buff *skb);
 
 int ovs_nla_get_match(struct sw_flow_match *, const struct nlattr *key,
@@ -56,6 +57,11 @@ int ovs_nla_get_match(struct sw_flow_match *, const struct nlattr *key,
 int ovs_nla_put_egress_tunnel_key(struct sk_buff *,
 				  const struct ovs_tunnel_info *);
 
+bool ovs_nla_get_ufid(struct sw_flow_id *, const struct nlattr *, bool log);
+int ovs_nla_get_identifier(struct sw_flow_id *sfid, const struct nlattr *ufid,
+			   const struct sw_flow_key *key, bool log);
+u32 ovs_nla_get_ufid_flags(const struct nlattr *attr);
+
 int ovs_nla_copy_actions(const struct nlattr *attr,
 			 const struct sw_flow_key *key,
 			 struct sw_flow_actions **sfa, bool log);
diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c
index 9a3f41f..5e57628 100644
--- a/net/openvswitch/flow_table.c
+++ b/net/openvswitch/flow_table.c
@@ -139,6 +139,8 @@ static void flow_free(struct sw_flow *flow)
 {
 	int node;
 
+	if (ovs_identifier_is_key(&flow->id))
+		kfree(flow->id.unmasked_key);
 	kfree((struct sw_flow_actions __force *)flow->sf_acts);
 	for_each_node(node)
 		if (flow->stats[node])
@@ -200,18 +202,28 @@ static struct table_instance *table_instance_alloc(int new_size)
 
 int ovs_flow_tbl_init(struct flow_table *table)
 {
-	struct table_instance *ti;
+	struct table_instance *ti, *ufid_ti;
 
 	ti = table_instance_alloc(TBL_MIN_BUCKETS);
 
 	if (!ti)
 		return -ENOMEM;
 
+	ufid_ti = table_instance_alloc(TBL_MIN_BUCKETS);
+	if (!ufid_ti)
+		goto free_ti;
+
 	rcu_assign_pointer(table->ti, ti);
+	rcu_assign_pointer(table->ufid_ti, ufid_ti);
 	INIT_LIST_HEAD(&table->mask_list);
 	table->last_rehash = jiffies;
 	table->count = 0;
+	table->ufid_count = 0;
 	return 0;
+
+free_ti:
+	__table_instance_destroy(ti);
+	return -ENOMEM;
 }
 
 static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu)
@@ -221,13 +233,16 @@ static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu)
 	__table_instance_destroy(ti);
 }
 
-static void table_instance_destroy(struct table_instance *ti, bool deferred)
+static void table_instance_destroy(struct table_instance *ti,
+				   struct table_instance *ufid_ti,
+				   bool deferred)
 {
 	int i;
 
 	if (!ti)
 		return;
 
+	BUG_ON(!ufid_ti);
 	if (ti->keep_flows)
 		goto skip_flows;
 
@@ -236,18 +251,24 @@ static void table_instance_destroy(struct table_instance *ti, bool deferred)
 		struct hlist_head *head = flex_array_get(ti->buckets, i);
 		struct hlist_node *n;
 		int ver = ti->node_ver;
+		int ufid_ver = ufid_ti->node_ver;
 
-		hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) {
-			hlist_del_rcu(&flow->hash_node[ver]);
+		hlist_for_each_entry_safe(flow, n, head, flow_table.node[ver]) {
+			hlist_del_rcu(&flow->flow_table.node[ver]);
+			if (ovs_identifier_is_ufid(&flow->id))
+				hlist_del_rcu(&flow->ufid_table.node[ufid_ver]);
 			ovs_flow_free(flow, deferred);
 		}
 	}
 
 skip_flows:
-	if (deferred)
+	if (deferred) {
 		call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb);
-	else
+		call_rcu(&ufid_ti->rcu, flow_tbl_destroy_rcu_cb);
+	} else {
 		__table_instance_destroy(ti);
+		__table_instance_destroy(ufid_ti);
+	}
 }
 
 /* No need for locking this function is called from RCU callback or
@@ -256,8 +277,9 @@ skip_flows:
 void ovs_flow_tbl_destroy(struct flow_table *table)
 {
 	struct table_instance *ti = rcu_dereference_raw(table->ti);
+	struct table_instance *ufid_ti = rcu_dereference_raw(table->ufid_ti);
 
-	table_instance_destroy(ti, false);
+	table_instance_destroy(ti, ufid_ti, false);
 }
 
 struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti,
@@ -272,7 +294,7 @@ struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti,
 	while (*bucket < ti->n_buckets) {
 		i = 0;
 		head = flex_array_get(ti->buckets, *bucket);
-		hlist_for_each_entry_rcu(flow, head, hash_node[ver]) {
+		hlist_for_each_entry_rcu(flow, head, flow_table.node[ver]) {
 			if (i < *last) {
 				i++;
 				continue;
@@ -294,16 +316,26 @@ static struct hlist_head *find_bucket(struct table_instance *ti, u32 hash)
 				(hash & (ti->n_buckets - 1)));
 }
 
-static void table_instance_insert(struct table_instance *ti, struct sw_flow *flow)
+static void table_instance_insert(struct table_instance *ti,
+				  struct sw_flow *flow)
 {
 	struct hlist_head *head;
 
-	head = find_bucket(ti, flow->hash);
-	hlist_add_head_rcu(&flow->hash_node[ti->node_ver], head);
+	head = find_bucket(ti, flow->flow_table.hash);
+	hlist_add_head_rcu(&flow->flow_table.node[ti->node_ver], head);
+}
+
+static void ufid_table_instance_insert(struct table_instance *ti,
+				       struct sw_flow *flow)
+{
+	struct hlist_head *head;
+
+	head = find_bucket(ti, flow->ufid_table.hash);
+	hlist_add_head_rcu(&flow->ufid_table.node[ti->node_ver], head);
 }
 
 static void flow_table_copy_flows(struct table_instance *old,
-				  struct table_instance *new)
+				  struct table_instance *new, bool ufid)
 {
 	int old_ver;
 	int i;
@@ -318,15 +350,21 @@ static void flow_table_copy_flows(struct table_instance *old,
 
 		head = flex_array_get(old->buckets, i);
 
-		hlist_for_each_entry(flow, head, hash_node[old_ver])
-			table_instance_insert(new, flow);
+		if (ufid)
+			hlist_for_each_entry(flow, head,
+					     ufid_table.node[old_ver])
+				ufid_table_instance_insert(new, flow);
+		else
+			hlist_for_each_entry(flow, head,
+					     flow_table.node[old_ver])
+				table_instance_insert(new, flow);
 	}
 
 	old->keep_flows = true;
 }
 
 static struct table_instance *table_instance_rehash(struct table_instance *ti,
-					    int n_buckets)
+						    int n_buckets, bool ufid)
 {
 	struct table_instance *new_ti;
 
@@ -334,27 +372,38 @@ static struct table_instance *table_instance_rehash(struct table_instance *ti,
 	if (!new_ti)
 		return NULL;
 
-	flow_table_copy_flows(ti, new_ti);
+	flow_table_copy_flows(ti, new_ti, ufid);
 
 	return new_ti;
 }
 
 int ovs_flow_tbl_flush(struct flow_table *flow_table)
 {
-	struct table_instance *old_ti;
-	struct table_instance *new_ti;
+	struct table_instance *old_ti, *new_ti;
+	struct table_instance *old_ufid_ti, *new_ufid_ti;
 
-	old_ti = ovsl_dereference(flow_table->ti);
 	new_ti = table_instance_alloc(TBL_MIN_BUCKETS);
 	if (!new_ti)
 		return -ENOMEM;
+	new_ufid_ti = table_instance_alloc(TBL_MIN_BUCKETS);
+	if (!new_ufid_ti)
+		goto err_free_ti;
+
+	old_ti = ovsl_dereference(flow_table->ti);
+	old_ufid_ti = ovsl_dereference(flow_table->ufid_ti);
 
 	rcu_assign_pointer(flow_table->ti, new_ti);
+	rcu_assign_pointer(flow_table->ufid_ti, new_ufid_ti);
 	flow_table->last_rehash = jiffies;
 	flow_table->count = 0;
+	flow_table->ufid_count = 0;
 
-	table_instance_destroy(old_ti, true);
+	table_instance_destroy(old_ti, old_ufid_ti, true);
 	return 0;
+
+err_free_ti:
+	__table_instance_destroy(new_ti);
+	return -ENOMEM;
 }
 
 static u32 flow_hash(const struct sw_flow_key *key,
@@ -402,14 +451,15 @@ static bool flow_cmp_masked_key(const struct sw_flow *flow,
 	return cmp_key(&flow->key, key, range->start, range->end);
 }
 
-bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
-			       const struct sw_flow_match *match)
+static bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
+				      const struct sw_flow_match *match)
 {
 	struct sw_flow_key *key = match->key;
 	int key_start = flow_key_start(key);
 	int key_end = match->range.end;
 
-	return cmp_key(&flow->unmasked_key, key, key_start, key_end);
+	BUG_ON(ovs_identifier_is_ufid(&flow->id));
+	return cmp_key(flow->id.unmasked_key, key, key_start, key_end);
 }
 
 static struct sw_flow *masked_flow_lookup(struct table_instance *ti,
@@ -424,8 +474,8 @@ static struct sw_flow *masked_flow_lookup(struct table_instance *ti,
 	ovs_flow_mask_key(&masked_key, unmasked, mask);
 	hash = flow_hash(&masked_key, &mask->range);
 	head = find_bucket(ti, hash);
-	hlist_for_each_entry_rcu(flow, head, hash_node[ti->node_ver]) {
-		if (flow->mask == mask && flow->hash == hash &&
+	hlist_for_each_entry_rcu(flow, head, flow_table.node[ti->node_ver]) {
+		if (flow->mask == mask && flow->flow_table.hash == hash &&
 		    flow_cmp_masked_key(flow, &masked_key, &mask->range))
 			return flow;
 	}
@@ -468,7 +518,48 @@ struct sw_flow *ovs_flow_tbl_lookup_exact(struct flow_table *tbl,
 	/* Always called under ovs-mutex. */
 	list_for_each_entry(mask, &tbl->mask_list, list) {
 		flow = masked_flow_lookup(ti, match->key, mask);
-		if (flow && ovs_flow_cmp_unmasked_key(flow, match))  /* Found */
+		if (flow && ovs_identifier_is_key(&flow->id) &&
+		    ovs_flow_cmp_unmasked_key(flow, match))
+			return flow;
+	}
+	return NULL;
+}
+
+static u32 ufid_hash(const struct sw_flow_id *sfid)
+{
+	return jhash(sfid->ufid, sfid->ufid_len, 0);
+}
+
+static bool ovs_flow_cmp_ufid(const struct sw_flow *flow,
+			      const struct sw_flow_id *sfid)
+{
+	if (flow->id.ufid_len != sfid->ufid_len)
+		return false;
+
+	return !memcmp(flow->id.ufid, sfid->ufid, sfid->ufid_len);
+}
+
+bool ovs_flow_cmp(const struct sw_flow *flow, const struct sw_flow_match *match)
+{
+	if (ovs_identifier_is_ufid(&flow->id))
+		return flow_cmp_masked_key(flow, match->key, &match->range);
+
+	return ovs_flow_cmp_unmasked_key(flow, match);
+}
+
+struct sw_flow *ovs_flow_tbl_lookup_ufid(struct flow_table *tbl,
+					 const struct sw_flow_id *ufid)
+{
+	struct table_instance *ti = rcu_dereference_ovsl(tbl->ufid_ti);
+	struct sw_flow *flow;
+	struct hlist_head *head;
+	u32 hash;
+
+	hash = ufid_hash(ufid);
+	head = find_bucket(ti, hash);
+	hlist_for_each_entry_rcu(flow, head, ufid_table.node[ti->node_ver]) {
+		if (flow->ufid_table.hash == hash &&
+		    ovs_flow_cmp_ufid(flow, ufid))
 			return flow;
 	}
 	return NULL;
@@ -485,9 +576,10 @@ int ovs_flow_tbl_num_masks(const struct flow_table *table)
 	return num;
 }
 
-static struct table_instance *table_instance_expand(struct table_instance *ti)
+static struct table_instance *table_instance_expand(struct table_instance *ti,
+						    bool ufid)
 {
-	return table_instance_rehash(ti, ti->n_buckets * 2);
+	return table_instance_rehash(ti, ti->n_buckets * 2, ufid);
 }
 
 /* Remove 'mask' from the mask list, if it is not needed any more. */
@@ -512,10 +604,15 @@ static void flow_mask_remove(struct flow_table *tbl, struct sw_flow_mask *mask)
 void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
 {
 	struct table_instance *ti = ovsl_dereference(table->ti);
+	struct table_instance *ufid_ti = ovsl_dereference(table->ufid_ti);
 
 	BUG_ON(table->count == 0);
-	hlist_del_rcu(&flow->hash_node[ti->node_ver]);
+	hlist_del_rcu(&flow->flow_table.node[ti->node_ver]);
 	table->count--;
+	if (ovs_identifier_is_ufid(&flow->id)) {
+		hlist_del_rcu(&flow->ufid_table.node[ufid_ti->node_ver]);
+		table->ufid_count--;
+	}
 
 	/* RCU delete the mask. 'flow->mask' is not NULLed, as it should be
 	 * accessible as long as the RCU read lock is held.
@@ -589,25 +686,47 @@ static void flow_key_insert(struct flow_table *table, struct sw_flow *flow)
 	struct table_instance *new_ti = NULL;
 	struct table_instance *ti;
 
-	flow->hash = flow_hash(&flow->key, &flow->mask->range);
+	flow->flow_table.hash = flow_hash(&flow->key, &flow->mask->range);
 	ti = ovsl_dereference(table->ti);
 	table_instance_insert(ti, flow);
 	table->count++;
 
 	/* Expand table, if necessary, to make room. */
 	if (table->count > ti->n_buckets)
-		new_ti = table_instance_expand(ti);
+		new_ti = table_instance_expand(ti, false);
 	else if (time_after(jiffies, table->last_rehash + REHASH_INTERVAL))
-		new_ti = table_instance_rehash(ti, ti->n_buckets);
+		new_ti = table_instance_rehash(ti, ti->n_buckets, false);
 
 	if (new_ti) {
 		rcu_assign_pointer(table->ti, new_ti);
-		table_instance_destroy(ti, true);
+		call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb);
 		table->last_rehash = jiffies;
 	}
 }
 
 /* Must be called with OVS mutex held. */
+static void flow_ufid_insert(struct flow_table *table, struct sw_flow *flow)
+{
+	struct table_instance *ti;
+
+	flow->ufid_table.hash = ufid_hash(&flow->id);
+	ti = ovsl_dereference(table->ufid_ti);
+	ufid_table_instance_insert(ti, flow);
+	table->ufid_count++;
+
+	/* Expand table, if necessary, to make room. */
+	if (table->ufid_count > ti->n_buckets) {
+		struct table_instance *new_ti;
+
+		new_ti = table_instance_expand(ti, true);
+		if (new_ti) {
+			rcu_assign_pointer(table->ufid_ti, new_ti);
+			call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb);
+		}
+	}
+}
+
+/* Must be called with OVS mutex held. */
 int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
 			const struct sw_flow_mask *mask)
 {
@@ -617,6 +736,8 @@ int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
 	if (err)
 		return err;
 	flow_key_insert(table, flow);
+	if (ovs_identifier_is_ufid(&flow->id))
+		flow_ufid_insert(table, flow);
 
 	return 0;
 }
diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h
index 309fa64..616eda1 100644
--- a/net/openvswitch/flow_table.h
+++ b/net/openvswitch/flow_table.h
@@ -47,9 +47,11 @@ struct table_instance {
 
 struct flow_table {
 	struct table_instance __rcu *ti;
+	struct table_instance __rcu *ufid_ti;
 	struct list_head mask_list;
 	unsigned long last_rehash;
 	unsigned int count;
+	unsigned int ufid_count;
 };
 
 extern struct kmem_cache *flow_stats_cache;
@@ -78,8 +80,10 @@ struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *,
 				    const struct sw_flow_key *);
 struct sw_flow *ovs_flow_tbl_lookup_exact(struct flow_table *tbl,
 					  const struct sw_flow_match *match);
-bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
-			       const struct sw_flow_match *match);
+struct sw_flow *ovs_flow_tbl_lookup_ufid(struct flow_table *,
+					 const struct sw_flow_id *);
+
+bool ovs_flow_cmp(const struct sw_flow *, const struct sw_flow_match *);
 
 void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
 		       const struct sw_flow_mask *mask);
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH net-next v14 3/5] openvswitch: Use sw_flow_key_range for key ranges.
From: Joe Stringer @ 2015-01-22  0:42 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel, dev, pshelar
In-Reply-To: <1421887372-56414-1-git-send-email-joestringer@nicira.com>

These minor tidyups make a future patch a little tidier.

Signed-off-by: Joe Stringer <joestringer@nicira.com>
---
 net/openvswitch/flow_table.c |   20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c
index 81b977d..9a3f41f 100644
--- a/net/openvswitch/flow_table.c
+++ b/net/openvswitch/flow_table.c
@@ -357,9 +357,11 @@ int ovs_flow_tbl_flush(struct flow_table *flow_table)
 	return 0;
 }
 
-static u32 flow_hash(const struct sw_flow_key *key, int key_start,
-		     int key_end)
+static u32 flow_hash(const struct sw_flow_key *key,
+		     const struct sw_flow_key_range *range)
 {
+	int key_start = range->start;
+	int key_end = range->end;
 	const u32 *hash_key = (const u32 *)((const u8 *)key + key_start);
 	int hash_u32s = (key_end - key_start) >> 2;
 
@@ -395,9 +397,9 @@ static bool cmp_key(const struct sw_flow_key *key1,
 
 static bool flow_cmp_masked_key(const struct sw_flow *flow,
 				const struct sw_flow_key *key,
-				int key_start, int key_end)
+				const struct sw_flow_key_range *range)
 {
-	return cmp_key(&flow->key, key, key_start, key_end);
+	return cmp_key(&flow->key, key, range->start, range->end);
 }
 
 bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
@@ -416,18 +418,15 @@ static struct sw_flow *masked_flow_lookup(struct table_instance *ti,
 {
 	struct sw_flow *flow;
 	struct hlist_head *head;
-	int key_start = mask->range.start;
-	int key_end = mask->range.end;
 	u32 hash;
 	struct sw_flow_key masked_key;
 
 	ovs_flow_mask_key(&masked_key, unmasked, mask);
-	hash = flow_hash(&masked_key, key_start, key_end);
+	hash = flow_hash(&masked_key, &mask->range);
 	head = find_bucket(ti, hash);
 	hlist_for_each_entry_rcu(flow, head, hash_node[ti->node_ver]) {
 		if (flow->mask == mask && flow->hash == hash &&
-		    flow_cmp_masked_key(flow, &masked_key,
-					  key_start, key_end))
+		    flow_cmp_masked_key(flow, &masked_key, &mask->range))
 			return flow;
 	}
 	return NULL;
@@ -590,8 +589,7 @@ static void flow_key_insert(struct flow_table *table, struct sw_flow *flow)
 	struct table_instance *new_ti = NULL;
 	struct table_instance *ti;
 
-	flow->hash = flow_hash(&flow->key, flow->mask->range.start,
-			flow->mask->range.end);
+	flow->hash = flow_hash(&flow->key, &flow->mask->range);
 	ti = ovsl_dereference(table->ti);
 	table_instance_insert(ti, flow);
 	table->count++;
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH net-next v14 4/5] genetlink: Add genlmsg_parse() helper function.
From: Joe Stringer @ 2015-01-22  0:42 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel, dev, pshelar
In-Reply-To: <1421887372-56414-1-git-send-email-joestringer@nicira.com>

The first user will be the next patch.

Signed-off-by: Joe Stringer <joestringer@nicira.com>
---
 include/net/genetlink.h |   17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/include/net/genetlink.h b/include/net/genetlink.h
index f24aa83..d5a9a8b 100644
--- a/include/net/genetlink.h
+++ b/include/net/genetlink.h
@@ -206,6 +206,23 @@ static inline struct nlmsghdr *genlmsg_nlhdr(void *user_hdr,
 }
 
 /**
+ * genlmsg_parse - parse attributes of a genetlink message
+ * @nlh: netlink message header
+ * @family: genetlink message family
+ * @tb: destination array with maxtype+1 elements
+ * @maxtype: maximum attribute type to be expected
+ * @policy: validation policy
+ * */
+static inline int genlmsg_parse(const struct nlmsghdr *nlh,
+				const struct genl_family *family,
+				struct nlattr *tb[], int maxtype,
+				const struct nla_policy *policy)
+{
+	return nlmsg_parse(nlh, family->hdrsize + GENL_HDRLEN, tb, maxtype,
+			   policy);
+}
+
+/**
  * genl_dump_check_consistent - check if sequence is consistent and advertise if not
  * @cb: netlink callback structure that stores the sequence number
  * @user_hdr: user header as returned from genlmsg_put()
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH net-next v14 2/5] openvswitch: Refactor ovs_flow_tbl_insert().
From: Joe Stringer @ 2015-01-22  0:42 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA
  Cc: dev-yBygre7rU0TnMu66kgdUjQ, linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1421887372-56414-1-git-send-email-joestringer-l0M0P4e3n4LQT0dZR+AlfA@public.gmane.org>

Rework so that ovs_flow_tbl_insert() calls flow_{key,mask}_insert().
This tidies up a future patch.

Signed-off-by: Joe Stringer <joestringer@nicira.com>
---
 net/openvswitch/flow_table.c |   21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c
index 5899bf1..81b977d 100644
--- a/net/openvswitch/flow_table.c
+++ b/net/openvswitch/flow_table.c
@@ -585,16 +585,10 @@ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow,
 }
 
 /* Must be called with OVS mutex held. */
-int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
-			const struct sw_flow_mask *mask)
+static void flow_key_insert(struct flow_table *table, struct sw_flow *flow)
 {
 	struct table_instance *new_ti = NULL;
 	struct table_instance *ti;
-	int err;
-
-	err = flow_mask_insert(table, flow, mask);
-	if (err)
-		return err;
 
 	flow->hash = flow_hash(&flow->key, flow->mask->range.start,
 			flow->mask->range.end);
@@ -613,6 +607,19 @@ int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
 		table_instance_destroy(ti, true);
 		table->last_rehash = jiffies;
 	}
+}
+
+/* Must be called with OVS mutex held. */
+int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
+			const struct sw_flow_mask *mask)
+{
+	int err;
+
+	err = flow_mask_insert(table, flow, mask);
+	if (err)
+		return err;
+	flow_key_insert(table, flow);
+
 	return 0;
 }
 
-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

^ permalink raw reply related

* [PATCH net-next v14 1/5] openvswitch: Refactor ovs_nla_fill_match().
From: Joe Stringer @ 2015-01-22  0:42 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA
  Cc: dev-yBygre7rU0TnMu66kgdUjQ, linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1421887372-56414-1-git-send-email-joestringer-l0M0P4e3n4LQT0dZR+AlfA@public.gmane.org>

Refactor the ovs_nla_fill_match() function into separate netlink
serialization functions ovs_nla_put_{unmasked_key,mask}(). Modify
ovs_nla_put_flow() to handle attribute nesting and expose the 'is_mask'
parameter - all callers need to nest the flow, and callers have better
knowledge about whether it is serializing a mask or not.

Signed-off-by: Joe Stringer <joestringer@nicira.com>
---
 net/openvswitch/datapath.c     |   41 ++++++----------------------------------
 net/openvswitch/flow_netlink.c |   38 ++++++++++++++++++++++++++++++++++---
 net/openvswitch/flow_netlink.h |    7 +++++--
 3 files changed, 46 insertions(+), 40 deletions(-)

diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index f45f1bf..257b975 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -461,10 +461,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
 			     0, upcall_info->cmd);
 	upcall->dp_ifindex = dp_ifindex;
 
-	nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_KEY);
-	err = ovs_nla_put_flow(key, key, user_skb);
+	err = ovs_nla_put_key(key, key, OVS_PACKET_ATTR_KEY, false, user_skb);
 	BUG_ON(err);
-	nla_nest_end(user_skb, nla);
 
 	if (upcall_info->userdata)
 		__nla_put(user_skb, OVS_PACKET_ATTR_USERDATA,
@@ -676,37 +674,6 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
 }
 
 /* Called with ovs_mutex or RCU read lock. */
-static int ovs_flow_cmd_fill_match(const struct sw_flow *flow,
-				   struct sk_buff *skb)
-{
-	struct nlattr *nla;
-	int err;
-
-	/* Fill flow key. */
-	nla = nla_nest_start(skb, OVS_FLOW_ATTR_KEY);
-	if (!nla)
-		return -EMSGSIZE;
-
-	err = ovs_nla_put_flow(&flow->unmasked_key, &flow->unmasked_key, skb);
-	if (err)
-		return err;
-
-	nla_nest_end(skb, nla);
-
-	/* Fill flow mask. */
-	nla = nla_nest_start(skb, OVS_FLOW_ATTR_MASK);
-	if (!nla)
-		return -EMSGSIZE;
-
-	err = ovs_nla_put_flow(&flow->key, &flow->mask->key, skb);
-	if (err)
-		return err;
-
-	nla_nest_end(skb, nla);
-	return 0;
-}
-
-/* Called with ovs_mutex or RCU read lock. */
 static int ovs_flow_cmd_fill_stats(const struct sw_flow *flow,
 				   struct sk_buff *skb)
 {
@@ -787,7 +754,11 @@ static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex,
 
 	ovs_header->dp_ifindex = dp_ifindex;
 
-	err = ovs_flow_cmd_fill_match(flow, skb);
+	err = ovs_nla_put_unmasked_key(flow, skb);
+	if (err)
+		goto error;
+
+	err = ovs_nla_put_mask(flow, skb);
 	if (err)
 		goto error;
 
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index d210d1b..33751f8 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -1216,12 +1216,12 @@ int ovs_nla_get_flow_metadata(const struct nlattr *attr,
 	return metadata_from_nlattrs(&match, &attrs, a, false, log);
 }
 
-int ovs_nla_put_flow(const struct sw_flow_key *swkey,
-		     const struct sw_flow_key *output, struct sk_buff *skb)
+static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
+			     const struct sw_flow_key *output, bool is_mask,
+			     struct sk_buff *skb)
 {
 	struct ovs_key_ethernet *eth_key;
 	struct nlattr *nla, *encap;
-	bool is_mask = (swkey != output);
 
 	if (nla_put_u32(skb, OVS_KEY_ATTR_RECIRC_ID, output->recirc_id))
 		goto nla_put_failure;
@@ -1431,6 +1431,38 @@ nla_put_failure:
 	return -EMSGSIZE;
 }
 
+int ovs_nla_put_key(const struct sw_flow_key *swkey,
+		    const struct sw_flow_key *output, int attr, bool is_mask,
+		    struct sk_buff *skb)
+{
+	int err;
+	struct nlattr *nla;
+
+	nla = nla_nest_start(skb, attr);
+	if (!nla)
+		return -EMSGSIZE;
+	err = __ovs_nla_put_key(swkey, output, is_mask, skb);
+	if (err)
+		return err;
+	nla_nest_end(skb, nla);
+
+	return 0;
+}
+
+/* Called with ovs_mutex or RCU read lock. */
+int ovs_nla_put_unmasked_key(const struct sw_flow *flow, struct sk_buff *skb)
+{
+	return ovs_nla_put_key(&flow->unmasked_key, &flow->unmasked_key,
+				OVS_FLOW_ATTR_KEY, false, skb);
+}
+
+/* Called with ovs_mutex or RCU read lock. */
+int ovs_nla_put_mask(const struct sw_flow *flow, struct sk_buff *skb)
+{
+	return ovs_nla_put_key(&flow->key, &flow->mask->key,
+				OVS_FLOW_ATTR_MASK, true, skb);
+}
+
 #define MAX_ACTIONS_BUFSIZE	(32 * 1024)
 
 static struct sw_flow_actions *nla_alloc_flow_actions(int size, bool log)
diff --git a/net/openvswitch/flow_netlink.h b/net/openvswitch/flow_netlink.h
index 577f12b..9ed09e6 100644
--- a/net/openvswitch/flow_netlink.h
+++ b/net/openvswitch/flow_netlink.h
@@ -43,11 +43,14 @@ size_t ovs_key_attr_size(void);
 void ovs_match_init(struct sw_flow_match *match,
 		    struct sw_flow_key *key, struct sw_flow_mask *mask);
 
-int ovs_nla_put_flow(const struct sw_flow_key *,
-		     const struct sw_flow_key *, struct sk_buff *);
+int ovs_nla_put_key(const struct sw_flow_key *, const struct sw_flow_key *,
+		    int attr, bool is_mask, struct sk_buff *);
 int ovs_nla_get_flow_metadata(const struct nlattr *, struct sw_flow_key *,
 			      bool log);
 
+int ovs_nla_put_unmasked_key(const struct sw_flow *flow, struct sk_buff *skb);
+int ovs_nla_put_mask(const struct sw_flow *flow, struct sk_buff *skb);
+
 int ovs_nla_get_match(struct sw_flow_match *, const struct nlattr *key,
 		      const struct nlattr *mask, bool log);
 int ovs_nla_put_egress_tunnel_key(struct sk_buff *,
-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

^ permalink raw reply related

* [PATCH net-next v14 0/5] openvswitch: Introduce 128-bit unique flow identifiers.
From: Joe Stringer @ 2015-01-22  0:42 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA
  Cc: dev-yBygre7rU0TnMu66kgdUjQ, linux-kernel-u79uwXL29TY76Z2rM5mHXA

This series extends the openvswitch datapath interface for flow commands to use
128-bit unique identifiers as an alternative to the netlink-formatted flow key.
This significantly reduces the cost of assembling messages between the kernel
and userspace, in particular improving Open vSwitch revalidation performance by
40% or more.

v14:
- Perform lookup using unmasked key in legacy case.
- Fix minor checkpatch.pl style violations.

v13:
- Embed sw_flow_id in sw_flow to save memory allocation in UFID case.
- Malloc unmasked key for id in non-UFID case.
- Fix bug where non-UFID case could double-serialize keys.

v12:
- Userspace patches fully merged into Open vSwitch master
- New minor refactor patches (2,3,4)
- Merge unmasked_key, ufid representation of flow identifier in sw_flow
- Improve memory allocation sizes when serializing ufid
- Handle corner case where a flow_new is requested with a flow that has an
  identical ufid as an existing flow, but a different flow key
- Limit UFID to between 1-16 octets inclusive.
- Add various helper functions to improve readibility

v11:
- Pushed most of the prerequisite patches for this series to OVS master.
- Split out openvswitch.h interface changes from datapath implementation
- Datapath implementation to be reviewed on net-next, separately

v10:
- New patch allowing datapath to serialize masked keys
- Simplify datapath interface by accepting UFID or flow_key, but not both
- Flows set up with UFID must be queried/deleted using UFID
- Reduce sw_flow memory usage for UFID
- Don't periodically rehash UFID table in linux datapath
- Remove kernel_only UFID in linux datapath

v9:
- No kernel changes

v8:
- Rename UID -> UFID
- Fix null dereference in datapath when paired with older userspace
- All patches are reviewed/acked except datapath changes.

v7:
- Remove OVS_DP_F_INDEX_BY_UID
- Rework datapath UID serialization for variable length UIDs

v6:
- Reduce netlink conversions for all datapaths
- Various bugfixes

v5:
- Various bugfixes
- Improve logging

v4:
- Datapath memory leak fixes
- Enable UID-based terse dumping and deleting by default
- Various fixes

RFCv3:
- Add datapath implementation

Joe Stringer (5):
  openvswitch: Refactor ovs_nla_fill_match().
  openvswitch: Refactor ovs_flow_tbl_insert().
  openvswitch: Use sw_flow_key_range for key ranges.
  genetlink: Add genlmsg_parse() helper function.
  openvswitch: Add support for unique flow IDs.

 Documentation/networking/openvswitch.txt |   13 ++
 include/net/genetlink.h                  |   17 +++
 include/uapi/linux/openvswitch.h         |   20 +++
 net/openvswitch/datapath.c               |  226 ++++++++++++++++++++----------
 net/openvswitch/flow.h                   |   28 +++-
 net/openvswitch/flow_netlink.c           |  102 +++++++++++++-
 net/openvswitch/flow_netlink.h           |   13 +-
 net/openvswitch/flow_table.c             |  226 +++++++++++++++++++++++-------
 net/openvswitch/flow_table.h             |    8 +-
 9 files changed, 519 insertions(+), 134 deletions(-)

-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

^ permalink raw reply

* Re: [PATCH] ethernet: atheros: Add nss-gmac driver
From: wstephen-sgV2jX0FEOL9JmXXK+q4OQ @ 2015-01-22  0:20 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: wstephen-sgV2jX0FEOL9JmXXK+q4OQ, jcliburn-Re5JQEeQqe8AvxtiuMwx3w,
	grant.likely-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1702448.pOVnDx4KzI@wuerfel>

> Right. For review purposes, I think it would be helpful to split this
> huge patch into several steps then:
>
> - add a base driver
> - add the overlay interface
> - add the nss driver
>
> Ideally more of them.

The nss-drv driver is open sourced but we are currently not planning to
upstream to linux kernel yet because we are still actively adding new
features
https://www.codeaurora.org/cgit/quic/qsdk/oss/lklm/nss-drv

> Thanks for the description, this sounds very interesting indeed. I do
> have more questions though: how do you get the rules into the NSS driver?
> Does this get handled transparently by the openvswitch driver or
> did you have to add new user interfaces for it?
>
> 	Arnd
>

No, we are not using openvswitch. We have a connection manager monitoring
conntrack events and creates rules then send it through the interface
built in nss-drv.

Thanks,
Stephen

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 2/2] net: mv643xx_eth: Fix highmem support in non-TSO egress path
From: Russell King - ARM Linux @ 2015-01-22  0:11 UTC (permalink / raw)
  To: Ezequiel Garcia; +Cc: netdev, David Miller, B38611, fabio.estevam
In-Reply-To: <54C03786.2060101@free-electrons.com>

On Wed, Jan 21, 2015 at 08:34:30PM -0300, Ezequiel Garcia wrote:
> I have just realised that the non-TSO and the TSO paths must work
> simultaneously (we don't know which path an egress skb will take).
> 
> So, with these patches, the unmapping is done using dma_unmap_page() which
> is only correct if the skb took the non-TSO paths. In other words,
> these fixes are wrong (although I have no idea the effect of
> using dma_unmap_page on a mapping done with dma_map_single).
> 
> And the problem is that in the TSO path, the linear and the non-linear
> fragments use the same kind of descriptors, so we can't distinguish
> them in the cleanup, and can't decide if _single or _page should be used.
> 
> Any ideas?

Or, maybe, if davem would reply, we might come to the conclusion (as
I previously pointed out) that it's not a driver issue, but a netdev
core issue:

static netdev_features_t harmonize_features(struct sk_buff *skb,
        netdev_features_t features)
{
...
        if (skb->ip_summed != CHECKSUM_NONE &&
            !can_checksum_protocol(features, type)) {
                features &= ~NETIF_F_ALL_CSUM;
        } else if (illegal_highdma(skb->dev, skb)) {
                features &= ~NETIF_F_SG;
        }

The problem is when the first "if" is true (as is the case with IPv6 on
mv643xx_eth.c), we clear NETIF_F_ALL_CSUM, but leave NETIF_F_SG set.

Had that first if been false, we would've called illegal_highdma(), and
found that the skb contains some highmem fragments, but the device does
*not* have NETIF_F_HIGHDMA set, and so that second "if" would be true.
The result of that is NETIF_F_SG is cleared.

In this case, in validate_xmit_skb(), skb_needs_linearize() would be
false for a skb with fragments, causing the skb to be linearised.  I've
not completely traced the GSO path, but I'd assume that does something
similar (which I think skb_segment() handles.)

So, I'm wondering whether the above should be:

static netdev_features_t harmonize_features(struct sk_buff *skb,
        netdev_features_t features)
{
...
        if (skb->ip_summed != CHECKSUM_NONE &&
            !can_checksum_protocol(features, type)) {
                features &= ~NETIF_F_ALL_CSUM;
        }

        if (illegal_highdma(skb->dev, skb)) {
                features &= ~NETIF_F_SG;
        }

So that we get NETIF_F_SG turned off for all cases (irrespective of the
NETIF_F_ALL_CSUM test) if we see a skb with highmem and we the device
does not support highdma.

Yes, the code above hasn't changed in functionality for a long time, but
that doesn't mean it isn't buggy, and isn't the cause of our current bug.

However, it would be far better to have the drivers fixed for the sake
of performance - it's only this dma_map_page() thing that is the real
cause of the problem in these drivers.

Looking at TSO, it seems madness that it doesn't support highmem:

void tso_start(struct sk_buff *skb, struct tso_t *tso)
{
...
        tso->data = skb->data + hdr_len;
...
                tso->data = page_address(frag->page.p) + frag->page_offset;

Of course, this would all be a lot easier for drivers if all drivers had
to worry about was a struct page, offset and size, rather than having to
track whether each individual mapping of a transmit packet was mapped
with dma_map_single() or dma_map_page().

That all said, what I really care about is the regression which basically
makes 3.18 unusable on this hardware and seeing _some_ kind of resolution
to that regression - I don't care if it doesn't quite perform, what I care
about is that the network driver doesn't oops the kernel.

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply

* [PATCH net 1/1]: ks8695net.c and supporting files
From: Dick Hollenbeck @ 2015-01-21 23:51 UTC (permalink / raw)
  To: netdev

[-- Attachment #1: Type: text/plain, Size: 455 bytes --]

This patch is against a 3.14 kernel, but I think this should apply to most new kernels
very easily.

We make a board with this chip on it, so I am willing to be the maintainer of this driver
if it not made too difficult.  This will be the case for a few more years.

I don't know all your rules but I've been writing interrupt handlers and C code longer
than Linus, please go easy on me.  I will not be a regular contributor.

Thanks,

Dick Hollenbeck




[-- Attachment #2: ks8695net.patch --]
[-- Type: text/x-patch, Size: 62584 bytes --]

The ks8695 SOC rx_ring was overflowing under heavy network or cpu load.
This confused the rx DMA engine into sporadic behaviour, and a reboot was
required.

Increase the rx_ring size.

Interleave replentishment of the rx_ring descriptors as they are drained
making any given number of descriptors more effective.

Add ISRs for significant MAC DMA events so that the interrupt
status register does not have to be polled on every interrupt.

Add comments and improve style.


Signed-off-by: Dick Hollenbeck <dick@softplc.com>

=== modified file 'arch/arm/mach-ks8695/devices.c'
--- old/arch/arm/mach-ks8695/devices.c	2014-10-21 19:15:28 +0000
+++ new/arch/arm/mach-ks8695/devices.c	2015-01-21 21:29:53 +0000
@@ -39,20 +39,21 @@ static u64 eth_dmamask = 0xffffffffUL;

 static struct resource ks8695_wan_resources[] = {
	[0] = {
+		.name	= "WAN REGS",
		.start	= KS8695_WAN_PA,
		.end	= KS8695_WAN_PA + 0x00ff,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.name	= "WAN RX",
-		.start	= KS8695_IRQ_WAN_RX_STATUS,
-		.end	= KS8695_IRQ_WAN_RX_STATUS,
+		.start	= KS8695_IRQ_WAN_RX,
+		.end	= KS8695_IRQ_WAN_RX,
		.flags	= IORESOURCE_IRQ,
	},
	[2] = {
		.name	= "WAN TX",
-		.start	= KS8695_IRQ_WAN_TX_STATUS,
-		.end	= KS8695_IRQ_WAN_TX_STATUS,
+		.start	= KS8695_IRQ_WAN_TX,
+		.end	= KS8695_IRQ_WAN_TX,
		.flags	= IORESOURCE_IRQ,
	},
	[3] = {
@@ -83,20 +84,21 @@ static struct platform_device ks8695_wan

 static struct resource ks8695_lan_resources[] = {
	[0] = {
+		.name	= "LAN REGS",
		.start	= KS8695_LAN_PA,
		.end	= KS8695_LAN_PA + 0x00ff,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.name	= "LAN RX",
-		.start	= KS8695_IRQ_LAN_RX_STATUS,
-		.end	= KS8695_IRQ_LAN_RX_STATUS,
+		.start	= KS8695_IRQ_LAN_RX,
+		.end	= KS8695_IRQ_LAN_RX,
		.flags	= IORESOURCE_IRQ,
	},
	[2] = {
		.name	= "LAN TX",
-		.start	= KS8695_IRQ_LAN_TX_STATUS,
-		.end	= KS8695_IRQ_LAN_TX_STATUS,
+		.start	= KS8695_IRQ_LAN_TX,
+		.end	= KS8695_IRQ_LAN_TX,
		.flags	= IORESOURCE_IRQ,
	},
	[3] = {
@@ -127,14 +129,14 @@ static struct resource ks8695_hpna_resou
	},
	[1] = {
		.name	= "HPNA RX",
-		.start	= KS8695_IRQ_HPNA_RX_STATUS,
-		.end	= KS8695_IRQ_HPNA_RX_STATUS,
+		.start	= KS8695_IRQ_HPNA_RX,
+		.end	= KS8695_IRQ_HPNA_RX,
		.flags	= IORESOURCE_IRQ,
	},
	[2] = {
		.name	= "HPNA TX",
-		.start	= KS8695_IRQ_HPNA_TX_STATUS,
-		.end	= KS8695_IRQ_HPNA_TX_STATUS,
+		.start	= KS8695_IRQ_HPNA_TX,
+		.end	= KS8695_IRQ_HPNA_TX,
		.flags	= IORESOURCE_IRQ,
	},
 };

=== modified file 'arch/arm/mach-ks8695/include/mach/irqs.h'
--- old/arch/arm/mach-ks8695/include/mach/irqs.h	2014-10-21 19:15:28 +0000
+++ new/arch/arm/mach-ks8695/include/mach/irqs.h	2015-01-20 21:24:38 +0000
@@ -30,25 +30,25 @@
 #define KS8695_IRQ_UART_RX		9
 #define KS8695_IRQ_UART_LINE_STATUS	10
 #define KS8695_IRQ_UART_MODEM_STATUS	11
-#define KS8695_IRQ_LAN_RX_STOP		12
-#define KS8695_IRQ_LAN_TX_STOP		13
-#define KS8695_IRQ_LAN_RX_BUF		14
-#define KS8695_IRQ_LAN_TX_BUF		15
-#define KS8695_IRQ_LAN_RX_STATUS	16
-#define KS8695_IRQ_LAN_TX_STATUS	17
-#define KS8695_IRQ_HPNA_RX_STOP		18
-#define KS8695_IRQ_HPNA_TX_STOP		19
-#define KS8695_IRQ_HPNA_RX_BUF		20
-#define KS8695_IRQ_HPNA_TX_BUF		21
-#define KS8695_IRQ_HPNA_RX_STATUS	22
-#define KS8695_IRQ_HPNA_TX_STATUS	23
+#define KS8695_IRQ_LAN_RPS		12
+#define KS8695_IRQ_LAN_TPS		13
+#define KS8695_IRQ_LAN_RBU		14
+#define KS8695_IRQ_LAN_TBU		15
+#define KS8695_IRQ_LAN_RX		16
+#define KS8695_IRQ_LAN_TX		17
+#define KS8695_IRQ_HPNA_RPS		18
+#define KS8695_IRQ_HPNA_TPS		19
+#define KS8695_IRQ_HPNA_RBU		20
+#define KS8695_IRQ_HPNA_TBU		21
+#define KS8695_IRQ_HPNA_RX		22
+#define KS8695_IRQ_HPNA_TX		23
 #define KS8695_IRQ_BUS_ERROR		24
-#define KS8695_IRQ_WAN_RX_STOP		25
-#define KS8695_IRQ_WAN_TX_STOP		26
-#define KS8695_IRQ_WAN_RX_BUF		27
-#define KS8695_IRQ_WAN_TX_BUF		28
-#define KS8695_IRQ_WAN_RX_STATUS	29
-#define KS8695_IRQ_WAN_TX_STATUS	30
+#define KS8695_IRQ_WAN_RPS		25
+#define KS8695_IRQ_WAN_TPS		26
+#define KS8695_IRQ_WAN_RBU		27
+#define KS8695_IRQ_WAN_TBU		28
+#define KS8695_IRQ_WAN_RX		29
+#define KS8695_IRQ_WAN_TX		30
 #define KS8695_IRQ_WAN_LINK		31

 #endif

=== modified file 'arch/arm/mach-ks8695/include/mach/regs-lan.h'
--- old/arch/arm/mach-ks8695/include/mach/regs-lan.h	2014-10-21 19:15:28 +0000
+++ new/arch/arm/mach-ks8695/include/mach/regs-lan.h	2015-01-21 22:02:41 +0000
@@ -18,6 +18,7 @@
 #define KS8695_LAN_PA		(KS8695_IO_PA + KS8695_LAN_OFFSET)


+#if 0
 /*
  * LAN registers
  */
@@ -61,5 +62,6 @@
 /* Additional Station Address High */
 #define LMAAH_E			(1    << 31)	/* Address Enabled */

+#endif

 #endif

=== modified file 'arch/arm/mach-ks8695/include/mach/regs-wan.h'
--- old/arch/arm/mach-ks8695/include/mach/regs-wan.h	2014-10-21 19:15:28 +0000
+++ new/arch/arm/mach-ks8695/include/mach/regs-wan.h	2015-01-21 22:03:42 +0000
@@ -18,6 +18,7 @@
 #define KS8695_WAN_PA		(KS8695_IO_PA + KS8695_WAN_OFFSET)


+#if 0
 /*
  * WAN registers
  */
@@ -61,5 +62,6 @@
 /* Additional Station Address High */
 #define WMAAH_E			(1    << 31)	/* Address Enabled */

+#endif

 #endif

=== modified file 'arch/arm/mach-ks8695/irq.c'
--- old/arch/arm/mach-ks8695/irq.c	2014-10-21 19:15:28 +0000
+++ new/arch/arm/mach-ks8695/irq.c	2015-01-21 21:34:18 +0000
@@ -36,29 +36,33 @@

 static void ks8695_irq_mask(struct irq_data *d)
 {
-	unsigned long inten;
+	int inten = __raw_readl(KS8695_IRQ_VA + KS8695_INTEN);
+	int mask  = 1 << d->irq;

-	inten = __raw_readl(KS8695_IRQ_VA + KS8695_INTEN);
-	inten &= ~(1 << d->irq);
-
-	__raw_writel(inten, KS8695_IRQ_VA + KS8695_INTEN);
+	__raw_writel(inten & ~mask, KS8695_IRQ_VA + KS8695_INTEN);
 }

 static void ks8695_irq_unmask(struct irq_data *d)
 {
-	unsigned long inten;
-
-	inten = __raw_readl(KS8695_IRQ_VA + KS8695_INTEN);
-	inten |= (1 << d->irq);
+	int inten = __raw_readl(KS8695_IRQ_VA + KS8695_INTEN);
+	int mask  = 1 << d->irq;

-	__raw_writel(inten, KS8695_IRQ_VA + KS8695_INTEN);
+	__raw_writel(inten | mask, KS8695_IRQ_VA + KS8695_INTEN);
 }

 static void ks8695_irq_ack(struct irq_data *d)
 {
-	__raw_writel((1 << d->irq), KS8695_IRQ_VA + KS8695_INTST);
+	__raw_writel(1 << d->irq, KS8695_IRQ_VA + KS8695_INTST);
 }

+static void ks8695_irq_mask_ack(struct irq_data *d)
+{
+	int inten = __raw_readl(KS8695_IRQ_VA + KS8695_INTEN);
+	int mask  = 1 << d->irq;
+
+	__raw_writel(inten & ~mask, KS8695_IRQ_VA + KS8695_INTEN);
+	__raw_writel(mask, KS8695_IRQ_VA + KS8695_INTST);
+}

 static struct irq_chip ks8695_irq_level_chip;
 static struct irq_chip ks8695_irq_edge_chip;
@@ -66,19 +70,19 @@ static struct irq_chip ks8695_irq_edge_c

 static int ks8695_irq_set_type(struct irq_data *d, unsigned int type)
 {
-	unsigned long ctrl, mode;
-	unsigned short level_triggered = 0;
+	bool level_triggered = false;

-	ctrl = __raw_readl(KS8695_GPIO_VA + KS8695_IOPC);
+	int mode;
+	int ctrl = __raw_readl(KS8695_GPIO_VA + KS8695_IOPC);

	switch (type) {
		case IRQ_TYPE_LEVEL_HIGH:
			mode = IOPC_TM_HIGH;
-			level_triggered = 1;
+			level_triggered = true;
			break;
		case IRQ_TYPE_LEVEL_LOW:
			mode = IOPC_TM_LOW;
-			level_triggered = 1;
+			level_triggered = true;
			break;
		case IRQ_TYPE_EDGE_RISING:
			mode = IOPC_TM_RISING;
@@ -128,6 +132,7 @@ static int ks8695_irq_set_type(struct ir
 }

 static struct irq_chip ks8695_irq_level_chip = {
+	.irq_mask_ack	= ks8695_irq_mask_ack,
	.irq_ack	= ks8695_irq_mask,
	.irq_mask	= ks8695_irq_mask,
	.irq_unmask	= ks8695_irq_unmask,
@@ -135,6 +140,7 @@ static struct irq_chip ks8695_irq_level_
 };

 static struct irq_chip ks8695_irq_edge_chip = {
+	.irq_mask_ack	= ks8695_irq_mask_ack,
	.irq_ack	= ks8695_irq_ack,
	.irq_mask	= ks8695_irq_mask,
	.irq_unmask	= ks8695_irq_unmask,
@@ -143,7 +149,7 @@ static struct irq_chip ks8695_irq_edge_c

 void __init ks8695_init_irq(void)
 {
-	unsigned int irq;
+	int irq;

	/* Disable all interrupts initially */
	__raw_writel(0, KS8695_IRQ_VA + KS8695_INTMC);

=== modified file 'drivers/net/ethernet/micrel/ks8695net.c'
--- old/drivers/net/ethernet/micrel/ks8695net.c	2014-10-23 18:05:25 +0000
+++ new/drivers/net/ethernet/micrel/ks8695net.c	2015-01-21 22:55:31 +0000
@@ -14,8 +14,13 @@
  * Copyright 2008 Simtec Electronics
  *		  Daniel Silverstone <dsilvers@simtec.co.uk>
  *		  Vincent Sanders <vince@simtec.co.uk>
+ * Copyright 2015 SoftPLC Corporation
+ *		  Dick Hollenbeck <dick@softplc.com>
  */

+/* #define DEBUG */
+
+
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
 #include <linux/ioport.h>
@@ -41,17 +46,76 @@
 #include <asm/mach/irq.h>
 #include <mach/regs-irq.h>

-#include "ks8695net.h"

 #define MODULENAME	"ks8695_ether"
-#define MODULEVERSION	"1.02"
+#define MODULEVERSION	"1.03"

-/*
- * Transmit and device reset timeout, default 5 seconds.
- */
+
+#if defined(DEBUG)
+#define DPRINT(fmt...)	printk(fmt)
+/* #define DEBUG_IO */
+#else
+#define DPRINT(fmt...)	do { } while (0)
+#endif
+
+
+/* LAN and WAN MAC Registers (offsets from KS8695_LAN_PA or KS8695_WAN_PA)
+ */
+#define MAC_DTXC	0x00		/* DMA Transmit Control */
+#define MAC_DRXC	0x04		/* DMA Receive Control */
+#define MAC_DTSC	0x08		/* DMA Transmit Start Command */
+#define MAC_DRSC	0x0c		/* DMA Receive Start Command */
+#define MAC_TDLB	0x10		/* Transmit Descr List Base Address */
+#define MAC_RDLB	0x14		/* Receive Descr List Base Address */
+#define MAC_MAL		0x18		/* MAC Station Address Low */
+#define MAC_MAH		0x1c		/* MAC Station Address High */
+#define MAC_AAL_(n)	(0x80+((n)*8))	/* MAC Additional Address (0..15) LO */
+#define MAC_AAH_(n)	(0x84+((n)*8))	/* MAC Additional Address (0..15) HI */
+
+/* DMA Transmit Control Register Bits, applicable to offset MAC_DTXC in
+ * each MAC
+ */
+#define DTXC_TRST	(1 << 31)	/* Soft Reset */
+#define DTXC_TBS	(0x3f << 24)	/* Transmit Burst Size */
+#define DTXC_TUCG	(1 << 18)	/* Transmit UDP Checksum Generate */
+#define DTXC_TTCG	(1 << 17)	/* Transmit TCP Checksum Generate */
+#define DTXC_TICG	(1 << 16)	/* Transmit IP Checksum Generate */
+#define DTXC_TFCE	(1 <<  9)	/* Transmit Flow Control Enable */
+#define DTXC_TLB	(1 <<  8)	/* Loopback mode */
+#define DTXC_TEP	(1 <<  2)	/* Transmit Enable Padding */
+#define DTXC_TAC	(1 <<  1)	/* Transmit Add CRC */
+#define DTXC_TE		(1 <<  0)	/* TX Enable */
+
+/* DMA Receive Control Register Bits, applicable to offset MAC_DRXC in
+ * each MAC
+ */
+#define DRXC_RBS	(0x3f << 24)	/* Receive Burst Size */
+#define DRXC_RUCC	(1 << 18)	/* Receive UDP Checksum check */
+#define DRXC_RTCG	(1 << 17)	/* Receive TCP Checksum check */
+#define DRXC_RICG	(1 << 16)	/* Receive IP Checksum check */
+#define DRXC_RFCE	(1 <<  9)	/* Receive Flow Control Enable */
+#define DRXC_RB		(1 <<  6)	/* Receive Broadcast */
+#define DRXC_RM		(1 <<  5)	/* Receive Multicast */
+#define DRXC_RU		(1 <<  4)	/* Receive Unicast */
+#define DRXC_RERR	(1 <<  3)	/* Receive Error Frame */
+#define DRXC_RA		(1 <<  2)	/* Receive All */
+#define DRXC_RE		(1 <<  0)	/* RX Enable */
+
+/* Additional Station Address High Bit */
+#define AAH_E		(1 << 31)	/* Address Enabled */
+
+
+/* Transmit timeout in msecs */
 static int watchdog = 5000;

-/* Hardware structures */
+/* Reset timeout in loop counts */
+static const int reset_timeout = 500000;
+
+/* Descriptor structures are all little endian regardless of code or cpu mode,
+ * and are documented in section
+ * 4.1 Descriptor Lists and Data buffers
+ * of the KS8695PX REG DESCP pdf
+ */

 /**
  *	struct rx_ring_desc - Receive descriptor ring element
@@ -62,7 +126,25 @@ static int watchdog = 5000;
  */
 struct rx_ring_desc {
	__le32	status;
+#define RDES_OWN	(1 << 31)	/* Ownership: 1:DMA, 0:CPU */
+#define RDES_FS		(1 << 30)	/* First Descriptor */
+#define RDES_LS		(1 << 29)	/* Last Descriptor */
+#define RDES_IPE	(1 << 28)	/* IP Checksum error */
+#define RDES_TCPE	(1 << 27)	/* TCP Checksum error */
+#define RDES_UDPE	(1 << 26)	/* UDP Checksum error */
+#define RDES_ES		(1 << 25)	/* Error summary */
+#define RDES_MF		(1 << 24)	/* Multicast Frame */
+#define RDES_RE		(1 << 19)	/* MII Error reported */
+#define RDES_TL		(1 << 18)	/* Frame too Long */
+#define RDES_RF		(1 << 17)	/* Runt Frame */
+#define RDES_CE		(1 << 16)	/* CRC error */
+#define RDES_FT		(1 << 15)	/* Frame Type */
+#define RDES_FLEN	0x7ff		/* Frame Length */
+
	__le32	length;
+#define RDES_RER	(1 << 25)	/* Receive End of Ring */
+#define RDES_RBS	0x7ff		/* Receive Buffer Size */
+
	__le32	data_ptr;
	__le32	next_desc;
 };
@@ -76,7 +158,18 @@ struct rx_ring_desc {
  */
 struct tx_ring_desc {
	__le32	owner;
+#define TDES_OWN		(1 << 31)	/* Ownership: 1:DMA, 0:CPU */
+
	__le32	status;
+#define TDES_IC		(1 << 31)	/* Interrupt on Completion */
+#define TDES_FS		(1 << 30)	/* First Segment */
+#define TDES_LS		(1 << 29)	/* Last Segment */
+#define TDES_IPCKG	(1 << 28)	/* IP Checksum generate */
+#define TDES_TCPCKG	(1 << 27)	/* TCP Checksum generate */
+#define TDES_UDPCKG	(1 << 26)	/* UDP Checksum generate */
+#define TDES_TER	(1 << 25)	/* Transmit End of Ring */
+#define TDES_TBS	0x7ff		/* Transmit Buffer Size */
+
	__le32	data_ptr;
	__le32	next_desc;
 };
@@ -90,24 +183,26 @@ struct tx_ring_desc {
 struct ks8695_skbuff {
	struct sk_buff	*skb;
	dma_addr_t	dma_ptr;
-	u32		length;
+	unsigned	length;
 };

 /* Private device structure */

-#define MAX_TX_DESC 8
-#define MAX_TX_DESC_MASK 0x7
-#define MAX_RX_DESC 16
-#define MAX_RX_DESC_MASK 0xf
-
-/*napi_weight have better more than rx DMA buffers*/
-#define NAPI_WEIGHT   64
-
-#define MAX_RXBUF_SIZE 0x700
-
-#define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC)
-#define RX_RING_DMA_SIZE (sizeof(struct rx_ring_desc) * MAX_RX_DESC)
-#define RING_DMA_SIZE (TX_RING_DMA_SIZE + RX_RING_DMA_SIZE)
+#define NUM_TX_DESC		8
+#if defined(DEBUG)
+#define NUM_RX_DESC		8	/* 8 causes deliberate overflows */
+#else
+#define NUM_RX_DESC		32
+#endif
+
+/* napi_weight should be slightly more than NUM_RX_DESC */
+#define NAPI_WEIGHT		(NUM_RX_DESC + NUM_RX_DESC/2)
+
+#define RXBUF_SIZE		0x700
+
+#define TX_RING_DMA_SIZE		(sizeof(struct tx_ring_desc) * NUM_TX_DESC)
+#define RX_RING_DMA_SIZE		(sizeof(struct rx_ring_desc) * NUM_RX_DESC)
+#define RING_DMA_SIZE		(TX_RING_DMA_SIZE + RX_RING_DMA_SIZE)

 /**
  *	enum ks8695_dtype - Device type
@@ -126,7 +221,6 @@ enum ks8695_dtype {
  *	@in_suspend: Flag to indicate if we're suspending/resuming
  *	@ndev: The net_device for this interface
  *	@dev: The platform device object for this interface
- *	@dtype: The type of this device
  *	@io_regs: The ioremapped registers for this interface
  *      @napi : Add support NAPI for Rx
  *	@rx_irq_name: The textual name of the RX IRQ from the platform data
@@ -159,13 +253,17 @@ struct ks8695_priv {
	int in_suspend;
	struct net_device *ndev;
	struct device *dev;
-	enum ks8695_dtype dtype;
	void __iomem *io_regs;

	struct napi_struct	napi;

-	const char *rx_irq_name, *tx_irq_name, *link_irq_name;
-	int rx_irq, tx_irq, link_irq;
+	const char *rx_irq_name, *rbu_irq_name, *rps_irq_name;
+	const char *tx_irq_name, *tps_irq_name;
+	const char *link_irq_name;
+
+	int rx_irq, rbu_irq, rps_irq;
+	int tx_irq, tps_irq;
+	int link_irq;

	struct resource *regs_req, *phyiface_req;
	void __iomem *phyiface_regs;
@@ -177,67 +275,148 @@ struct ks8695_priv {
	int tx_ring_used;
	int tx_ring_next_slot;
	dma_addr_t tx_ring_dma;
-	struct ks8695_skbuff tx_buffers[MAX_TX_DESC];
+	struct ks8695_skbuff tx_buffers[NUM_TX_DESC];
	spinlock_t txq_lock;

	struct rx_ring_desc *rx_ring;
	dma_addr_t rx_ring_dma;
-	struct ks8695_skbuff rx_buffers[MAX_RX_DESC];
+	struct ks8695_skbuff rx_buffers[NUM_RX_DESC];
	int next_rx_desc_read;
	spinlock_t rx_lock;

+	int rx_mask;
+
	int msg_enable;
 };

-/* Register access */
+
+static void
+ks8695_timeout(struct net_device *ndev);
+
+inline int next_tx(int tx_descriptor_index)
+{
+#if	NUM_TX_DESC == 2   || NUM_TX_DESC == 4  || NUM_TX_DESC == 8  || \
+	NUM_TX_DESC == 16  || NUM_TX_DESC == 32 || NUM_TX_DESC == 64 || \
+	NUM_TX_DESC == 128 || NUM_TX_DESC == 256
+	return (tx_descriptor_index + 1) & (NUM_TX_DESC-1);
+#else
+	int ret = tx_descriptor_index + 1;
+	return ret < NUM_TX_DESC ? ret : 0;
+#endif
+}
+
+
+inline int next_rx(int rx_descriptor_index)
+{
+#if	NUM_RX_DESC == 2   || NUM_RX_DESC == 4  || NUM_RX_DESC == 8  || \
+	NUM_RX_DESC == 16  || NUM_RX_DESC == 32 || NUM_RX_DESC == 64 || \
+	NUM_RX_DESC == 128 || NUM_RX_DESC == 256
+	return (rx_descriptor_index + 1) & (NUM_RX_DESC-1);
+#else
+	int ret = rx_descriptor_index + 1;
+	return ret < NUM_RX_DESC ? ret : 0;
+#endif
+}
+
+
+/* [Instrumented] Register access */

 /**
- *	ks8695_readreg - Read from a KS8695 ethernet register
+ *	ks8695_readmac - Read from a KS8695 ethernet register
  *	@ksp: The device to read from
  *	@reg: The register to read
  */
-static inline u32
-ks8695_readreg(struct ks8695_priv *ksp, int reg)
+static inline int
+ks8695_readmac(struct ks8695_priv *ksp, int reg)
 {
-	return readl(ksp->io_regs + reg);
+	int r = readl(ksp->io_regs + reg);
+#if 0 && defined(DEBUG_IO)
+	printk("r[%d]:%02x\n", reg, r);
+#endif
+	return r;
 }

 /**
- *	ks8695_writereg - Write to a KS8695 ethernet register
+ *	ks8695_writemac - Write to a KS8695 ethernet register
  *	@ksp: The device to write to
  *	@reg: The register to write
  *	@value: The value to write to the register
  */
 static inline void
-ks8695_writereg(struct ks8695_priv *ksp, int reg, u32 value)
+ks8695_writemac(struct ks8695_priv *ksp, int reg, int value)
 {
	writel(value, ksp->io_regs + reg);
+#if 0 && defined(DEBUG_IO)
+	printk("w[%d]:%02x\n", reg, value);
+#endif
+}
+
+
+/* [Instrumented] SOC (non-MAC) register accesses */
+
+static inline int read_cpu(void __iomem *addr)
+{
+	int value = readl(addr);
+#if defined(DEBUG_IO)
+	int offset = (int) addr & 0xffff;
+	if (offset == 0xe208)
+		printk("isr>%x ", value);
+	else if (offset == 0xe204)
+		printk("ier>%x ", value);
+	else
+		printk("%x>%x ", offset, value);
+#endif
+	return value;
+}
+
+static inline void write_cpu(int value, void __iomem *addr)
+{
+	writel(value, addr);
+#if defined(DEBUG_IO)
+	int offset = (int) addr & 0xffff;
+	if (offset == 0xe208)
+		printk("isr<%x ", value);
+	else if (offset == 0xe204)
+		printk("ier<%x ", value);
+	else
+		printk("%x<%x ", offset, value);
+#endif
 }

+
 /* Utility functions */

 /**
- *	ks8695_port_type - Retrieve port-type as user-friendly string
+ *	ks8695_port_name - Retrieve port-type as user-friendly string
  *	@ksp: The device to return the type for
  *
  *	Returns a string indicating which of the WAN, LAN or HPNA
  *	ports this device is likely to represent.
  */
 static const char *
-ks8695_port_type(struct ks8695_priv *ksp)
+ks8695_port_name(struct ks8695_priv *ksp)
 {
-	switch (ksp->dtype) {
-	case KS8695_DTYPE_LAN:
-		return "LAN";
-	case KS8695_DTYPE_WAN:
-		return "WAN";
-	case KS8695_DTYPE_HPNA:
-		return "HPNA";
+	switch (ksp->rx_irq) {
+	case KS8695_IRQ_LAN_RX:		return "LAN";
+	case KS8695_IRQ_WAN_RX:		return "WAN";
+	case KS8695_IRQ_HPNA_RX:		return "HPNA";
	}

	return "UNKNOWN";
 }

+static enum ks8695_dtype
+ks8695_port_type(struct ks8695_priv *ksp)
+{
+	switch (ksp->rx_irq) {
+	default:
+	case KS8695_IRQ_LAN_RX:		return KS8695_DTYPE_LAN;
+	case KS8695_IRQ_WAN_RX:		return KS8695_DTYPE_WAN;
+	case KS8695_IRQ_HPNA_RX:	return KS8695_DTYPE_HPNA;
+	}
+}
+
+
 /**
  *	ks8695_update_mac - Update the MAC registers in the device
  *	@ksp: The device to update
@@ -248,17 +427,54 @@ ks8695_port_type(struct ks8695_priv *ksp
 static void
 ks8695_update_mac(struct ks8695_priv *ksp)
 {
+	DPRINT("%s:\n", __func__+7);
+
	/* Update the HW with the MAC from the net_device */
	struct net_device *ndev = ksp->ndev;
-	u32 machigh, maclow;

-	maclow	= ((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
-		   (ndev->dev_addr[4] <<  8) | (ndev->dev_addr[5] <<  0));
-	machigh = ((ndev->dev_addr[0] <<  8) | (ndev->dev_addr[1] <<  0));
+	int mac_lo =	(ndev->dev_addr[2] << 24) |
+			(ndev->dev_addr[3] << 16) |
+			(ndev->dev_addr[4] <<  8) |
+			(ndev->dev_addr[5] <<  0);

-	ks8695_writereg(ksp, KS8695_MAL, maclow);
-	ks8695_writereg(ksp, KS8695_MAH, machigh);
+	int mac_hi =	(ndev->dev_addr[0] <<  8) |
+			(ndev->dev_addr[1] <<  0);

+	ks8695_writemac(ksp, MAC_MAL, mac_lo);
+	ks8695_writemac(ksp, MAC_MAH, mac_hi);
+}
+
+static void
+ks8695_refill_one_rxbuffer(struct ks8695_priv *ksp, int buff_n)
+{
+	struct sk_buff *skb = netdev_alloc_skb(ksp->ndev, RXBUF_SIZE);
+
+	ksp->rx_buffers[buff_n].skb = skb;
+
+	if (skb) {
+		dma_addr_t mapping = dma_map_single(ksp->dev,
+				skb->data, RXBUF_SIZE, DMA_FROM_DEVICE);
+
+		if (likely(!dma_mapping_error(ksp->dev, mapping))) {
+			ksp->rx_buffers[buff_n].dma_ptr = mapping;
+
+			/* Record new mapping into DMA ring */
+			ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping);
+
+			wmb();
+
+			/* give ownership of descriptor to DMA engine */
+			ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+		} else {
+			/* Failed to DMA map this SKB, try later */
+			dev_kfree_skb_irq(skb);
+			ksp->rx_buffers[buff_n].skb = NULL;
+			DPRINT("refill: no dma_map\n");
+		}
+	} else {
+		/* Failed to allocate, we'll try again later. */
+		DPRINT("refill: no skb\n");
+	}
 }

 /**
@@ -273,48 +489,18 @@ ks8695_update_mac(struct ks8695_priv *ks
 static void
 ks8695_refill_rxbuffers(struct ks8695_priv *ksp)
 {
-	/* Run around the RX ring, filling in any missing sk_buff's */
	int buff_n;

-	for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
-		if (!ksp->rx_buffers[buff_n].skb) {
-			struct sk_buff *skb =
-				netdev_alloc_skb(ksp->ndev, MAX_RXBUF_SIZE);
-			dma_addr_t mapping;
-
-			ksp->rx_buffers[buff_n].skb = skb;
-			if (skb == NULL) {
-				/* Failed to allocate one, perhaps
-				 * we'll try again later.
-				 */
-				break;
-			}
+	for (buff_n = 0; buff_n < NUM_RX_DESC; ++buff_n) {

-			mapping = dma_map_single(ksp->dev, skb->data,
-						 MAX_RXBUF_SIZE,
-						 DMA_FROM_DEVICE);
-			if (unlikely(dma_mapping_error(ksp->dev, mapping))) {
-				/* Failed to DMA map this SKB, try later */
-				dev_kfree_skb_irq(skb);
-				ksp->rx_buffers[buff_n].skb = NULL;
-				break;
-			}
-			ksp->rx_buffers[buff_n].dma_ptr = mapping;
-			ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE;
-
-			/* Record this into the DMA ring */
-			ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping);
-			ksp->rx_ring[buff_n].length =
-				cpu_to_le32(MAX_RXBUF_SIZE);
-
-			wmb();
-
-			/* And give ownership over to the hardware */
-			ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+		/* fill only missing ones */
+		if (!ksp->rx_buffers[buff_n].skb) {
+			ks8695_refill_one_rxbuffer(ksp, buff_n);
		}
	}
 }

+
 /* Maximum number of multicast addresses which the KS8695 HW supports */
 #define KS8695_NR_ADDRESSES	16

@@ -332,28 +518,46 @@ static void
 ks8695_init_partial_multicast(struct ks8695_priv *ksp,
			      struct net_device *ndev)
 {
-	u32 low, high;
-	int i;
+	DPRINT("%s:\n", __func__+7);
+
	struct netdev_hw_addr *ha;

-	i = 0;
+	int hi, lo;
+
+	int i = 0;
	netdev_for_each_mc_addr(ha, ndev) {
		/* Ran out of space in chip? */
		BUG_ON(i == KS8695_NR_ADDRESSES);
+		if (i < KS8695_NR_ADDRESSES) {
+			lo =	(ha->addr[2] << 24) |
+				(ha->addr[3] << 16) |
+				(ha->addr[4] <<  8) |
+				(ha->addr[5] <<  0);
+
+			hi =	(ha->addr[0] << 8) |
+				(ha->addr[1] << 0);
+
+			DPRINT(" %02x:%02x:%02x:%02x:%02x:%02x\n",
+				ha->addr[0],
+				ha->addr[1],
+				ha->addr[2],
+				ha->addr[3],
+				ha->addr[4],
+				ha->addr[5]
+				);

-		low = (ha->addr[2] << 24) | (ha->addr[3] << 16) |
-		      (ha->addr[4] << 8) | (ha->addr[5]);
-		high = (ha->addr[0] << 8) | (ha->addr[1]);
-
-		ks8695_writereg(ksp, KS8695_AAL_(i), low);
-		ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high);
+			ks8695_writemac(ksp, MAC_AAL_(i), lo);
+			ks8695_writemac(ksp, MAC_AAH_(i), AAH_E | hi);
+		}
		i++;
	}

+	DPRINT(" clearing %d mac addrs\n", KS8695_NR_ADDRESSES - i);
+
	/* Clear the remaining Additional Station Addresses */
	for (; i < KS8695_NR_ADDRESSES; i++) {
-		ks8695_writereg(ksp, KS8695_AAL_(i), 0);
-		ks8695_writereg(ksp, KS8695_AAH_(i), 0);
+		ks8695_writemac(ksp, MAC_AAL_(i), 0);
+		ks8695_writemac(ksp, MAC_AAH_(i), 0);
	}
 }

@@ -371,16 +575,23 @@ ks8695_init_partial_multicast(struct ks8
 static irqreturn_t
 ks8695_tx_irq(int irq, void *dev_id)
 {
+	DPRINT("T");
+
	struct net_device *ndev = (struct net_device *)dev_id;
	struct ks8695_priv *ksp = netdev_priv(ndev);
	int buff_n;

-	for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+	spin_lock(&ksp->txq_lock);
+
+	for (buff_n = 0; buff_n < NUM_TX_DESC; ++buff_n) {
		if (ksp->tx_buffers[buff_n].skb &&
		    !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) {
+
			rmb();
-			/* An SKB which is not owned by HW is present */
-			/* Update the stats for the net_device */
+
+			/* An SKB which is not owned by HW is present,
+			 * update the stats for the net_device
+			 */
			ndev->stats.tx_packets++;
			ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length;

@@ -392,36 +603,68 @@ ks8695_tx_irq(int irq, void *dev_id)
					 ksp->tx_buffers[buff_n].dma_ptr,
					 ksp->tx_buffers[buff_n].length,
					 DMA_TO_DEVICE);
+
			dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
			ksp->tx_buffers[buff_n].skb = NULL;
			ksp->tx_ring_used--;
		}
	}

-	netif_wake_queue(ndev);
+	spin_unlock(&ksp->txq_lock);
+
+	if (netif_queue_stopped(ndev))
+		netif_wake_queue(ndev);

	return IRQ_HANDLED;
 }

-/**
- *	ks8695_get_rx_enable_bit - Get rx interrupt enable/status bit
- *	@ksp: Private data for the KS8695 Ethernet
- *
- *    For KS8695 document:
- *    Interrupt Enable Register (offset 0xE204)
- *        Bit29 : WAN MAC Receive Interrupt Enable
- *        Bit16 : LAN MAC Receive Interrupt Enable
- *    Interrupt Status Register (Offset 0xF208)
- *        Bit29: WAN MAC Receive Status
- *        Bit16: LAN MAC Receive Status
- *    So, this Rx interrupt enable/status bit number is equal
- *    as Rx IRQ number.
- */
-static inline u32 ks8695_get_rx_enable_bit(struct ks8695_priv *ksp)
+
+static irqreturn_t
+ks8695_rbu_irq(int irq, void *dev_id)
 {
-	return ksp->rx_irq;
+	DPRINT("OOOOVERFLOWWWW\n");
+
+	struct net_device *ndev = (struct net_device *)dev_id;
+	struct ks8695_priv *ksp = netdev_priv(ndev);
+	int reg = ks8695_readmac(ksp, MAC_DRXC);
+
+	if (reg & DRXC_RE) {
+		/* Turn off the RX DMA, frames are arriving too fast
+		 * for polling to keep up.  Enable RX DMA much later,
+		 * after draining the ring. This gives the RX DMA time
+		 * to fully stop.
+		 */
+		 ks8695_writemac(ksp, MAC_DRXC, reg & ~DRXC_RE);
+
+		 ndev->stats.rx_over_errors++;
+	}
+
+	return IRQ_HANDLED;
 }

+static irqreturn_t
+ks8695_rps_irq(int irq, void *dev_id)
+{
+	DPRINT("RX_STOPPED\n");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+ks8695_tps_irq(int irq, void *dev_id)
+{
+	DPRINT("TX_STOPPED\n");
+
+	struct net_device *ndev = (struct net_device *)dev_id;
+	struct ks8695_priv *ksp = netdev_priv(ndev);
+
+	/* Re-start the TX DMA */
+	ks8695_writemac(ksp, MAC_DTSC, 0);
+
+	return IRQ_HANDLED;
+}
+
+
 /**
  *	ks8695_rx_irq - Receive IRQ handler
  *	@irq: The IRQ which went off (ignored)
@@ -429,60 +672,77 @@ static inline u32 ks8695_get_rx_enable_b
  *
  *	Inform NAPI that packet reception needs to be scheduled
  */
-
 static irqreturn_t
 ks8695_rx_irq(int irq, void *dev_id)
 {
+	/* DPRINT("R%x ", read_cpu(KS8695_IRQ_VA + KS8695_INTST)); */
+	DPRINT("R");
+
	struct net_device *ndev = (struct net_device *)dev_id;
	struct ks8695_priv *ksp = netdev_priv(ndev);

	spin_lock(&ksp->rx_lock);

	if (napi_schedule_prep(&ksp->napi)) {
-		unsigned long status = readl(KS8695_IRQ_VA + KS8695_INTEN);
-		unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp);
-		/*disable rx interrupt*/
-		status &= ~mask_bit;
-		writel(status , KS8695_IRQ_VA + KS8695_INTEN);
+#if 1
+		/* a faster disable_irq_nosync(ksp->rx_irq); */
+		int ier  = read_cpu(KS8695_IRQ_VA + KS8695_INTEN);
+		write_cpu(ier & ~ksp->rx_mask, KS8695_IRQ_VA + KS8695_INTEN);
+#else
+		disable_irq_nosync(ksp->rx_irq);
+#endif
		__napi_schedule(&ksp->napi);
+	} else {
+		/* http://www.linuxfoundation.org/collaborate/workgroups/networking/napi#NAPI_Driver_design */
+		DPRINT("%s: why another interrupt?\n", __func__);
	}

	spin_unlock(&ksp->rx_lock);
+
	return IRQ_HANDLED;
 }

+
 /**
  *	ks8695_rx - Receive packets called by NAPI poll method
  *	@ksp: Private data for the KS8695 Ethernet
- *	@budget: Number of packets allowed to process
+ *	@budget: Number of packets allowed to process, set in here
+ *       as NAPI_WEIGHT
  */
-static int ks8695_rx(struct ks8695_priv *ksp, int budget)
+inline int ks8695_rx(struct ks8695_priv *ksp, int budget)
 {
+	/* DPRINT("X"); */
+
	struct net_device *ndev = ksp->ndev;
	struct sk_buff *skb;
	int buff_n;
-	u32 flags;
+	int flags;
	int pktlen;
	int received = 0;
+	int loop_count;

-	buff_n = ksp->next_rx_desc_read;
-	while (received < budget
-			&& ksp->rx_buffers[buff_n].skb
-			&& (!(ksp->rx_ring[buff_n].status &
-					cpu_to_le32(RDES_OWN)))) {
-			rmb();
-			flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
+	buff_n = ksp->next_rx_desc_read;   /* next expected frame */

-			/* Found an SKB which we own, this means we
-			 * received a packet
-			 */
-			if ((flags & (RDES_FS | RDES_LS)) !=
-			    (RDES_FS | RDES_LS)) {
+	/* budget is NAPI_WEIGHT */
+	for (loop_count = 0; loop_count < budget;
+			++loop_count, buff_n = next_rx(buff_n)) {
+		if (unlikely(!ksp->rx_buffers[buff_n].skb)) {
+			DPRINT("refill_%u\n", buff_n);
+			ks8695_refill_one_rxbuffer(ksp, buff_n);
+		} else if (!((flags = ksp->rx_ring[buff_n].status) &
+				cpu_to_le32(RDES_OWN))) {
+
+			flags = cpu_to_le32(flags);
+
+			/* Found an SKB which we own, we received a packet */
+			if ((flags & (RDES_FS | RDES_LS)) != (RDES_FS | RDES_LS)) {
				/* This packet is not the first and
				 * the last segment.  Therefore it is
				 * a "spanning" packet and we can't
				 * handle it
				 */
+				DPRINT("rx:spanning\n");
+				ndev->stats.rx_errors++;
				goto rx_failure;
			}

@@ -497,10 +757,16 @@ static int ks8695_rx(struct ks8695_priv
					ndev->stats.rx_crc_errors++;
				if (flags & RDES_RE)
					ndev->stats.rx_missed_errors++;
-
+				DPRINT("rx:flags:%x\n", flags);
				goto rx_failure;
			}

+#if defined(DEBUG)
+			if (!flags) {
+				DPRINT("rx: empty frame!\n");
+			}
+#endif
+
			pktlen = flags & RDES_FLEN;
			pktlen -= 4; /* Drop the CRC */

@@ -522,11 +788,13 @@ static int ks8695_rx(struct ks8695_priv
			skb->protocol = eth_type_trans(skb, ndev);
			netif_receive_skb(skb);

+			ks8695_refill_one_rxbuffer(ksp, buff_n);
+
			/* Record stats */
			ndev->stats.rx_packets++;
			ndev->stats.rx_bytes += pktlen;
-			goto rx_finished;

+			goto rx_finished;
 rx_failure:
			/* This ring entry is an error, but we can
			 * re-use the skb
@@ -534,49 +802,83 @@ rx_failure:
			/* Give the ring entry back to the hardware */
			ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
 rx_finished:
+			/* Note which RX descriptor to start from next time */
+			ksp->next_rx_desc_read = next_rx(buff_n);
			received++;
-			buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
+		} else {
+			/* We've gone around ring fully once, and here we've
+			 * seen an un-filled descriptor, so we're ahead
+			 * of RX DMA, quit.
+			 */
+			if (loop_count >= NUM_RX_DESC)
+				break;
+		}
	}

-	/* And note which RX descriptor we last did */
-	ksp->next_rx_desc_read = buff_n;
-
-	/* And refill the buffers */
-	ks8695_refill_rxbuffers(ksp);
-
-	/* Kick the RX DMA engine, in case it became suspended */
-	ks8695_writereg(ksp, KS8695_DRSC, 0);
-
	return received;
 }

-
 /**
  *	ks8695_poll - Receive packet by NAPI poll method
  *	@ksp: Private data for the KS8695 Ethernet
  *	@budget: The remaining number packets for network subsystem
  *
- *     Invoked by the network core when it requests for new
+ *     Invoked by the network core when it requests new
  *     packets from the driver
  */
 static int ks8695_poll(struct napi_struct *napi, int budget)
 {
	struct ks8695_priv *ksp = container_of(napi, struct ks8695_priv, napi);
-	unsigned long  work_done;
-
-	unsigned long isr = readl(KS8695_IRQ_VA + KS8695_INTEN);
-	unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp);
-
-	work_done = ks8695_rx(ksp, budget);
+	int work_done = ks8695_rx(ksp, budget);

	if (work_done < budget) {
+		/* Re-enable receiver interrupts only if we have leisure
+		 * time and will stop polling.  Proof of leisure time is
+		 * having done less work than asked for.
+		 */
		unsigned long flags;
+
+		int reg;
+
		spin_lock_irqsave(&ksp->rx_lock, flags);
		__napi_complete(napi);
-		/*enable rx interrupt*/
-		writel(isr | mask_bit, KS8695_IRQ_VA + KS8695_INTEN);
+
+		/* We sometimes intentionally turn of RX MAC,
+		 * turn it back on now if off.
+		 */
+		reg = ks8695_readmac(ksp, MAC_DRXC);
+		if (!(reg & DRXC_RE)) {
+			DPRINT("Re-enable RMAC\n");
+#if 1
+			ks8695_writemac(ksp, MAC_DRXC, reg | DRXC_RE);
+		}
+#else
+			/* does not yet recover from overflow */
+			ks8695_timeout(ksp->ndev);
+		} else
+#endif
+
+		/* Kick the RX DMA engine, in case it became suspended.  Quirky,
+		 * seems to timeout from time to time, so rather than hooking
+		 * the RX STOP isr we simply re-enable it here since that's a
+		 * leaner code path.  Also note that we _must_ renable it after
+		 * we've disabled it ourselves under hi inbound traffic.  This
+		 * seems to be the best place for that also, right after setting
+		 * DRXC_RE above.
+		 */
+		ks8695_writemac(ksp, MAC_DRSC, 0);
+
+		/* re-enable rx interrupt */
+#if 1
+		/* faster enable_irq(ksp->rx_irq); */
+		reg  = read_cpu(KS8695_IRQ_VA + KS8695_INTEN);
+		write_cpu(reg | ksp->rx_mask, KS8695_IRQ_VA + KS8695_INTEN);
+#else
+		enable_irq(ksp->rx_irq);
+#endif
		spin_unlock_irqrestore(&ksp->rx_lock, flags);
	}
+
	return work_done;
 }

@@ -591,11 +893,12 @@ static int ks8695_poll(struct napi_struc
 static irqreturn_t
 ks8695_link_irq(int irq, void *dev_id)
 {
+	DPRINT("%s:\n", __func__+7);
+
	struct net_device *ndev = (struct net_device *)dev_id;
	struct ks8695_priv *ksp = netdev_priv(ndev);
-	u32 ctrl;
+	int ctrl = read_cpu(ksp->phyiface_regs + KS8695_WMC);

-	ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
	if (ctrl & WMC_WLS) {
		netif_carrier_on(ndev);
		if (netif_msg_link(ksp))
@@ -614,7 +917,6 @@ ks8695_link_irq(int irq, void *dev_id)
	return IRQ_HANDLED;
 }

-
 /* KS8695 Device functions */

 /**
@@ -627,32 +929,39 @@ ks8695_link_irq(int irq, void *dev_id)
 static void
 ks8695_reset(struct ks8695_priv *ksp)
 {
-	int reset_timeout = watchdog;
+	DPRINT("%s:\n", __func__+7);
+
+	int reset_count = reset_timeout;
+
	/* Issue the reset via the TX DMA control register */
-	ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST);
-	while (reset_timeout--) {
-		if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST))
+	ks8695_writemac(ksp, MAC_DTXC, DTXC_TRST);
+
+	while (reset_count--) {
+		if (!(ks8695_readmac(ksp, MAC_DTXC) & DTXC_TRST))
			break;
-		msleep(1);
+
+		DPRINT("w");
+		/* msleep(1); */
	}

-	if (reset_timeout < 0) {
-		dev_crit(ksp->dev,
-			 "Timeout waiting for DMA engines to reset\n");
+	if (reset_count < 0) {
+		dev_crit(ksp->dev, "Timeout waiting for DMA engines to reset\n");
		/* And blithely carry on */
	}

	/* Definitely wait long enough before attempting to program
	 * the engines
	 */
-	msleep(10);
+	/* msleep(10); */

	/* RX: unicast and broadcast */
-	ks8695_writereg(ksp, KS8695_DRXC, DRXC_RU | DRXC_RB);
+	ks8695_writemac(ksp, MAC_DRXC, DRXC_RU | DRXC_RB);
+
	/* TX: pad and add CRC */
-	ks8695_writereg(ksp, KS8695_DTXC, DTXC_TEP | DTXC_TAC);
+	ks8695_writemac(ksp, MAC_DTXC, DTXC_TEP | DTXC_TAC);
 }

+
 /**
  *	ks8695_shutdown - Shut down a KS8695 ethernet interface
  *	@ksp: The interface to shut down
@@ -664,25 +973,30 @@ ks8695_reset(struct ks8695_priv *ksp)
 static void
 ks8695_shutdown(struct ks8695_priv *ksp)
 {
-	u32 ctrl;
+	DPRINT("%s:\n", __func__+7);
	int buff_n;

	/* Disable packet transmission */
-	ctrl = ks8695_readreg(ksp, KS8695_DTXC);
-	ks8695_writereg(ksp, KS8695_DTXC, ctrl & ~DTXC_TE);
+	int ctrl = ks8695_readmac(ksp, MAC_DTXC);
+	ks8695_writemac(ksp, MAC_DTXC, ctrl & ~DTXC_TE);

	/* Disable packet reception */
-	ctrl = ks8695_readreg(ksp, KS8695_DRXC);
-	ks8695_writereg(ksp, KS8695_DRXC, ctrl & ~DRXC_RE);
+	ctrl = ks8695_readmac(ksp, MAC_DRXC);
+	ks8695_writemac(ksp, MAC_DRXC, ctrl & ~DRXC_RE);

	/* Release the IRQs */
	free_irq(ksp->rx_irq, ksp->ndev);
+	free_irq(ksp->rbu_irq, ksp->ndev);
+	free_irq(ksp->rps_irq, ksp->ndev);
+
	free_irq(ksp->tx_irq, ksp->ndev);
+	free_irq(ksp->tps_irq, ksp->ndev);
+
	if (ksp->link_irq != -1)
		free_irq(ksp->link_irq, ksp->ndev);

	/* Throw away any pending TX packets */
-	for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+	for (buff_n = 0; buff_n < NUM_TX_DESC; ++buff_n) {
		if (ksp->tx_buffers[buff_n].skb) {
			/* Remove this SKB from the TX ring */
			ksp->tx_ring[buff_n].owner = 0;
@@ -700,7 +1014,7 @@ ks8695_shutdown(struct ks8695_priv *ksp)
	}

	/* Purge the RX buffers */
-	for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+	for (buff_n = 0; buff_n < NUM_RX_DESC; ++buff_n) {
		if (ksp->rx_buffers[buff_n].skb) {
			/* Remove the SKB from the RX ring */
			ksp->rx_ring[buff_n].status = 0;
@@ -731,9 +1045,8 @@ static int
 ks8695_setup_irq(int irq, const char *irq_name,
		 irq_handler_t handler, struct net_device *ndev)
 {
-	int ret;
-
-	ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev);
+	DPRINT("%s(irq=%d, name=%s)\n", __func__+7, irq, irq_name);
+	int ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev);

	if (ret) {
		dev_err(&ndev->dev, "failure to request IRQ %d\n", irq);
@@ -743,6 +1056,7 @@ ks8695_setup_irq(int irq, const char *ir
	return 0;
 }

+
 /**
  *	ks8695_init_net - Initialise a KS8695 ethernet interface
  *	@ksp: The interface to initialise
@@ -754,24 +1068,43 @@ ks8695_setup_irq(int irq, const char *ir
 static int
 ks8695_init_net(struct ks8695_priv *ksp)
 {
+	DPRINT("%s:\n", __func__+7);
+
	int ret;
-	u32 ctrl;
+	int ctrl;

	ks8695_refill_rxbuffers(ksp);

	/* Initialise the DMA engines */
-	ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma);
-	ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma);
+	ks8695_writemac(ksp, MAC_RDLB, (u32) ksp->rx_ring_dma);
+	ks8695_writemac(ksp, MAC_TDLB, (u32) ksp->tx_ring_dma);

	/* Request the IRQs */
-	ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name,
-			       ks8695_rx_irq, ksp->ndev);
+	ret = ks8695_setup_irq(ksp->rx_irq,
+			ksp->rx_irq_name, ks8695_rx_irq, ksp->ndev);
	if (ret)
		return ret;
-	ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name,
-			       ks8695_tx_irq, ksp->ndev);
+
+	ret = ks8695_setup_irq(ksp->rbu_irq,
+			ksp->rbu_irq_name, ks8695_rbu_irq, ksp->ndev);
+	if (ret)
+		return ret;
+
+	ret = ks8695_setup_irq(ksp->rps_irq,
+			ksp->rps_irq_name, ks8695_rps_irq, ksp->ndev);
	if (ret)
		return ret;
+
+	ret = ks8695_setup_irq(ksp->tx_irq,
+			ksp->tx_irq_name, ks8695_tx_irq, ksp->ndev);
+	if (ret)
+		return ret;
+
+	ret = ks8695_setup_irq(ksp->tps_irq,
+			ksp->tps_irq_name, ks8695_tps_irq, ksp->ndev);
+	if (ret)
+		return ret;
+
	if (ksp->link_irq != -1) {
		ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name,
				       ks8695_link_irq, ksp->ndev);
@@ -785,18 +1118,16 @@ ks8695_init_net(struct ks8695_priv *ksp)
	ksp->tx_ring_used = 0;

	/* Bring up transmission */
-	ctrl = ks8695_readreg(ksp, KS8695_DTXC);
-	/* Enable packet transmission */
-	ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE);
+	ctrl = ks8695_readmac(ksp, MAC_DTXC);
+	ks8695_writemac(ksp, MAC_DTXC, ctrl | DTXC_TE);

	/* Bring up the reception */
-	ctrl = ks8695_readreg(ksp, KS8695_DRXC);
-	/* Enable packet reception */
-	ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE);
-	/* And start the DMA engine */
-	ks8695_writereg(ksp, KS8695_DRSC, 0);
+	ctrl = ks8695_readmac(ksp, MAC_DRXC);
+	ks8695_writemac(ksp, MAC_DRXC, ctrl | DRXC_RE);
+
+	/* And start the RX DMA engine */
+	ks8695_writemac(ksp, MAC_DRSC, 0);

-	/* All done */
	return 0;
 }

@@ -810,6 +1141,8 @@ ks8695_init_net(struct ks8695_priv *ksp)
 static void
 ks8695_release_device(struct ks8695_priv *ksp)
 {
+	DPRINT("%s:\n", __func__+7);
+
	/* Unmap the registers */
	iounmap(ksp->io_regs);
	if (ksp->phyiface_regs)
@@ -818,6 +1151,7 @@ ks8695_release_device(struct ks8695_priv
	/* And release the request */
	release_resource(ksp->regs_req);
	kfree(ksp->regs_req);
+
	if (ksp->phyiface_req) {
		release_resource(ksp->phyiface_req);
		kfree(ksp->phyiface_req);
@@ -837,6 +1171,7 @@ ks8695_release_device(struct ks8695_priv
 static u32
 ks8695_get_msglevel(struct net_device *ndev)
 {
+	DPRINT("%s:\n", __func__+7);
	struct ks8695_priv *ksp = netdev_priv(ndev);

	return ksp->msg_enable;
@@ -850,6 +1185,7 @@ ks8695_get_msglevel(struct net_device *n
 static void
 ks8695_set_msglevel(struct net_device *ndev, u32 value)
 {
+	DPRINT("%s:\n", __func__+7);
	struct ks8695_priv *ksp = netdev_priv(ndev);

	ksp->msg_enable = value;
@@ -863,8 +1199,9 @@ ks8695_set_msglevel(struct net_device *n
 static int
 ks8695_wan_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
 {
+	DPRINT("%s:\n", __func__+7);
	struct ks8695_priv *ksp = netdev_priv(ndev);
-	u32 ctrl;
+	int ctrl;

	/* All ports on the KS8695 support these... */
	cmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
@@ -877,7 +1214,7 @@ ks8695_wan_get_settings(struct net_devic
	cmd->supported |= (SUPPORTED_Autoneg | SUPPORTED_Pause);
	cmd->phy_address = 0;

-	ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+	ctrl = read_cpu(ksp->phyiface_regs + KS8695_WMC);
	if ((ctrl & WMC_WAND) == 0) {
		/* auto-negotiation is enabled */
		cmd->advertising |= ADVERTISED_Autoneg;
@@ -918,8 +1255,9 @@ ks8695_wan_get_settings(struct net_devic
 static int
 ks8695_wan_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
 {
+	DPRINT("%s:\n", __func__+7);
	struct ks8695_priv *ksp = netdev_priv(ndev);
-	u32 ctrl;
+	int ctrl;

	if ((cmd->speed != SPEED_10) && (cmd->speed != SPEED_100))
		return -EINVAL;
@@ -940,7 +1278,7 @@ ks8695_wan_set_settings(struct net_devic
				ADVERTISED_100baseT_Full)) == 0)
			return -EINVAL;

-		ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+		ctrl = read_cpu(ksp->phyiface_regs + KS8695_WMC);

		ctrl &= ~(WMC_WAND | WMC_WANA100F | WMC_WANA100H |
			  WMC_WANA10F | WMC_WANA10H);
@@ -955,9 +1293,9 @@ ks8695_wan_set_settings(struct net_devic

		/* force a re-negotiation */
		ctrl |= WMC_WANR;
-		writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+		write_cpu(ctrl, ksp->phyiface_regs + KS8695_WMC);
	} else {
-		ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+		ctrl = read_cpu(ksp->phyiface_regs + KS8695_WMC);

		/* disable auto-negotiation */
		ctrl |= WMC_WAND;
@@ -968,7 +1306,7 @@ ks8695_wan_set_settings(struct net_devic
		if (cmd->duplex == DUPLEX_FULL)
			ctrl |= WMC_WANFF;

-		writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+		write_cpu(ctrl, ksp->phyiface_regs + KS8695_WMC);
	}

	return 0;
@@ -981,14 +1319,12 @@ ks8695_wan_set_settings(struct net_devic
 static int
 ks8695_wan_nwayreset(struct net_device *ndev)
 {
+	DPRINT("%s:\n", __func__+7);
	struct ks8695_priv *ksp = netdev_priv(ndev);
-	u32 ctrl;
-
-	ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+	int ctrl = read_cpu(ksp->phyiface_regs + KS8695_WMC);

	if ((ctrl & WMC_WAND) == 0)
-		writel(ctrl | WMC_WANR,
-		       ksp->phyiface_regs + KS8695_WMC);
+		write_cpu(ctrl | WMC_WANR, ksp->phyiface_regs + KS8695_WMC);
	else
		/* auto-negotiation not enabled */
		return -EINVAL;
@@ -1004,21 +1340,20 @@ ks8695_wan_nwayreset(struct net_device *
 static void
 ks8695_wan_get_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
 {
+	DPRINT("%s:\n", __func__+7);
	struct ks8695_priv *ksp = netdev_priv(ndev);
-	u32 ctrl;
-
-	ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+	int ctrl = read_cpu(ksp->phyiface_regs + KS8695_WMC);

	/* advertise Pause */
-	param->autoneg = (ctrl & WMC_WANAP);
+	param->autoneg = ctrl & WMC_WANAP;

	/* current Rx Flow-control */
-	ctrl = ks8695_readreg(ksp, KS8695_DRXC);
-	param->rx_pause = (ctrl & DRXC_RFCE);
+	ctrl = ks8695_readmac(ksp, MAC_DRXC);
+	param->rx_pause = ctrl & DRXC_RFCE;

	/* current Tx Flow-control */
-	ctrl = ks8695_readreg(ksp, KS8695_DTXC);
-	param->tx_pause = (ctrl & DTXC_TFCE);
+	ctrl = ks8695_readmac(ksp, MAC_DTXC);
+	param->tx_pause = ctrl & DTXC_TFCE;
 }

 /**
@@ -1029,27 +1364,37 @@ ks8695_wan_get_pause(struct net_device *
 static void
 ks8695_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
 {
+	DPRINT("%s:\n", __func__+7);
	strlcpy(info->driver, MODULENAME, sizeof(info->driver));
	strlcpy(info->version, MODULEVERSION, sizeof(info->version));
	strlcpy(info->bus_info, dev_name(ndev->dev.parent),
		sizeof(info->bus_info));
 }

+#if 0
+static void
+ks8695_get_stats(struct net_device *ndev, struct ethtool_stats *info, u64 *what)
+{
+}
+#endif
+
 static const struct ethtool_ops ks8695_ethtool_ops = {
-	.get_msglevel	= ks8695_get_msglevel,
-	.set_msglevel	= ks8695_set_msglevel,
-	.get_drvinfo	= ks8695_get_drvinfo,
+	.get_msglevel		= ks8695_get_msglevel,
+	.set_msglevel		= ks8695_set_msglevel,
+	.get_drvinfo		= ks8695_get_drvinfo,
+//	.get_ethtool_stats  	= ks8695_get_stats,
 };

 static const struct ethtool_ops ks8695_wan_ethtool_ops = {
-	.get_msglevel	= ks8695_get_msglevel,
-	.set_msglevel	= ks8695_set_msglevel,
-	.get_settings	= ks8695_wan_get_settings,
-	.set_settings	= ks8695_wan_set_settings,
-	.nway_reset	= ks8695_wan_nwayreset,
-	.get_link	= ethtool_op_get_link,
-	.get_pauseparam = ks8695_wan_get_pause,
-	.get_drvinfo	= ks8695_get_drvinfo,
+	.get_msglevel		= ks8695_get_msglevel,
+	.set_msglevel		= ks8695_set_msglevel,
+	.get_settings		= ks8695_wan_get_settings,
+	.set_settings		= ks8695_wan_set_settings,
+	.nway_reset		= ks8695_wan_nwayreset,
+	.get_link		= ethtool_op_get_link,
+	.get_pauseparam		= ks8695_wan_get_pause,
+	.get_drvinfo		= ks8695_get_drvinfo,
+//	.get_ethtool_stats	= ks8695_get_stats,
 };

 /* Network device interface functions */
@@ -1062,6 +1407,7 @@ static const struct ethtool_ops ks8695_w
 static int
 ks8695_set_mac(struct net_device *ndev, void *addr)
 {
+	DPRINT("%s:\n", __func__+7);
	struct ks8695_priv *ksp = netdev_priv(ndev);
	struct sockaddr *address = addr;

@@ -1089,9 +1435,20 @@ static void
 ks8695_set_multicast(struct net_device *ndev)
 {
	struct ks8695_priv *ksp = netdev_priv(ndev);
-	u32 ctrl;
+	int ctrl = ks8695_readmac(ksp, MAC_DRXC);
+
+#if defined(DEBUG)
+	{
+		char  msg[128];

-	ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+		msg[0] = 0;
+		/* not time critical */
+		strcat(msg, ndev->flags & IFF_PROMISC ?  " promisc=Y"   : " promisc=N");
+		strcat(msg, ndev->flags & IFF_ALLMULTI ? " multicast=Y" : " multicast=N");
+
+		DPRINT("%s:%s\n", __func__+7, msg);
+	}
+#endif

	if (ndev->flags & IFF_PROMISC) {
		/* enable promiscuous mode */
@@ -1115,7 +1472,7 @@ ks8695_set_multicast(struct net_device *
		ks8695_init_partial_multicast(ksp, ndev);
	}

-	ks8695_writereg(ksp, KS8695_DRXC, ctrl);
+	ks8695_writemac(ksp, MAC_DRXC, ctrl);
 }

 /**
@@ -1127,6 +1484,7 @@ ks8695_set_multicast(struct net_device *
 static void
 ks8695_timeout(struct net_device *ndev)
 {
+	DPRINT("%s:\n", __func__);
	struct ks8695_priv *ksp = netdev_priv(ndev);

	netif_stop_queue(ndev);
@@ -1148,6 +1506,7 @@ ks8695_timeout(struct net_device *ndev)
	netif_start_queue(ndev);
 }

+
 /**
  *	ks8695_start_xmit - Start a packet transmission
  *	@skb: The packet to transmit
@@ -1162,16 +1521,19 @@ ks8695_start_xmit(struct sk_buff *skb, s
 {
	struct ks8695_priv *ksp = netdev_priv(ndev);
	int buff_n;
+	int status;
	dma_addr_t dmap;

	spin_lock_irq(&ksp->txq_lock);

-	if (ksp->tx_ring_used == MAX_TX_DESC) {
+	if (ksp->tx_ring_used >= NUM_TX_DESC) {
		/* Somehow we got entered when we have no room */
		spin_unlock_irq(&ksp->txq_lock);
+		DPRINT("#########################\n");
		return NETDEV_TX_BUSY;
	}

+
	buff_n = ksp->tx_ring_next_slot;

	BUG_ON(ksp->tx_buffers[buff_n].skb);
@@ -1182,10 +1544,12 @@ ks8695_start_xmit(struct sk_buff *skb, s
		spin_unlock_irq(&ksp->txq_lock);
		dev_dbg(ksp->dev, "%s: Could not map DMA memory for "\
			"transmission, trying later\n", ndev->name);
+		DPRINT("!!!!!!!!!!!!!!!!!!!!!!!!!\n");
		return NETDEV_TX_BUSY;
	}

	ksp->tx_buffers[buff_n].dma_ptr = dmap;
+
	/* Mapped okay, store the buffer pointer and length for later */
	ksp->tx_buffers[buff_n].skb = skb;
	ksp->tx_buffers[buff_n].length = skb->len;
@@ -1193,25 +1557,30 @@ ks8695_start_xmit(struct sk_buff *skb, s
	/* Fill out the TX descriptor */
	ksp->tx_ring[buff_n].data_ptr =
		cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr);
-	ksp->tx_ring[buff_n].status =
-		cpu_to_le32(TDES_IC | TDES_FS | TDES_LS |
-			    (skb->len & TDES_TBS));
+
+	status = TDES_IC | TDES_FS | TDES_LS | (skb->len & TDES_TBS);
+	if (NUM_TX_DESC-1 == buff_n)
+		status |= TDES_TER;
+
+	ksp->tx_ring[buff_n].status = cpu_to_le32(status);

	wmb();

-	/* Hand it over to the hardware */
+	/* ownership to TX DMA */
	ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN);

-	if (++ksp->tx_ring_used == MAX_TX_DESC)
+	if (++ksp->tx_ring_used >= NUM_TX_DESC) {
+		DPRINT("stop_queue ............................\n");
		netif_stop_queue(ndev);
+	}

-	/* Kick the TX DMA in case it decided to go IDLE */
-	ks8695_writereg(ksp, KS8695_DTSC, 0);
+	/* Kick the TX DMA back into action */
+	ks8695_writemac(ksp, MAC_DTSC, 0);

-	/* And update the next ring slot */
-	ksp->tx_ring_next_slot = (buff_n + 1) & MAX_TX_DESC_MASK;
+	ksp->tx_ring_next_slot = next_tx(buff_n);

	spin_unlock_irq(&ksp->txq_lock);
+
	return NETDEV_TX_OK;
 }

@@ -1225,6 +1594,8 @@ ks8695_start_xmit(struct sk_buff *skb, s
 static int
 ks8695_stop(struct net_device *ndev)
 {
+	DPRINT("%s:\n", __func__+7);
+
	struct ks8695_priv *ksp = netdev_priv(ndev);

	netif_stop_queue(ndev);
@@ -1246,6 +1617,7 @@ ks8695_stop(struct net_device *ndev)
 static int
 ks8695_open(struct net_device *ndev)
 {
+	DPRINT("%s:\n", __func__+7);
	struct ks8695_priv *ksp = netdev_priv(ndev);
	int ret;

@@ -1277,10 +1649,10 @@ ks8695_open(struct net_device *ndev)
 static void
 ks8695_init_switch(struct ks8695_priv *ksp)
 {
-	u32 ctrl;
+	DPRINT("%s:\n", __func__+7);

	/* Default value for SEC0 according to datasheet */
-	ctrl = 0x40819e00;
+	int ctrl = 0x40819e00;

	/* LED0 = Speed	 LED1 = Link/Activity */
	ctrl &= ~(SEC0_LLED1S | SEC0_LLED0S);
@@ -1289,10 +1661,10 @@ ks8695_init_switch(struct ks8695_priv *k
	/* Enable Switch */
	ctrl |= SEC0_ENABLE;

-	writel(ctrl, ksp->phyiface_regs + KS8695_SEC0);
+	write_cpu(ctrl, ksp->phyiface_regs + KS8695_SEC0);

	/* Defaults for SEC1 */
-	writel(0x9400100, ksp->phyiface_regs + KS8695_SEC1);
+	write_cpu(0x9400100, ksp->phyiface_regs + KS8695_SEC1);
 }

 /**
@@ -1305,11 +1677,11 @@ ks8695_init_switch(struct ks8695_priv *k
 static void
 ks8695_init_wan_phy(struct ks8695_priv *ksp)
 {
-	u32 ctrl;
+	DPRINT("%s:\n", __func__+7);

	/* Support auto-negotiation */
-	ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H |
-		WMC_WANA10F | WMC_WANA10H);
+	int ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H |
+			WMC_WANA10F | WMC_WANA10H);

	/* LED0 = Activity , LED1 = Link */
	ctrl |= (WLED0S_ACTIVITY | WLED1S_LINK);
@@ -1317,10 +1689,10 @@ ks8695_init_wan_phy(struct ks8695_priv *
	/* Restart Auto-negotiation */
	ctrl |= WMC_WANR;

-	writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+	write_cpu(ctrl, ksp->phyiface_regs + KS8695_WMC);

-	writel(0, ksp->phyiface_regs + KS8695_WPPM);
-	writel(0, ksp->phyiface_regs + KS8695_PPS);
+	write_cpu(0, ksp->phyiface_regs + KS8695_WPPM);
+	write_cpu(0, ksp->phyiface_regs + KS8695_PPS);
 }

 static const struct net_device_ops ks8695_netdev_ops = {
@@ -1349,13 +1721,17 @@ static const struct net_device_ops ks869
 static int
 ks8695_probe(struct platform_device *pdev)
 {
+	DPRINT("%s:\n", __func__+7);
+
	struct ks8695_priv *ksp;
	struct net_device *ndev;
	struct resource *regs_res, *phyiface_res;
-	struct resource *rxirq_res, *txirq_res, *linkirq_res;
+	struct resource *rxirq_res, *txirq_res;
+	struct resource *linkirq_res;				/* optional */
+
	int ret = 0;
	int buff_n;
-	u32 machigh, maclow;
+	u32 mac_hi, mac_lo;

	/* Initialise a net_device */
	ndev = alloc_etherdev(sizeof(struct ks8695_priv));
@@ -1374,12 +1750,12 @@ ks8695_probe(struct platform_device *pde
	ksp->msg_enable = NETIF_MSG_LINK;

	/* Retrieve resources */
-	regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	phyiface_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	regs_res      = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	phyiface_res  = platform_get_resource(pdev, IORESOURCE_MEM, 1);

-	rxirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-	txirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
-	linkirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+	rxirq_res     = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	txirq_res     = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+	linkirq_res   = platform_get_resource(pdev, IORESOURCE_IRQ, 2);

	if (!(regs_res && rxirq_res && txirq_res)) {
		dev_err(ksp->dev, "insufficient resources\n");
@@ -1430,31 +1806,65 @@ ks8695_probe(struct platform_device *pde
	}

	ksp->rx_irq = rxirq_res->start;
-	ksp->rx_irq_name = rxirq_res->name ? rxirq_res->name : "Ethernet RX";
+	ksp->rx_irq_name = rxirq_res->name;
+	ksp->rx_mask = 1 << ksp->rx_irq;
+
	ksp->tx_irq = txirq_res->start;
-	ksp->tx_irq_name = txirq_res->name ? txirq_res->name : "Ethernet TX";
-	ksp->link_irq = (linkirq_res ? linkirq_res->start : -1);
-	ksp->link_irq_name = (linkirq_res && linkirq_res->name) ?
-		linkirq_res->name : "Ethernet Link";
+	ksp->tx_irq_name = txirq_res->name;
+
+	ksp->link_irq = linkirq_res ? linkirq_res->start : -1;
+	ksp->link_irq_name = linkirq_res ? linkirq_res->name : NULL;
+
+	/* The rbu, rps, and tps irq nums are at common offsets from rx_irq
+	 * accross all 3 ports
+	 */
+	ksp->rbu_irq = ksp->rx_irq - 2;
+	ksp->rps_irq = ksp->rx_irq - 4;
+	ksp->tps_irq = ksp->rx_irq - 3;
+
+	static const char * const rbu[] = {
+		[KS8695_DTYPE_WAN]  = "WAN rx_ring overflow",
+		[KS8695_DTYPE_LAN]  = "LAN rx_ring overflow",
+		[KS8695_DTYPE_HPNA] = "HPNA rx_ring overflow",
+	};
+
+	static const char * const rps[] = {
+		[KS8695_DTYPE_WAN]  = "WAN rx_MAC stopped",
+		[KS8695_DTYPE_LAN]  = "LAN rx_MAC stopped",
+		[KS8695_DTYPE_HPNA] = "HPNA rx_MAC stopped",
+	};
+
+	static const char * const tps[] = {
+		[KS8695_DTYPE_WAN]  = "WAN tx_MAC stopped",
+		[KS8695_DTYPE_LAN]  = "LAN tx_MAC stopped",
+		[KS8695_DTYPE_HPNA] = "HPNA tx_MAC stopped",
+	};
+
+	ksp->rbu_irq_name = rbu[ks8695_port_type(ksp)];
+	ksp->rps_irq_name = rps[ks8695_port_type(ksp)];
+	ksp->tps_irq_name = tps[ks8695_port_type(ksp)];

	/* driver system setup */
	ndev->netdev_ops = &ks8695_netdev_ops;
-	ndev->watchdog_timeo	 = msecs_to_jiffies(watchdog);
+	ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+
+	DPRINT("%s: watchdog:%d\n", __func__, watchdog);

	netif_napi_add(ndev, &ksp->napi, ks8695_poll, NAPI_WEIGHT);

	/* Retrieve the default MAC addr from the chip. */
	/* The bootloader should have left it in there for us. */

-	machigh = ks8695_readreg(ksp, KS8695_MAH);
-	maclow = ks8695_readreg(ksp, KS8695_MAL);
+	mac_hi = ks8695_readmac(ksp, MAC_MAH);
+	mac_lo = ks8695_readmac(ksp, MAC_MAL);
+
+	ndev->dev_addr[0] = (u8) (mac_hi >> 8);
+	ndev->dev_addr[1] = (u8) (mac_hi >> 0);

-	ndev->dev_addr[0] = (machigh >> 8) & 0xFF;
-	ndev->dev_addr[1] = machigh & 0xFF;
-	ndev->dev_addr[2] = (maclow >> 24) & 0xFF;
-	ndev->dev_addr[3] = (maclow >> 16) & 0xFF;
-	ndev->dev_addr[4] = (maclow >> 8) & 0xFF;
-	ndev->dev_addr[5] = maclow & 0xFF;
+	ndev->dev_addr[2] = (u8) (mac_lo >> 24);
+	ndev->dev_addr[3] = (u8) (mac_lo >> 16);
+	ndev->dev_addr[4] = (u8) (mac_lo >> 8);
+	ndev->dev_addr[5] = (u8) (mac_lo >> 0);

	if (!is_valid_ether_addr(ndev->dev_addr))
		dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please "
@@ -1487,32 +1897,31 @@ ks8695_probe(struct platform_device *pde
	memset(ksp->rx_ring, 0, RX_RING_DMA_SIZE);

	/* Build the rings */
-	for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+	for (buff_n = 0; buff_n < NUM_TX_DESC; ++buff_n) {
		ksp->tx_ring[buff_n].next_desc =
			cpu_to_le32(ksp->tx_ring_dma +
-				    (sizeof(struct tx_ring_desc) *
-				     ((buff_n + 1) & MAX_TX_DESC_MASK)));
+				(sizeof(struct tx_ring_desc) * next_tx(buff_n)));
	}

-	for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+	for (buff_n = 0; buff_n < NUM_RX_DESC; ++buff_n) {
		ksp->rx_ring[buff_n].next_desc =
			cpu_to_le32(ksp->rx_ring_dma +
-				    (sizeof(struct rx_ring_desc) *
-				     ((buff_n + 1) & MAX_RX_DESC_MASK)));
+				(sizeof(struct rx_ring_desc) * next_rx(buff_n)));
+
+		ksp->rx_ring[buff_n].length = cpu_to_le32(RXBUF_SIZE);
+		ksp->rx_buffers[buff_n].length = RXBUF_SIZE;
	}
+	ksp->rx_ring[NUM_RX_DESC-1].length = cpu_to_le32(RDES_RER | RXBUF_SIZE);

	/* Initialise the port (physically) */
	if (ksp->phyiface_regs && ksp->link_irq == -1) {
		ks8695_init_switch(ksp);
-		ksp->dtype = KS8695_DTYPE_LAN;
		SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
	} else if (ksp->phyiface_regs && ksp->link_irq != -1) {
		ks8695_init_wan_phy(ksp);
-		ksp->dtype = KS8695_DTYPE_WAN;
		SET_ETHTOOL_OPS(ndev, &ks8695_wan_ethtool_ops);
	} else {
		/* No initialisation since HPNA does not have a PHY */
-		ksp->dtype = KS8695_DTYPE_HPNA;
		SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
	}

@@ -1522,7 +1931,7 @@ ks8695_probe(struct platform_device *pde

	if (ret == 0) {
		dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %pM\n",
-			 ks8695_port_type(ksp), ndev->dev_addr);
+			 ks8695_port_name(ksp), ndev->dev_addr);
	} else {
		/* Report the failure to register the net_device */
		dev_err(ksp->dev, "ks8695net: failed to register netdev.\n");
@@ -1550,6 +1959,8 @@ failure:
 static int
 ks8695_drv_suspend(struct platform_device *pdev, pm_message_t state)
 {
+	DPRINT("%s:\n", __func__+7);
+
	struct net_device *ndev = platform_get_drvdata(pdev);
	struct ks8695_priv *ksp = netdev_priv(ndev);

@@ -1573,6 +1984,8 @@ ks8695_drv_suspend(struct platform_devic
 static int
 ks8695_drv_resume(struct platform_device *pdev)
 {
+	DPRINT("%s:\n", __func__+7);
+
	struct net_device *ndev = platform_get_drvdata(pdev);
	struct ks8695_priv *ksp = netdev_priv(ndev);

@@ -1597,6 +2010,8 @@ ks8695_drv_resume(struct platform_device
 static int
 ks8695_drv_remove(struct platform_device *pdev)
 {
+	DPRINT("%s:\n", __func__+7);
+
	struct net_device *ndev = platform_get_drvdata(pdev);
	struct ks8695_priv *ksp = netdev_priv(ndev);

@@ -1629,5 +2044,5 @@ MODULE_DESCRIPTION("Micrel KS8695 (Centa
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:" MODULENAME);

-module_param(watchdog, int, 0400);
+module_param(watchdog, int, S_IRUGO);
 MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");

=== removed file 'drivers/net/ethernet/micrel/ks8695net.h'
--- old/drivers/net/ethernet/micrel/ks8695net.h	2014-10-21 19:15:28 +0000
+++ new/drivers/net/ethernet/micrel/ks8695net.h	1970-01-01 00:00:00 +0000
@@ -1,107 +0,0 @@
-/*
- * Micrel KS8695 (Centaur) Ethernet.
- *
- * Copyright 2008 Simtec Electronics
- *		  Daniel Silverstone <dsilvers@simtec.co.uk>
- *		  Vincent Sanders <vince@simtec.co.uk>
- */
-
-#ifndef KS8695NET_H
-#define KS8695NET_H
-
-/* Receive descriptor flags */
-#define RDES_OWN	(1 << 31)	/* Ownership */
-#define RDES_FS		(1 << 30)	/* First Descriptor */
-#define RDES_LS		(1 << 29)	/* Last Descriptor */
-#define RDES_IPE	(1 << 28)	/* IP Checksum error */
-#define RDES_TCPE	(1 << 27)	/* TCP Checksum error */
-#define RDES_UDPE	(1 << 26)	/* UDP Checksum error */
-#define RDES_ES		(1 << 25)	/* Error summary */
-#define RDES_MF		(1 << 24)	/* Multicast Frame */
-#define RDES_RE		(1 << 19)	/* MII Error reported */
-#define RDES_TL		(1 << 18)	/* Frame too Long */
-#define RDES_RF		(1 << 17)	/* Runt Frame */
-#define RDES_CE		(1 << 16)	/* CRC error */
-#define RDES_FT		(1 << 15)	/* Frame Type */
-#define RDES_FLEN	(0x7ff)		/* Frame Length */
-
-#define RDES_RER	(1 << 25)	/* Receive End of Ring */
-#define RDES_RBS	(0x7ff)		/* Receive Buffer Size */
-
-/* Transmit descriptor flags */
-
-#define TDES_OWN	(1 << 31)	/* Ownership */
-
-#define TDES_IC		(1 << 31)	/* Interrupt on Completion */
-#define TDES_FS		(1 << 30)	/* First Segment */
-#define TDES_LS		(1 << 29)	/* Last Segment */
-#define TDES_IPCKG	(1 << 28)	/* IP Checksum generate */
-#define TDES_TCPCKG	(1 << 27)	/* TCP Checksum generate */
-#define TDES_UDPCKG	(1 << 26)	/* UDP Checksum generate */
-#define TDES_TER	(1 << 25)	/* Transmit End of Ring */
-#define TDES_TBS	(0x7ff)		/* Transmit Buffer Size */
-
-/*
- * Network controller register offsets
- */
-#define KS8695_DTXC		(0x00)		/* DMA Transmit Control */
-#define KS8695_DRXC		(0x04)		/* DMA Receive Control */
-#define KS8695_DTSC		(0x08)		/* DMA Transmit Start Command */
-#define KS8695_DRSC		(0x0c)		/* DMA Receive Start Command */
-#define KS8695_TDLB		(0x10)		/* Transmit Descriptor List
-						 * Base Address
-						 */
-#define KS8695_RDLB		(0x14)		/* Receive Descriptor List
-						 * Base Address
-						 */
-#define KS8695_MAL		(0x18)		/* MAC Station Address Low */
-#define KS8695_MAH		(0x1c)		/* MAC Station Address High */
-#define KS8695_AAL_(n)		(0x80 + ((n)*8))	/* MAC Additional
-							 * Station Address
-							 * (0..15) Low
-							 */
-#define KS8695_AAH_(n)		(0x84 + ((n)*8))	/* MAC Additional
-							 * Station Address
-							 * (0..15) High
-							 */
-
-
-/* DMA Transmit Control Register */
-#define DTXC_TRST		(1    << 31)	/* Soft Reset */
-#define DTXC_TBS		(0x3f << 24)	/* Transmit Burst Size */
-#define DTXC_TUCG		(1    << 18)	/* Transmit UDP
-						 * Checksum Generate
-						 */
-#define DTXC_TTCG		(1    << 17)	/* Transmit TCP
-						 * Checksum Generate
-						 */
-#define DTXC_TICG		(1    << 16)	/* Transmit IP
-						 * Checksum Generate
-						 */
-#define DTXC_TFCE		(1    <<  9)	/* Transmit Flow
-						 * Control Enable
-						 */
-#define DTXC_TLB		(1    <<  8)	/* Loopback mode */
-#define DTXC_TEP		(1    <<  2)	/* Transmit Enable Padding */
-#define DTXC_TAC		(1    <<  1)	/* Transmit Add CRC */
-#define DTXC_TE			(1    <<  0)	/* TX Enable */
-
-/* DMA Receive Control Register */
-#define DRXC_RBS		(0x3f << 24)	/* Receive Burst Size */
-#define DRXC_RUCC		(1    << 18)	/* Receive UDP Checksum check */
-#define DRXC_RTCG		(1    << 17)	/* Receive TCP Checksum check */
-#define DRXC_RICG		(1    << 16)	/* Receive IP Checksum check */
-#define DRXC_RFCE		(1    <<  9)	/* Receive Flow Control
-						 * Enable
-						 */
-#define DRXC_RB			(1    <<  6)	/* Receive Broadcast */
-#define DRXC_RM			(1    <<  5)	/* Receive Multicast */
-#define DRXC_RU			(1    <<  4)	/* Receive Unicast */
-#define DRXC_RERR		(1    <<  3)	/* Receive Error Frame */
-#define DRXC_RA			(1    <<  2)	/* Receive All */
-#define DRXC_RE			(1    <<  0)	/* RX Enable */
-
-/* Additional Station Address High */
-#define AAH_E			(1    << 31)	/* Address Enabled */
-
-#endif /* KS8695NET_H */


^ permalink raw reply

* Re: [PATCH net-next 0/3] openvswitch: Add STT support.
From: Vincent JARDIN @ 2015-01-21 23:46 UTC (permalink / raw)
  To: Jesse Gross, Tom Herbert; +Cc: Pravin Shelar, David Miller, Linux Netdev List
In-Reply-To: <CAEP_g=_B5xTcfONUP4PTw91PdJKx_cN_+Qdd4F6300aXVyzLZg@mail.gmail.com>

Jesse, Tom,

On 21/01/2015 23:14, Jesse Gross wrote:
>> I'm not going to try to draw conclusions from data which is obviously
>> >biased and incomplete. If you want to move forward on this, then just
>> >provide network interface for STT so we can independently run our own
>> >comparisons against other encapsulations like we've been doing all
>> >along.
> You have the source code, so you are totally free to run whatever
> tests you like to draw your own conclusions. Personally, I find a more
> than doubling of performance in the environments that I have seen
> compelling. Your mileage may vary.

+1 for STT in the kernel:
   - whatever the performances, it is needed because it happened to be 
used. If the patch can be optimized, someone will do and provide the 
related patches. The patch from Pravin is ok but...

   - ...I agree with Tom, a netdevice is a must have to ack't this 
patch. Such feature should not be added into openvswitch without its 
counter-part netdevice.

thank you,
   Vincent

^ permalink raw reply

* Re: [PATCH net-next v13 5/5] openvswitch: Add support for unique flow IDs.
From: Pravin Shelar @ 2015-01-21 23:41 UTC (permalink / raw)
  To: Joe Stringer; +Cc: netdev, LKML, dev@openvswitch.org
In-Reply-To: <CANr6G5yc1PZTAXHGrDJ-zLZf13UB4aef1hsQyK7RTPHzJMGoRg@mail.gmail.com>

On Wed, Jan 21, 2015 at 11:29 AM, Joe Stringer <joestringer@nicira.com> wrote:
> On 21 January 2015 at 10:31, Pravin Shelar <pshelar@nicira.com> wrote:
>> On Tue, Jan 20, 2015 at 10:32 AM, Joe Stringer <joestringer@nicira.com> wrote:
>>> Previously, flows were manipulated by userspace specifying a full,
>>> unmasked flow key. This adds significant burden onto flow
>>> serialization/deserialization, particularly when dumping flows.
>>>
>>> This patch adds an alternative way to refer to flows using a
>>> variable-length "unique flow identifier" (UFID). At flow setup time,
>>> userspace may specify a UFID for a flow, which is stored with the flow
>>> and inserted into a separate table for lookup, in addition to the
>>> standard flow table. Flows created using a UFID must be fetched or
>>> deleted using the UFID.
>>>
>>> All flow dump operations may now be made more terse with OVS_UFID_F_*
>>> flags. For example, the OVS_UFID_F_OMIT_KEY flag allows responses to
>>> omit the flow key from a datapath operation if the flow has a
>>> corresponding UFID. This significantly reduces the time spent assembling
>>> and transacting netlink messages. With all OVS_UFID_F_OMIT_* flags
>>> enabled, the datapath only returns the UFID and statistics for each flow
>>> during flow dump, increasing ovs-vswitchd revalidator performance by 40%
>>> or more.
>>>
>>> Signed-off-by: Joe Stringer <joestringer@nicira.com>
>>
>> Its almost ready. But I saw minor issues,
>> few checkpatch.pl failures.
>> in ovs_flow_cmd_new() we should use unmasked key to lookup in flow
>> table for legacy case.
>
> Thanks for review, I can send out a fresh version soon. Should I
> resend the whole series or is just a new version of this patch
> sufficient?

I think you have to send entire series. Dave will not apply series
patches from different versions.

^ permalink raw reply

* Re: [PATCH 2/2] net: mv643xx_eth: Fix highmem support in non-TSO egress path
From: Ezequiel Garcia @ 2015-01-21 23:34 UTC (permalink / raw)
  To: Russell King - ARM Linux; +Cc: netdev, David Miller, B38611, fabio.estevam
In-Reply-To: <20150121174049.GW26493@n2100.arm.linux.org.uk>

On 01/21/2015 02:40 PM, Russell King - ARM Linux wrote:
> On Wed, Jan 21, 2015 at 09:54:10AM -0300, Ezequiel Garcia wrote:
>> Commit 69ad0dd7af22b61d9e0e68e56b6290121618b0fb
>> Author: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
>> Date:   Mon May 19 13:59:59 2014 -0300
>>
>>     net: mv643xx_eth: Use dma_map_single() to map the skb fragments
>>
>> caused a nasty regression by removing the support for highmem skb
>> fragments. By using page_address() to get the address of a fragment's
>> page, we are assuming a lowmem page. However, such assumption is incorrect,
>> as fragments can be in highmem pages, resulting in very nasty issues.
>>
>> This commit fixes this by using the skb_frag_dma_map() helper,
>> which takes care of mapping the skb fragment properly.
> 
> This seems fine, so:
> 

I have just realised that the non-TSO and the TSO paths must work
simultaneously (we don't know which path an egress skb will take).

So, with these patches, the unmapping is done using dma_unmap_page() which
is only correct if the skb took the non-TSO paths. In other words,
these fixes are wrong (although I have no idea the effect of
using dma_unmap_page on a mapping done with dma_map_single).

And the problem is that in the TSO path, the linear and the non-linear
fragments use the same kind of descriptors, so we can't distinguish
them in the cleanup, and can't decide if _single or _page should be used.

Any ideas?

I guess we could keep track in some data structure of the type of mapping
on each descriptor. Or alternatively, avoid highmem fragments altogether
by mapping to a lowmem page.

I'll try to come up with some more patches following the first idea.

Sorry for the crappiness,
-- 
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

^ permalink raw reply

* Question on arping not updating the arp table in v3.10.61
From: Murali Karicheri @ 2015-01-21 23:30 UTC (permalink / raw)
  To: netdev

Hi,

I am facing an issue on my board that is using v3.10.61 Linux kernel. 
The arping -c 3 -f <IP address> doesn't update the arp table even though 
I get a proper response

root@keystone-evm:~# arping -b -c 3 -I eth0 -f 158.218.104.3
ARPING to 158.218.104.3 from 158.218.104.192 via eth0
Unicast reply from 158.218.104.3 [0:12:1:f7:d6:ff] 0.751ms
Sent 1 probe(s) (1 broadcast(s))
Received 1 replies (0 request(s), 0 broadcast(s))

root@keystone-evm:~# arp -n -a
? (158.218.104.1) at 00:00:0c:07:ac:68 [ether]  on eth0
? (158.218.104.233) at d4:be:d9:3d:b6:c1 [ether]  on eth0

But by enabling following sysfs entry, it works.

  echo 1 > /proc/sys/net/ipv4/conf/eth0/arp_accept

Is this the expected behavior? On my ubuntu Linux pc with v3.2 running I 
don't have to do this and it always update the arp table.

Thanks for your help.

-- 
Murali Karicheri
Linux Kernel, Texas Instruments

^ permalink raw reply

* BUG_ONs in net/core/skbuff.c in kernels 3.14.28/29 and 3.18.3
From: Chris Caputo @ 2015-01-21 23:09 UTC (permalink / raw)
  To: netdev

I opened a ticket for ixgbe at https://sourceforge.net/p/e1000/bugs/450/ 
but this might be a non-ixgbe issue, so forwarding details to netdev.

I had no problems with 3.5.7 which I used for many months. Then after 
upgrading to 3.14.28, 3.14.29 and 3.18.3 I have experienced several BUG_ON 
crashes. I put my config up at:

  https://www.caputo.com/foss/config_3.18.3_20150121.txt

This server is a router with a HotLava Systems Tambora 64G6 Part 
#6ST2830A2, PCI-e 2.0 (5GT/s), x8, 6-port, Intel 82599ES based NIC. 2x 
Intel Xeon E5420. SuperMicro X7DBE+ Rev 2.01. Intel 5000P (Blackford) 
Chipset. 32GB RAM.

Four of the 10G ports are bonded and trunked. There are packets being 
received and forwarded from one VLAN to another on the same bond1. Total 
utilization is under 5 Gbps. The traffic type is IP and generally TCP, 
with the vast majority of traffic in the 1,024 to 1,522 byte range. 
Example, I just cleared counters on the switch, and for one of the four 
10G's that make up the bundle, stats as follows after several minutes:

Input  
Port                64 Byte      65-127 Byte     128-255 Byte     256-511 
Byte  
------------------------------------------------------------------------------  
Et1                 1451474           278417            72206            59056  
  
Port          512-1023 Byte   1024-1522 Byte    1523-MAX Byte  
-------------------------------------------------------------  
Et1                   77757         55304548                0  

Crash dumps as follows:

With 3.18.3 I had this crash:

[49356.792102] ------------[ cut here ]------------
[49356.792185] kernel BUG at net/core/skbuff.c:2019!
[49356.792260] invalid opcode: 0000 [#1] SMP
[49356.792336] Modules linked in: w83627hf_wdt ip_vs_wlc ip_vs_wlib ip_vs libcrc32c nf_conntrack bonding e1000e e1000
[49356.793074]  [<ffffffff813c0cc8>] netif_receive_skb_internal+0x28/0x90
[49356.793074]  [<ffffffff813c0de4>] napi_gro_complete+0xa4/0xe0
[49356.793074]  [<ffffffff813c0e85>] napi_gro_flush+0x65/0x90
[49356.793074]  [<ffffffff8131bf94>] ixgbe_poll+0x474/0x7c0
[49356.793074]  [<ffffffff813c0fdb>] net_rx_action+0xfb/0x1a0
[49356.793074]  [<ffffffff8105461b>] __do_softirq+0xdb/0x1f0
[49356.793074]  [<ffffffff8105493d>] irq_exit+0x9d/0xb0
[49356.793074]  [<ffffffff810043a7>] do_IRQ+0x57/0xf0
[49356.793074]  [<ffffffff81526f6a>] common_interrupt+0x6a/0x6a
[49356.793074]  <EOI>
[49356.793074]  [<ffffffff8100b6b6>] ? default_idle+0x6/0x10
[49356.793074]  [<ffffffff8100bf1a>] arch_cpu_idle+0xa/0x10
[49356.793074]  [<ffffffff81081a12>] cpu_startup_entry+0x262/0x290
[49356.793074]  [<ffffffff810a01b3>] ? clockevents_register_device+0xe3/0x140
[49356.793074]  [<ffffffff8102ec0f>] start_secondary+0x13f/0x150
[49356.793074] Code: 44 8b 4d b0 48 8b 45 b8 e9 40 fe ff ff be d2 07 00 00 48 c7
               c7 2f 0d 74 81 44 89 5d b8 e8 bd 1b ca ff 44 8b 4d b8 e9 14 ff ff ff <0f> 0b 66
               90 55 48 89 e5 48 83 ec 10 4c 8d 45 f0 48 c7 45 f0 f0
[49356.793074] RIP  [<ffffffff813afa7c>] __skb_checksum+0x28c/0x290
[49356.793074]  RSP <ffff88082fcc37e8>
[49356.798627] ---[ end trace c0598b5bc30231bf ]---
[49356.798752] Kernel panic - not syncing: Fatal exception in interrupt
[49356.798892] Kernel Offset: 0x0 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffff9fffffff)
[49356.799092] Rebooting in 10 seconds..

__skb_checksum+0x28c/0x290 (skbuff.c line 2019):

        skb_walk_frags(skb, frag_iter) {
                int end;

                WARN_ON(start > offset + len);

                end = start + frag_iter->len;
                if ((copy = end - offset) > 0) {
                        __wsum csum2;
                        if (copy > len)
                                copy = len;
                        csum2 = __skb_checksum(frag_iter, offset - start,
                                               copy, 0, ops);
                        csum = ops->combine(csum, csum2, pos, copy);
                        if ((len -= copy) == 0)
                                return csum;
                        offset += copy;
                        pos    += copy;
                }                               
                start = end;
        }
        BUG_ON(len);

3.14.28 crash:

[375129.789047] BUG: unable to handle kernel NULL pointer dereference at 0000000
[375129.790004]  [<ffffffff813a16f5>] napi_gro_flush+0x65/0x80
[375129.790004]  [<ffffffff813a1729>] napi_complete+0x19/0x30
[375129.790004]  [<ffffffff812f9fbe>] ixgbe_poll+0x4ee/0x940
[375129.790004]  [<ffffffff813a183b>] net_rx_action+0xfb/0x1a0
[375129.790004]  [<ffffffff8104ec3c>] __do_softirq+0xdc/0x1f0
[375129.790004]  [<ffffffff8104ef5d>] irq_exit+0x9d/0xb0
[375129.790004]  [<ffffffff81003e33>] do_IRQ+0x53/0xf0
[375129.790004]  [<ffffffff814fddaa>] common_interrupt+0x6a/0x6a
[375129.790004]  <EOI>
[375129.790004]  [<ffffffff81074ac8>] ? sched_clock_cpu+0x88/0xb0
[375129.790004]  [<ffffffff8100a526>] ? default_idle+0x6/0x10
[375129.790004]  [<ffffffff8100ac96>] arch_cpu_idle+0x16/0x20
[375129.790004]  [<ffffffff810863c1>] cpu_startup_entry+0x91/0x180
[375129.790004]  [<ffffffff8102c13f>] start_secondary+0x19f/0x1f0
[375129.790004] Code: 4c 24 60 eb 21 0f 1f 80 00 00 00 00 41 83 c5 01 49 83 c4 10
                48 83 c1 10 41 39 c3 0f 86 7b 01 00 00 41 89 c7 89 c2 45 39 e9 7f 37 <41> 8b 46
                6c 41 39 46 68 0f 85 6d 03 00 00 45 8b a6 c4 00 00 00
[375129.790004] RIP  [<ffffffff8139567f>] skb_segment+0x5df/0x980
[375129.790004]  RSP <ffff88082fcc3828>
[375129.790004] CR2: 000000000000006c
[375129.790004] ---[ end trace ce413143217a96ad ]---
[375129.790004] Kernel panic - not syncing: Fatal exception in interrupt
[375129.790004] Kernel Offset: 0x0 from 0xffffffff81000000 (relocation range: 0x [ffffffff80000000-0xffffffff9fffffff)
[375129.790004] Rebooting in 10 seconds..

And then just after rebooting:

[   53.268587] BUG: unable to handle kernel NULL pointer dereference at 00000000
[   53.269532]  [<ffffffff813a1729>] napi_complete+0x19/0x30
[   53.269532]  [<ffffffff812f9fbe>] ixgbe_poll+0x4ee/0x940
[   53.269532]  [<ffffffff812032c4>] ? timerqueue_del+0x24/0x70
[   53.269532]  [<ffffffff81203230>] ? timerqueue_add+0x60/0xb0
[   53.269532]  [<ffffffff813a183b>] net_rx_action+0xfb/0x1a0
[   53.269532]  [<ffffffff8104ec3c>] __do_softirq+0xdc/0x1f0
[   53.269532]  [<ffffffff8104ef5d>] irq_exit+0x9d/0xb0
[   53.269532]  [<ffffffff81003e33>] do_IRQ+0x53/0xf0
[   53.269532]  [<ffffffff814fddaa>] common_interrupt+0x6a/0x6a
[   53.269532]  <EOI>
[   53.269532]  [<ffffffff8100a526>] ? default_idle+0x6/0x10
[   53.269532]  [<ffffffff8100ac96>] arch_cpu_idle+0x16/0x20
[   53.269532]  [<ffffffff810863c1>] cpu_startup_entry+0x91/0x180
[   53.269532]  [<ffffffff8102c13f>] start_secondary+0x19f/0x1f0
[   53.269532] Code: 4c 24 60 eb 21 0f 1f 80 00 00 00 00 41 83 c5 01 49 83 c4 10
[              48 83 c1 10 41 39 c3 0f 86 7b 01 00 00 41 89 c7 89 c2 45 39 e9 7f 37 <41> 8b 46
[              6c 41 39 46 68 0f 85 6d 03 00 00 45 8b a6 c4 00 00 00
[   53.269532] RIP  [<ffffffff8139567f>] skb_segment+0x5df/0x980
[   53.269532]  RSP <ffff88082fd43840>
[   53.269532] CR2: 000000000000006c
[   53.269532] ---[ end trace 1c1a68627fa9d6de ]---
[   53.269532] Kernel panic - not syncing: Fatal exception in interrupt
[   53.269532] Kernel Offset: 0x0 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffff9fffffff)
[   53.269532] Rebooting in 10 seconds..

The code which triggered the BUG is in skb_segment() in net/core/skbuff.c 
(line 3001 of kernel 3.14.28):

                while (pos < offset + len) {
                        if (i >= nfrags) {
                                BUG_ON(skb_headlen(list_skb));

                                i = 0;

Crash with 3.14.29:

[ 4010.835995] BUG: unable to handle kernel NULL pointer dereference at 000000000000006c 
[ 4010.836048] IP: [<ffffffff813955df> skb_segment+0x5df/0x980
[ 4010.836075] PGD 7f8296067 PUD 7f8298067 PMD 0
[ 4010.836130] Oops: 0000 [#1] SMP
[ 4010.836158] Modules linked in: w83627hf_wdt ip_vs_wlc ip_vs_wlib ip_vs libcrc32 nf_conntrack bonding e1000 e1000e 
[ 4010.836250] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.14.29 
[ 4010.836261] Hardware name: Supermicro X7DB8/X7DB8, BIOS 2.1 06/23/2008
[ 4010.836301] task: ffffffff81810460 ti: ffffffff81800000 task.ti: ffffffff81800000
[ 4010.836346] RIP: 0010:[<ffffffff813955df>]  [<ffffffff813955df>] skb_segment+0x5df/0x980 
[ 4010.836407] RSP: 0018:ffff88082fc03730  EFLAGS: 00010246                     
[ 4010.836503] RAX: 0000000000000a95 RBX: ffff88080b1ddb00 RCX: ffff8805e2edff10
[ 4010.836591] RDX: 0000000000000a95 RSI: 00000000000004d1 RDI: ffffea00032c6480
[ 4010.836680] RBP: ffff88082fc03800 R08: 0000000000010496 R09: 0000000000000002
[ 4010.836769] R10: ffff88080b1dcd00 R11: 0000000000010a12 R12: ffff8808073c9810
[ 4010.836842] R13: 0000000000000002 R14: 0000000000000000 R15: 0000000000000a95
[ 4010.836842] FS:  0000000000000000(0000) GS:ffff88082fc00000(0000) knlGS:0000000000000000 
[ 4010.836842] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[ 4010.836842] CR2: 000000000000006c CR3: 00000000c9fc8000 CR4: 0000000 0000007f0
[ 4010.836842] Stack:
[ 4010.836842]  ffffffff813a2f0b ffff88082fc03758 0000000000010496 fffffffffffefb6a
[ 4010.836842]  0000000000010a12 0000000000000066 ffff88080b1dcd00 0000000100001ee0 
[ 4010.836842]  ffffffffffffffda 00000000000104bc 000000260000057c ffff88080b1ddb00 
[ 4010.836842] Call Trace:
[ 4010.836842]  <IRQ>
[ 4010.836842]  [<ffffffff813a2f0b>] ? dev_queue_xmit+0xb/0x10
[ 4010.836842]  [<ffffffff8143c91d>] tcp_gso_segment+0x10d/0x3f0
[ 4010.836842]  [<ffffffff814ccf42>] ipv6_gso_segment+0x102/0x2c0
[ 4010.836842]  [<ffffffff813a22e3>] skb_mac_gso_segment+0x93/0x170
[ 4010.836842]  [<ffffffff8145adaf>] gre_gso_segment+0x12f/0x360
[ 4010.836842]  [<ffffffff8144c38d>] inet_gso_segment+0x12d/0x360
[ 4010.836842]  [<ffffffff813a22e3>] skb_mac_gso_segment+0x93/0x170
[ 4010.836842]  [<ffffffff813a241b>] __skb_gso_segment+0x5b/H0xc0
[ 4010.836842]  [<ffffffff813a273d>] dev_hard_start_xmit+0x17d/0x4d0
[ 4010.836842]  [<ffffffff813be290>] sch_direct_xmit+0xe0/0x1c0
[ 4010.836842]  [<ffffffff813be3f9>] __qdisc_run+0x89/0x150
[ 4010.836842]  [<ffffffff813a2d12>] __dev_queue_xmit+0x282/0x470
[ 4010.836842]  [<ffffffff813a2f0b>] dev_queue_xmit+0xb/0x10
[ 4010.836842]  [<ffffffff813aa832>] neigh_connected_output+0xb2/0xf0
[ 4010.836842]  [<ffffffff81419778>] ip_finish_output+0x1c8/0x400
[ 4010.836842]  [<ffffffff8141acd8>] ip_output+0x88/0x90
[ 4010.836842]  [<ffffffff81416cb6>] ip_forward_finish+0x86/0x1c0
[ 4010.836842]  [<ffffffff81417163>] ip_forward+0x373/0x440
[ 4010.836842]  [<ffffffff81414ea8>] ip_rcv_finish+0x78/0x340
[ 4010.836842]  [<ffffffff814157dc>] ip_rcv+0x2cc/0x3e0
[ 4010.836842]  [<ffffffff813a120e>] __netif_receive_skb_core+0x5be/0x7d0
[ 4010.836842]  [<ffffffff814cd162>] ? tcp6_gro_complete+0x62/0x70
[ 4010.836842]  [<ffffffff813a1438>] __netif_receive_skb+0x18/0x60
[ 4010.836842]  [<ffffffff813a14a8>] netif_receive_skb_internal+0x28/0x90
[ 4010.836842]  [<ffffffff813a15bc>] napi_gro_complete+0x9c/0xd0
[ 4010.836842]  [<ffffffff813a1ad6>] dev_gro_receive+0x296/0x440
[ 4010.836842]  [<ffffffff813a1d7d>] napi_gro_receive+0xd/0x80
[ 4010.836842]  [<ffffffff812f8c1c>] ixgbe_clean_rx_irq+0x62c/0x9e0
[ 4010.836842]  [<ffffffff812f9ec3>] ixgbe_poll+0x493/0x940
[ 4010.836842]  [<ffffffff8107fb8f>] ? __wake_up+0x3f/0x50
[ 4010.836842]  [<ffffffff813a179b>] net_rx_action+0xfb/0x1a0
[ 4010.836842]  [<ffffffff8104ec3c>] __do_softirq+0xdc/0x
[ 4010.836842]  [<ffffffff8104ef5d>] irq_exit+0x9d/0xb0
[ 4010.836842]  [<ffffffff81003e33>] do_IRQ+0x53/0xf0
[ 4010.836842]  [<ffffffff814fdd2a>] common_interrupt+0x6a/0x6a
[ 4010.836842]  <EOI>
[ 4010.836842]  [<ffffffff8100a526>] ? default_idle+0x6/0x10
[ 4010.836842]  [<ffffffff8100ac96>] arch_cpu_idle+0x16/0x20
[ 4010.836842]  [<ffffffff810863a1>] cpu_startup_entry+0x91/0x180
[ 4010.836842]  [<ffffffff814f1202>] rest_init+0x72/0x80
[ 4010.836842]  [<ffffffff81892da6>] start_kernel+0x340/0x34b
[ 4010.836842]  [<ffffffff8189286f>] ? repair_env_string+0x5c/0x5c
[ 4010.836842]  [<ffffffff818925ad>] x86_64_start_reservations+0x2a/0x2c
[ 4010.836842]  [<ffffffff81892676>] x86_64_start_kernel+0xc7/0xca
[ 4010.836842] Code: 4c 24 60 eb 21 0f 1f 80 00 00 00 00 41 83 c5 01 49 83 c4 10
[ 4010.836842] 48 83 c1 10 41 39 c3 0f 86 7b 01 00 00 41 89 c7 89 c2 45 39 e9 7f 37 <41> 8b 46
[ 4010.836842] 6c 41 39 46 68 0f 85 6d 03 00 00 45 8b a6 c4 00 00 00
[ 4010.836842] RIP  [<ffffffff813955df>] skb_segment+0x5df/0x980
[ 4010.836842]  RSP <ffff88082fc03730>
[ 4010.836842] CR2: 000000000000006c
[ 4010.836842] ---[ end trace ad63244a1b43b393 ]---
[ 4010.836842] Kernel panic - not syncing: Fatal exception in interrupt
[ 4010.836842] Kernel Offset: 0x0 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffff9fffffff)
[ 4010.836842] Rebooting in 10 seconds..

Thanks,
Chris

^ permalink raw reply

* Re: [PATCH v5 2/5] can: kvaser_usb: Consolidate and unify state change handling
From: Marc Kleine-Budde @ 2015-01-21 22:59 UTC (permalink / raw)
  To: Andri Yngvason, Ahmed S. Darwish, Olivier Sobrie, Oliver Hartkopp,
	Wolfgang Grandegger
  Cc: Linux-CAN, netdev, LKML
In-Reply-To: <20150121162025.2933.13836@shannon>

[-- Attachment #1: Type: text/plain, Size: 613 bytes --]

On 01/21/2015 05:20 PM, Andri Yngvason wrote:
> Marc, could you merge the "move bus_off++" patch before you merge this so that I
> won't have to incorporate this patch-set into it?

...included in the lastest pull-request to David. Use
tags/linux-can-next-for-3.20-20150121 of the can-next repo as you new base.

regards,
Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply

* pull-request: can-next 2015-21-01
From: Marc Kleine-Budde @ 2015-01-21 22:58 UTC (permalink / raw)
  To: netdev; +Cc: David Miller, linux-can@vger.kernel.org, kernel@pengutronix.de

[-- Attachment #1: Type: text/plain, Size: 2699 bytes --]

Hello David,

this is a pull request of 4 patches for net-next/master.

Andri Yngvason contributes one patch to further consolidate the CAN
state change handling. The next patch is by kbuild test robot/Fengguang
Wu which fixes a coccinelle warning in the CAN infrastructure. The two
last patches are by me, they remove a unused variable from the flexcan
and at91_can driver.

Marc

---

The following changes since commit 0c49087462e8587c12ecfeaf1dd46fdc0ddc4532:

  Merge tag 'mac80211-next-for-davem-2015-01-19' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next (2015-01-19 16:22:19 -0500)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git tags/linux-can-next-for-3.20-20150121

for you to fetch changes up to ef186f25fdf00d92c6bd5d3301fc0ee44f17ca33:

  can: at91_can: remove unused variable (2015-01-21 22:47:44 +0100)

----------------------------------------------------------------
linux-can-next-for-3.20-20150121

----------------------------------------------------------------
Andri Yngvason (1):
      can: move can_stats.bus_off++ from can_bus_off into can_change_state

Marc Kleine-Budde (2):
      can: flexcan: remove unused variable
      can: at91_can: remove unused variable

kbuild test robot (1):
      can: dev: fix semicolon.cocci warnings

 drivers/net/can/at91_can.c                  | 2 --
 drivers/net/can/bfin_can.c                  | 1 +
 drivers/net/can/c_can/c_can.c               | 2 +-
 drivers/net/can/cc770/cc770.c               | 1 +
 drivers/net/can/dev.c                       | 5 +++--
 drivers/net/can/flexcan.c                   | 2 --
 drivers/net/can/janz-ican3.c                | 1 +
 drivers/net/can/m_can/m_can.c               | 1 +
 drivers/net/can/pch_can.c                   | 1 +
 drivers/net/can/rcar_can.c                  | 1 +
 drivers/net/can/softing/softing_main.c      | 1 +
 drivers/net/can/spi/mcp251x.c               | 1 +
 drivers/net/can/ti_hecc.c                   | 1 +
 drivers/net/can/usb/ems_usb.c               | 1 +
 drivers/net/can/usb/esd_usb2.c              | 1 +
 drivers/net/can/usb/peak_usb/pcan_usb.c     | 1 +
 drivers/net/can/usb/peak_usb/pcan_usb_pro.c | 1 +
 drivers/net/can/usb/usb_8dev.c              | 1 +
 18 files changed, 18 insertions(+), 7 deletions(-)

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |






















[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply

* Re: [PATCH net-next 1/2] udp: Do not require sock in udp_tunnel_xmit_skb
From: Tom Herbert @ 2015-01-21 22:55 UTC (permalink / raw)
  To: Or Gerlitz; +Cc: David Miller, Thomas Graf, Linux Netdev List
In-Reply-To: <CAJ3xEMhMgXLAv5qPXgUTdiTBuBuYO-DPczSvXOjKHfD0WsAn-g@mail.gmail.com>

On Wed, Jan 21, 2015 at 2:37 PM, Or Gerlitz <gerlitz.or@gmail.com> wrote:
> On Thu, Jan 22, 2015 at 12:24 AM, Tom Herbert <therbert@google.com> wrote:
>> On Wed, Jan 21, 2015 at 12:57 PM, Or Gerlitz <gerlitz.or@gmail.com> wrote:
>>> On Wed, Jan 21, 2015 at 12:24 AM, Tom Herbert <therbert@google.com> wrote:
>>>> vxlan_xmit_one calls udp_flow_src_port to get a source port value
>>>> based on the encapsulated flow.
>>>
>>> OK, got it -- does a by-product of invoking udp_flow_src_port on the
>>> skb is having an hash mark on the skb which is based on the inner
>>> packet and can (is?) used for TX queue selection over MQ devices?
>>
>> Yes, that should be fine. The hash can be used all the way up to TCP
>> and save in the TCP socket as the rxhash for a connection. This was
>> RFS, XPS, etc. will work for TCP over an encapsulation.
>
>
> so.. can be used or is already used today? Also we're talking on the
> TX path, so I wasn't sure to follow on your mentioning of RFS...

Yes. AFAIK all encapsulation implementations are single queue and
otherwise transparent to the application layer for the hash and queue
selection.

Is there a particular feature you're having trouble with?

Thanks,
Tom

^ permalink raw reply

* Re: [PATCH net-next 1/2] udp: Do not require sock in udp_tunnel_xmit_skb
From: Or Gerlitz @ 2015-01-21 22:37 UTC (permalink / raw)
  To: Tom Herbert; +Cc: David Miller, Thomas Graf, Linux Netdev List
In-Reply-To: <CA+mtBx_uwwDV+ogt6BeYH36qLztJLt75H9Z_r0BY5FjkixdBCw@mail.gmail.com>

On Thu, Jan 22, 2015 at 12:24 AM, Tom Herbert <therbert@google.com> wrote:
> On Wed, Jan 21, 2015 at 12:57 PM, Or Gerlitz <gerlitz.or@gmail.com> wrote:
>> On Wed, Jan 21, 2015 at 12:24 AM, Tom Herbert <therbert@google.com> wrote:
>>> vxlan_xmit_one calls udp_flow_src_port to get a source port value
>>> based on the encapsulated flow.
>>
>> OK, got it -- does a by-product of invoking udp_flow_src_port on the
>> skb is having an hash mark on the skb which is based on the inner
>> packet and can (is?) used for TX queue selection over MQ devices?
>
> Yes, that should be fine. The hash can be used all the way up to TCP
> and save in the TCP socket as the rxhash for a connection. This was
> RFS, XPS, etc. will work for TCP over an encapsulation.


so.. can be used or is already used today? Also we're talking on the
TX path, so I wasn't sure to follow on your mentioning of RFS...

^ permalink raw reply

* subtle change in behavior with tun driver
From: Ani Sinha @ 2015-01-21 22:36 UTC (permalink / raw)
  To: mst, netdev@vger.kernel.org

Hi guys :

Commit 5d097109257c03 ("tun: only queue packets on device") seems to
have introduced a subtle change in behavior in the tun driver in the
default (non IFF_ONE_QUEUE) case. Previously when the queues got full
and eventually sk_wmem_alloc of the socket exceeded sk_sndbuf value,
the user would be given a feedback by returning EAGAIN from sendto()
etc. That way, the user could retry sending the packet again.
Unfortunately, with this new  default single queue mode, the driver
silently drops the packet when the device queue is full without giving
userland any feedback. This makes it appear to userland as though the
packet was transmitted successfully. It seems there is a semantic
change in the driver with this commit.

If the receiving process gets stuck for a short interval and is unable
to drain packets and then restarts again, one might see strange packet
drops in the kernel without getting any error back on the sender's
side. It kind of feels wrong.

Any thoughts?

Ani

^ permalink raw reply

* Re: [PATCH net-next 1/2] udp: Do not require sock in udp_tunnel_xmit_skb
From: Tom Herbert @ 2015-01-21 22:24 UTC (permalink / raw)
  To: Or Gerlitz; +Cc: David Miller, Thomas Graf, Linux Netdev List
In-Reply-To: <CAJ3xEMhX+2J5hRuxF9_694sqxsMwi0LuwiEAK1G2-ZBaOPgBTQ@mail.gmail.com>

On Wed, Jan 21, 2015 at 12:57 PM, Or Gerlitz <gerlitz.or@gmail.com> wrote:
> On Wed, Jan 21, 2015 at 12:24 AM, Tom Herbert <therbert@google.com> wrote:
>> vxlan_xmit_one calls udp_flow_src_port to get a source port value
>> based on the encapsulated flow.
>
> OK, got it -- does a by-product of invoking udp_flow_src_port on the
> skb is having an hash mark on the skb which is based on the inner
> packet and can (is?) used for TX queue selection over MQ devices?

Yes, that should be fine. The hash can be used all the way up to TCP
and save in the TCP socket as the rxhash for a connection. This was
RFS, XPS, etc. will work for TCP over an encapsulation.

^ permalink raw reply

* [PATCH 2/2] sh_eth: add more PM methods
From: Sergei Shtylyov @ 2015-01-21 22:19 UTC (permalink / raw)
  To: netdev; +Cc: linux-sh, mikhail.ulyanov
In-Reply-To: <5608250.lrFkJTjgSf@wasted.cogentembedded.com>

From: Mikhail Ulyanov <mikhail.ulyanov@cogentembedded.com>

Add sh_eth_{suspend|resume}() implementing {suspend|resume|freeze|thaw|poweroff|
restore}() PM methods to make it possible to restore from hibernation not only
in Linux  but also in e.g. U-Boot and  to have more determined state on resume/
restore.

Signed-off-by: Mikhail Ulyanov <mikhail.ulyanov@cogentembedded.com>
[Sergei: moved sh_eth_{suspend|resume}() before sh_eth_runtime_nop(), enclosed
them with #ifdef CONFIG_PM_SLEEP, reordered the local variables, got rid of
*goto* and label, reordered macro invocations, renamed, modified the changelog.]
Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

---
 drivers/net/ethernet/renesas/sh_eth.c |   31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

Index: net-next/drivers/net/ethernet/renesas/sh_eth.c
===================================================================
--- net-next.orig/drivers/net/ethernet/renesas/sh_eth.c
+++ net-next/drivers/net/ethernet/renesas/sh_eth.c
@@ -2947,6 +2947,36 @@ static int sh_eth_drv_remove(struct plat
 }
 
 #ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
+static int sh_eth_suspend(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (netif_running(ndev)) {
+		netif_device_detach(ndev);
+		ret = sh_eth_close(ndev);
+	}
+
+	return ret;
+}
+
+static int sh_eth_resume(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (netif_running(ndev)) {
+		ret = sh_eth_open(ndev);
+		if (ret < 0)
+			return ret;
+		netif_device_attach(ndev);
+	}
+
+	return ret;
+}
+#endif
+
 static int sh_eth_runtime_nop(struct device *dev)
 {
 	/* Runtime PM callback shared between ->runtime_suspend()
@@ -2960,6 +2990,7 @@ static int sh_eth_runtime_nop(struct dev
 }
 
 static const struct dev_pm_ops sh_eth_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(sh_eth_suspend, sh_eth_resume)
 	SET_RUNTIME_PM_OPS(sh_eth_runtime_nop, sh_eth_runtime_nop, NULL)
 };
 #define SH_ETH_PM_OPS (&sh_eth_dev_pm_ops)


^ permalink raw reply

* [PATCH 1/2] sh_eth: use SET_RUNTIME_PM_OPS()
From: Sergei Shtylyov @ 2015-01-21 22:18 UTC (permalink / raw)
  To: netdev; +Cc: linux-sh, mikhail.ulyanov
In-Reply-To: <5608250.lrFkJTjgSf@wasted.cogentembedded.com>

From: Mikhail Ulyanov <mikhail.ulyanov@cogentembedded.com>

Use SET_RUNTIME_PM_OPS() macro to initialize the runtime PM method pointers in
the 'struct dev_pm_ops'.

Signed-off-by: Mikhail Ulyanov <mikhail.ulyanov@cogentembedded.com>
[Sergei: renamed, added the changelog.]
Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

---
 drivers/net/ethernet/renesas/sh_eth.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

Index: net-next/drivers/net/ethernet/renesas/sh_eth.c
===================================================================
--- net-next.orig/drivers/net/ethernet/renesas/sh_eth.c
+++ net-next/drivers/net/ethernet/renesas/sh_eth.c
@@ -2960,8 +2960,7 @@ static int sh_eth_runtime_nop(struct dev
 }
 
 static const struct dev_pm_ops sh_eth_dev_pm_ops = {
-	.runtime_suspend = sh_eth_runtime_nop,
-	.runtime_resume = sh_eth_runtime_nop,
+	SET_RUNTIME_PM_OPS(sh_eth_runtime_nop, sh_eth_runtime_nop, NULL)
 };
 #define SH_ETH_PM_OPS (&sh_eth_dev_pm_ops)
 #else


^ permalink raw reply

* [PATCH 0/2] sh_eth: massage PM code
From: Sergei Shtylyov @ 2015-01-21 22:16 UTC (permalink / raw)
  To: netdev; +Cc: linux-sh

Hello.

   Here's a set of 2 patches against DaveM's 'net-next.git' repo. We're adding
the support for suspend/hibernation as well as somewhat changing the existing
code. There are still MDIO-related issue with suspend (kernel exception), we've
been working on it and shall address it with a separate patch...

[1/2] sh_eth: use SET_RUNTIME_PM_OPS()
[2/2] sh_eth: add more PM methods

WBR, Sergei


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox