aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/intel/fm10k/fm10k.h
diff options
context:
space:
mode:
authorJacob Keller <jacob.e.keller@intel.com>2017-01-12 15:59:38 -0800
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>2017-04-05 22:47:30 -0700
commit3ee7b3a3b9deb26278af308743ec927847c70366 (patch)
tree5de1bf57484fa7e773bbfeaedb71da36c36f9def /drivers/net/ethernet/intel/fm10k/fm10k.h
parentfm10k: correctly check if interface is removed (diff)
downloadlinux-dev-3ee7b3a3b9deb26278af308743ec927847c70366.tar.xz
linux-dev-3ee7b3a3b9deb26278af308743ec927847c70366.zip
fm10k: use a BITMAP for flags to avoid race conditions
Replace bitwise operators and #defines with a BITMAP and enumeration values. This is similar to how we handle the "state" values as well. This has two distinct advantages over the old method. First, we ensure correctness of operations which are currently problematic due to race conditions. Suppose that two kernel threads are running, such as the watchdog and an ethtool ioctl, and both modify flags. We'll say that the watchdog is CPU A, and the ethtool ioctl is CPU B. CPU A sets FLAG_1, which can be seen as CPU A read FLAGS CPU A write FLAGS | FLAG_1 CPU B sets FLAG_2, which can be seen as CPU B read FLAGS CPU A write FLAGS | FLAG_2 However, "|=" and "&=" operators are not actually atomic. So this could be ordered like the following: CPU A read FLAGS -> variable CPU B read FLAGS -> variable CPU A write FLAGS (variable | FLAG_1) CPU B write FLAGS (variable | FLAG_2) Notice how the 2nd write from CPU B could actually undo the write from CPU A because it isn't guaranteed that the |= operation is atomic. In practice the race windows for most flag writes is incredibly narrow so it is not easy to isolate issues. However, the more flags we have, the more likely they will cause problems. Additionally, if such a problem were to arise, it would be incredibly difficult to track down. Second, there is an additional advantage beyond code correctness. We can now automatically size the BITMAP if more flags were added, so that we do not need to remember that flags is u32 and thus if we added too many flags we would over-run the variable. This is not a likely occurrence for fm10k driver, but this patch can serve as an example for other drivers which have many more flags. This particular change does have a bit of trouble converting some of the idioms previously used with the #defines for flags. Specifically, when converting FM10K_FLAG_RSS_FIELD_IPV[46]_UDP flags. This whole operation was actually quite problematic, because we actually stored flags separately. This could more easily show the problem of the above re-ordering issue. This is really difficult to test whether atomics make a difference in practical scenarios, but you can ensure that basic functionality remains the same. This patch has a lot of code coverage, but most of it is relatively simple. While we are modifying these files, update their copyright year. Signed-off-by: Jacob Keller <jacob.e.keller@intel.com> Tested-by: Krishneil Singh <krishneil.k.singh@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/ethernet/intel/fm10k/fm10k.h')
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k.h27
1 files changed, 21 insertions, 6 deletions
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h
index 52b979443cde..d6db1c48d4dc 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k.h
@@ -1,5 +1,5 @@
/* Intel(R) Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2016 Intel Corporation.
+ * Copyright(c) 2013 - 2017 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -249,6 +249,23 @@ struct fm10k_udp_port {
/* one work queue for entire driver */
extern struct workqueue_struct *fm10k_workqueue;
+/* The following enumeration contains flags which indicate or enable modified
+ * driver behaviors. To avoid race conditions, the flags are stored in
+ * a BITMAP in the fm10k_intfc structure. The BITMAP should be accessed using
+ * atomic *_bit() operations.
+ */
+enum fm10k_flags_t {
+ FM10K_FLAG_RESET_REQUESTED,
+ FM10K_FLAG_RSS_FIELD_IPV4_UDP,
+ FM10K_FLAG_RSS_FIELD_IPV6_UDP,
+ FM10K_FLAG_SWPRI_CONFIG,
+ /* __FM10K_FLAGS_SIZE__ is used to calculate the size of
+ * interface->flags and must be the last value in this
+ * enumeration.
+ */
+ __FM10K_FLAGS_SIZE__
+};
+
struct fm10k_intfc {
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
struct net_device *netdev;
@@ -256,11 +273,9 @@ struct fm10k_intfc {
struct pci_dev *pdev;
unsigned long state;
- u32 flags;
-#define FM10K_FLAG_RESET_REQUESTED (u32)(BIT(0))
-#define FM10K_FLAG_RSS_FIELD_IPV4_UDP (u32)(BIT(1))
-#define FM10K_FLAG_RSS_FIELD_IPV6_UDP (u32)(BIT(2))
-#define FM10K_FLAG_SWPRI_CONFIG (u32)(BIT(3))
+ /* Access flag values using atomic *_bit() operations */
+ DECLARE_BITMAP(flags, __FM10K_FLAGS_SIZE__);
+
int xcast_mode;
/* Tx fast path data */