]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
authorDavid S. Miller <davem@davemloft.net>
Sat, 27 May 2017 00:46:35 +0000 (20:46 -0400)
committerDavid S. Miller <davem@davemloft.net>
Sat, 27 May 2017 00:46:35 +0000 (20:46 -0400)
Overlapping changes in drivers/net/phy/marvell.c, bug fix in 'net'
restricting a HW workaround alongside cleanups in 'net-next'.

Signed-off-by: David S. Miller <davem@davemloft.net>
15 files changed:
1  2 
drivers/net/ethernet/8390/ax88796.c
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
drivers/net/ethernet/mellanox/mlx5/core/eq.c
drivers/net/ethernet/mellanox/mlx5/core/health.c
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/phy/marvell.c
include/linux/mlx5/device.h
include/linux/mlx5/driver.h
include/net/dst.h
include/net/ip_fib.h
net/core/rtnetlink.c
net/ipv4/fib_semantics.c
net/ipv4/route.c
net/ipv4/tcp.c

index 2f0ee3d7ceb1b0c967ec35e1c95a5cfe423e1dda,db02bc2fb4b2d634ed454cedfcf57692e9b941c0..05d9d3e2e92e51816b4bfc3933ac42bafced23a3
@@@ -723,12 -723,6 +723,12 @@@ static int ax_init_dev(struct net_devic
            ax->plat->mac_addr)
                memcpy(dev->dev_addr, ax->plat->mac_addr, ETH_ALEN);
  
 +      if (!is_valid_ether_addr(dev->dev_addr)) {
 +              eth_hw_addr_random(dev);
 +              dev_info(&dev->dev, "Using random MAC address: %pM\n",
 +                       dev->dev_addr);
 +      }
 +
        ax_reset_8390(dev);
  
        ei_local->name = "AX88796";
  
        ret = ax_mii_init(dev);
        if (ret)
-               goto out_irq;
+               goto err_out;
  
        ax_NS8390_init(dev, 0);
  
        ret = register_netdev(dev);
        if (ret)
-               goto out_irq;
+               goto err_out;
  
        netdev_info(dev, "%dbit, irq %d, %lx, MAC: %pM\n",
                    ei_local->word16 ? 16 : 8, dev->irq, dev->base_addr,
  
        return 0;
  
-  out_irq:
-       /* cleanup irq */
-       free_irq(dev->irq, dev);
   err_out:
        return ret;
  }
index 8000551135837ffe04b0f29bc1caab988bc5be56,4eee18ce9be46b5e1dc35167b51af634a2070e60..319eee36649bedb17717c8b21d908903555708e3
@@@ -3241,9 -3241,8 +3241,9 @@@ void be_detect_error(struct be_adapter 
  {
        u32 ue_lo = 0, ue_hi = 0, ue_lo_mask = 0, ue_hi_mask = 0;
        u32 sliport_status = 0, sliport_err1 = 0, sliport_err2 = 0;
 -      u32 i;
        struct device *dev = &adapter->pdev->dev;
 +      u16 val;
 +      u32 i;
  
        if (be_check_error(adapter, BE_ERROR_HW))
                return;
                ue_lo = (ue_lo & ~ue_lo_mask);
                ue_hi = (ue_hi & ~ue_hi_mask);
  
 -              /* On certain platforms BE hardware can indicate spurious UEs.
 -               * Allow HW to stop working completely in case of a real UE.
 -               * Hence not setting the hw_error for UE detection.
 -               */
 -
                if (ue_lo || ue_hi) {
 +                      /* On certain platforms BE3 hardware can indicate
 +                       * spurious UEs. In case of a UE in the chip,
 +                       * the POST register correctly reports either a
 +                       * FAT_LOG_START state (FW is currently dumping
 +                       * FAT log data) or a ARMFW_UE state. Check for the
 +                       * above states to ascertain if the UE is valid or not.
 +                       */
 +                      if (BE3_chip(adapter)) {
 +                              val = be_POST_stage_get(adapter);
 +                              if ((val & POST_STAGE_FAT_LOG_START)
 +                                   != POST_STAGE_FAT_LOG_START &&
 +                                  (val & POST_STAGE_ARMFW_UE)
 +                                   != POST_STAGE_ARMFW_UE)
 +                                      return;
 +                      }
 +
                        dev_err(dev, "Error detected in the adapter");
 -                      if (skyhawk_chip(adapter))
 -                              be_set_error(adapter, BE_ERROR_UE);
 +                      be_set_error(adapter, BE_ERROR_UE);
  
                        for (i = 0; ue_lo; ue_lo >>= 1, i++) {
                                if (ue_lo & 1)
@@@ -5089,9 -5078,11 +5089,11 @@@ static netdev_features_t be_features_ch
        struct be_adapter *adapter = netdev_priv(dev);
        u8 l4_hdr = 0;
  
-       /* The code below restricts offload features for some tunneled packets.
+       /* The code below restricts offload features for some tunneled and
+        * Q-in-Q packets.
         * Offload features for normal (non tunnel) packets are unchanged.
         */
+       features = vlan_features_check(skb, features);
        if (!skb->encapsulation ||
            !(adapter->flags & BE_FLAGS_VXLAN_OFFLOADS))
                return features;
index b46c2f22b1b31247fd5210feb08b808fc3338555,ec63158ab64330c939ffa4ca8c001f8134f8e4e2..d2f90ba2dbc404fc2506d0400f7131420e3dd403
@@@ -43,6 -43,7 +43,7 @@@
  #include <net/tc_act/tc_vlan.h>
  #include <net/tc_act/tc_tunnel_key.h>
  #include <net/tc_act/tc_pedit.h>
+ #include <net/tc_act/tc_csum.h>
  #include <net/vxlan.h>
  #include <net/arp.h>
  #include "en.h"
@@@ -384,7 -385,7 +385,7 @@@ static void mlx5e_detach_encap(struct m
                if (e->flags & MLX5_ENCAP_ENTRY_VALID)
                        mlx5_encap_dealloc(priv->mdev, e->encap_id);
  
-               hlist_del_rcu(&e->encap_hlist);
+               hash_del_rcu(&e->encap_hlist);
                kfree(e->encap_header);
                kfree(e);
        }
@@@ -925,11 -926,11 +926,11 @@@ static int offload_pedit_fields(struct 
                                struct mlx5e_tc_flow_parse_attr *parse_attr)
  {
        struct pedit_headers *set_masks, *add_masks, *set_vals, *add_vals;
-       int i, action_size, nactions, max_actions, first, last;
+       int i, action_size, nactions, max_actions, first, last, first_z;
        void *s_masks_p, *a_masks_p, *vals_p;
-       u32 s_mask, a_mask, val;
        struct mlx5_fields *f;
        u8 cmd, field_bsize;
+       u32 s_mask, a_mask;
        unsigned long mask;
        void *action;
  
        for (i = 0; i < ARRAY_SIZE(fields); i++) {
                f = &fields[i];
                /* avoid seeing bits set from previous iterations */
-               s_mask = a_mask = mask = val = 0;
+               s_mask = 0;
+               a_mask = 0;
  
                s_masks_p = (void *)set_masks + f->offset;
                a_masks_p = (void *)add_masks + f->offset;
                        memset(a_masks_p, 0, f->size);
                }
  
-               memcpy(&val, vals_p, f->size);
                field_bsize = f->size * BITS_PER_BYTE;
+               first_z = find_first_zero_bit(&mask, field_bsize);
                first = find_first_bit(&mask, field_bsize);
                last  = find_last_bit(&mask, field_bsize);
-               if (first > 0 || last != (field_bsize - 1)) {
+               if (first > 0 || last != (field_bsize - 1) || first_z < last) {
                        printk(KERN_WARNING "mlx5: partial rewrite (mask %lx) is currently not offloaded\n",
                               mask);
                        return -EOPNOTSUPP;
                }
  
                if (field_bsize == 32)
-                       MLX5_SET(set_action_in, action, data, ntohl(val));
+                       MLX5_SET(set_action_in, action, data, ntohl(*(__be32 *)vals_p));
                else if (field_bsize == 16)
-                       MLX5_SET(set_action_in, action, data, ntohs(val));
+                       MLX5_SET(set_action_in, action, data, ntohs(*(__be16 *)vals_p));
                else if (field_bsize == 8)
-                       MLX5_SET(set_action_in, action, data, val);
+                       MLX5_SET(set_action_in, action, data, *(u8 *)vals_p);
  
                action += action_size;
                nactions++;
@@@ -1109,6 -1111,28 +1111,28 @@@ out_err
        return err;
  }
  
+ static bool csum_offload_supported(struct mlx5e_priv *priv, u32 action, u32 update_flags)
+ {
+       u32 prot_flags = TCA_CSUM_UPDATE_FLAG_IPV4HDR | TCA_CSUM_UPDATE_FLAG_TCP |
+                        TCA_CSUM_UPDATE_FLAG_UDP;
+       /*  The HW recalcs checksums only if re-writing headers */
+       if (!(action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)) {
+               netdev_warn(priv->netdev,
+                           "TC csum action is only offloaded with pedit\n");
+               return false;
+       }
+       if (update_flags & ~prot_flags) {
+               netdev_warn(priv->netdev,
+                           "can't offload TC csum action for some header/s - flags %#x\n",
+                           update_flags);
+               return false;
+       }
+       return true;
+ }
  static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                                struct mlx5e_tc_flow_parse_attr *parse_attr,
                                struct mlx5e_tc_flow *flow)
                        continue;
                }
  
+               if (is_tcf_csum(a)) {
+                       if (csum_offload_supported(priv, attr->action,
+                                                  tcf_csum_update_flags(a)))
+                               continue;
+                       return -EOPNOTSUPP;
+               }
                if (is_tcf_skbedit_mark(a)) {
                        u32 mark = tcf_skbedit_mark(a);
  
@@@ -1404,8 -1436,8 +1436,8 @@@ static int mlx5e_create_encap_header_ip
  
        if (!(nud_state & NUD_VALID)) {
                neigh_event_send(n, NULL);
 -              neigh_release(n);
 -              return -EAGAIN;
 +              err = -EAGAIN;
 +              goto out;
        }
  
        err = mlx5_encap_alloc(priv->mdev, e->tunnel_type,
@@@ -1510,8 -1542,8 +1542,8 @@@ static int mlx5e_create_encap_header_ip
  
        if (!(nud_state & NUD_VALID)) {
                neigh_event_send(n, NULL);
 -              neigh_release(n);
 -              return -EAGAIN;
 +              err = -EAGAIN;
 +              goto out;
        }
  
        err = mlx5_encap_alloc(priv->mdev, e->tunnel_type,
@@@ -1651,6 -1683,14 +1683,14 @@@ static int parse_tc_fdb_actions(struct 
                        continue;
                }
  
+               if (is_tcf_csum(a)) {
+                       if (csum_offload_supported(priv, attr->action,
+                                                  tcf_csum_update_flags(a)))
+                               continue;
+                       return -EOPNOTSUPP;
+               }
                if (is_tcf_mirred_egress_redirect(a)) {
                        int ifindex = tcf_mirred_ifindex(a);
                        struct net_device *out_dev, *encap_dev = NULL;
@@@ -1738,7 -1778,7 +1778,7 @@@ int mlx5e_configure_flower(struct mlx5e
        }
  
        flow = kzalloc(sizeof(*flow) + attr_size, GFP_KERNEL);
 -      parse_attr = mlx5_vzalloc(sizeof(*parse_attr));
 +      parse_attr = kvzalloc(sizeof(*parse_attr), GFP_KERNEL);
        if (!parse_attr || !flow) {
                err = -ENOMEM;
                goto err_free;
index 01d2cd7e474695b81f3f67f85f424ecca72f55ca,33eae5ad2fb09efe302e2b892c3c9e30d4b117f5..0ed8e90ba54f224048d1cd98d2d9c2ff41897d06
@@@ -35,7 -35,6 +35,7 @@@
  #include <linux/mlx5/driver.h>
  #include <linux/mlx5/cmd.h>
  #include "mlx5_core.h"
 +#include "fpga/core.h"
  #ifdef CONFIG_MLX5_CORE_EN
  #include "eswitch.h"
  #endif
@@@ -157,8 -156,6 +157,8 @@@ static const char *eqe_type_str(u8 type
                return "MLX5_EVENT_TYPE_PAGE_FAULT";
        case MLX5_EVENT_TYPE_PPS_EVENT:
                return "MLX5_EVENT_TYPE_PPS_EVENT";
 +      case MLX5_EVENT_TYPE_FPGA_ERROR:
 +              return "MLX5_EVENT_TYPE_FPGA_ERROR";
        default:
                return "Unrecognized event";
        }
@@@ -425,7 -422,7 +425,7 @@@ static irqreturn_t mlx5_eq_int(int irq
                        break;
  
                case MLX5_EVENT_TYPE_CMD:
-                       mlx5_cmd_comp_handler(dev, be32_to_cpu(eqe->data.cmd.vector));
+                       mlx5_cmd_comp_handler(dev, be32_to_cpu(eqe->data.cmd.vector), false);
                        break;
  
                case MLX5_EVENT_TYPE_PORT_CHANGE:
                        if (dev->event)
                                dev->event(dev, MLX5_DEV_EVENT_PPS, (unsigned long)eqe);
                        break;
 +
 +              case MLX5_EVENT_TYPE_FPGA_ERROR:
 +                      mlx5_fpga_event(dev, eqe->type, &eqe->data.raw);
 +                      break;
 +
                default:
                        mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n",
                                       eqe->type, eq->eqn);
@@@ -556,7 -548,7 +556,7 @@@ int mlx5_create_map_eq(struct mlx5_core
        inlen = MLX5_ST_SZ_BYTES(create_eq_in) +
                MLX5_FLD_SZ_BYTES(create_eq_in, pas[0]) * eq->buf.npages;
  
 -      in = mlx5_vzalloc(inlen);
 +      in = kvzalloc(inlen, GFP_KERNEL);
        if (!in) {
                err = -ENOMEM;
                goto err_buf;
@@@ -701,9 -693,6 +701,9 @@@ int mlx5_start_eqs(struct mlx5_core_de
        if (MLX5_CAP_GEN(dev, pps))
                async_event_mask |= (1ull << MLX5_EVENT_TYPE_PPS_EVENT);
  
 +      if (MLX5_CAP_GEN(dev, fpga))
 +              async_event_mask |= (1ull << MLX5_EVENT_TYPE_FPGA_ERROR);
 +
        err = mlx5_create_map_eq(dev, &table->cmd_eq, MLX5_EQ_VEC_CMD,
                                 MLX5_NUM_CMD_EQE, 1ull << MLX5_EVENT_TYPE_CMD,
                                 "mlx5_cmd_eq", MLX5_EQ_TYPE_ASYNC);
index c3cedb6cec3f1de1afe3575b9376d7b66225cafa,44f59b1d6f0f27f7bb4f818b11f341af28ba09dc..80b23333de7ac581b808266a3770baaa677eee19
@@@ -90,7 -90,7 +90,7 @@@ static void trigger_cmd_completions(str
        spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags);
  
        mlx5_core_dbg(dev, "vector 0x%llx\n", vector);
-       mlx5_cmd_comp_handler(dev, vector);
+       mlx5_cmd_comp_handler(dev, vector, true);
        return;
  
  no_trig:
@@@ -185,7 -185,6 +185,7 @@@ static void health_care(struct work_str
        struct mlx5_core_health *health;
        struct mlx5_core_dev *dev;
        struct mlx5_priv *priv;
 +      unsigned long flags;
  
        health = container_of(work, struct mlx5_core_health, work);
        priv = container_of(health, struct mlx5_priv, health);
        mlx5_core_warn(dev, "handling bad device here\n");
        mlx5_handle_bad_state(dev);
  
 -      spin_lock(&health->wq_lock);
 +      spin_lock_irqsave(&health->wq_lock, flags);
        if (!test_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags))
                schedule_delayed_work(&health->recover_work, recover_delay);
        else
                dev_err(&dev->pdev->dev,
                        "new health works are not permitted at this stage\n");
 -      spin_unlock(&health->wq_lock);
 +      spin_unlock_irqrestore(&health->wq_lock, flags);
  }
  
  static const char *hsynd_str(u8 synd)
@@@ -270,20 -269,6 +270,20 @@@ static unsigned long get_next_poll_jiff
        return next;
  }
  
 +void mlx5_trigger_health_work(struct mlx5_core_dev *dev)
 +{
 +      struct mlx5_core_health *health = &dev->priv.health;
 +      unsigned long flags;
 +
 +      spin_lock_irqsave(&health->wq_lock, flags);
 +      if (!test_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags))
 +              queue_work(health->wq, &health->work);
 +      else
 +              dev_err(&dev->pdev->dev,
 +                      "new health works are not permitted at this stage\n");
 +      spin_unlock_irqrestore(&health->wq_lock, flags);
 +}
 +
  static void poll_health(unsigned long data)
  {
        struct mlx5_core_dev *dev = (struct mlx5_core_dev *)data;
        if (in_fatal(dev) && !health->sick) {
                health->sick = true;
                print_health_info(dev);
 -              spin_lock(&health->wq_lock);
 -              if (!test_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags))
 -                      queue_work(health->wq, &health->work);
 -              else
 -                      dev_err(&dev->pdev->dev,
 -                              "new health works are not permitted at this stage\n");
 -              spin_unlock(&health->wq_lock);
 +              mlx5_trigger_health_work(dev);
        }
  }
  
@@@ -342,11 -333,10 +342,11 @@@ void mlx5_stop_health_poll(struct mlx5_
  void mlx5_drain_health_wq(struct mlx5_core_dev *dev)
  {
        struct mlx5_core_health *health = &dev->priv.health;
 +      unsigned long flags;
  
 -      spin_lock(&health->wq_lock);
 +      spin_lock_irqsave(&health->wq_lock, flags);
        set_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags);
 -      spin_unlock(&health->wq_lock);
 +      spin_unlock_irqrestore(&health->wq_lock, flags);
        cancel_delayed_work_sync(&health->recover_work);
        cancel_work_sync(&health->work);
  }
index ad0202cef203d95208be1a9d677a9fa47189e20e,fe5546bb41537f0af0c4bcfe9054ccceaa42bbb2..361cd112bb5bbd4a2b504c25941b4d1cd0551775
@@@ -56,7 -56,6 +56,7 @@@
  #ifdef CONFIG_MLX5_CORE_EN
  #include "eswitch.h"
  #endif
 +#include "fpga/core.h"
  
  MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
  MODULE_DESCRIPTION("Mellanox Connect-IB, ConnectX-4 core driver");
@@@ -613,7 -612,6 +613,6 @@@ static int mlx5_irq_set_affinity_hint(s
        struct mlx5_priv *priv  = &mdev->priv;
        struct msix_entry *msix = priv->msix_arr;
        int irq                 = msix[i + MLX5_EQ_VEC_COMP_BASE].vector;
-       int err;
  
        if (!zalloc_cpumask_var(&priv->irq_info[i].mask, GFP_KERNEL)) {
                mlx5_core_warn(mdev, "zalloc_cpumask_var failed");
        cpumask_set_cpu(cpumask_local_spread(i, priv->numa_node),
                        priv->irq_info[i].mask);
  
-       err = irq_set_affinity_hint(irq, priv->irq_info[i].mask);
-       if (err) {
-               mlx5_core_warn(mdev, "irq_set_affinity_hint failed,irq 0x%.4x",
-                              irq);
-               goto err_clear_mask;
-       }
+ #ifdef CONFIG_SMP
+       if (irq_set_affinity_hint(irq, priv->irq_info[i].mask))
+               mlx5_core_warn(mdev, "irq_set_affinity_hint failed, irq 0x%.4x", irq);
+ #endif
  
        return 0;
- err_clear_mask:
-       free_cpumask_var(priv->irq_info[i].mask);
-       return err;
  }
  
  static void mlx5_irq_clear_affinity_hint(struct mlx5_core_dev *mdev, int i)
@@@ -1114,16 -1106,10 +1107,16 @@@ static int mlx5_load_one(struct mlx5_co
                goto err_disable_msix;
        }
  
 +      err = mlx5_fpga_device_init(dev);
 +      if (err) {
 +              dev_err(&pdev->dev, "fpga device init failed %d\n", err);
 +              goto err_put_uars;
 +      }
 +
        err = mlx5_start_eqs(dev);
        if (err) {
                dev_err(&pdev->dev, "Failed to start pages and async EQs\n");
 -              goto err_put_uars;
 +              goto err_fpga_init;
        }
  
        err = alloc_comp_eqs(dev);
                goto err_sriov;
        }
  
 +      err = mlx5_fpga_device_start(dev);
 +      if (err) {
 +              dev_err(&pdev->dev, "fpga device start failed %d\n", err);
 +              goto err_reg_dev;
 +      }
 +
        if (mlx5_device_registered(dev)) {
                mlx5_attach_device(dev);
        } else {
@@@ -1195,9 -1175,6 +1188,9 @@@ err_affinity_hints
  err_stop_eqs:
        mlx5_stop_eqs(dev);
  
 +err_fpga_init:
 +      mlx5_fpga_device_cleanup(dev);
 +
  err_put_uars:
        mlx5_put_uars_page(dev, priv->uar);
  
@@@ -1262,7 -1239,6 +1255,7 @@@ static int mlx5_unload_one(struct mlx5_
        mlx5_irq_clear_affinity_hints(dev);
        free_comp_eqs(dev);
        mlx5_stop_eqs(dev);
 +      mlx5_fpga_device_cleanup(dev);
        mlx5_put_uars_page(dev, priv->uar);
        mlx5_disable_msix(dev);
        if (cleanup)
@@@ -1537,8 -1513,6 +1530,8 @@@ static const struct pci_device_id mlx5_
        { PCI_VDEVICE(MELLANOX, 0x101a), MLX5_PCI_DEV_IS_VF},   /* ConnectX-5 Ex VF */
        { PCI_VDEVICE(MELLANOX, 0x101b) },                      /* ConnectX-6 */
        { PCI_VDEVICE(MELLANOX, 0x101c), MLX5_PCI_DEV_IS_VF},   /* ConnectX-6 VF */
 +      { PCI_VDEVICE(MELLANOX, 0xa2d2) },                      /* BlueField integrated ConnectX-5 network controller */
 +      { PCI_VDEVICE(MELLANOX, 0xa2d3), MLX5_PCI_DEV_IS_VF},   /* BlueField integrated ConnectX-5 network controller VF */
        { 0, }
  };
  
index 2bd83920f5653c4f9a5e5633ec41144304d3a9f9,9097e42bec2e42d8ee864edea4a6be5d8c0a92cc..1a72bebc588ac6f9383fe71afd22a5ff6aac396f
  #include <linux/uaccess.h>
  
  #define MII_MARVELL_PHY_PAGE          22
 +#define MII_MARVELL_COPPER_PAGE               0x00
 +#define MII_MARVELL_FIBER_PAGE                0x01
 +#define MII_MARVELL_MSCR_PAGE         0x02
 +#define MII_MARVELL_LED_PAGE          0x03
 +#define MII_MARVELL_MISC_TEST_PAGE    0x06
 +#define MII_MARVELL_WOL_PAGE          0x11
  
  #define MII_M1011_IEVENT              0x13
  #define MII_M1011_IEVENT_CLEAR                0x0000
@@@ -60,6 -54,7 +60,6 @@@
  #define MII_M1011_PHY_SCR_MDI_X               0x0020
  #define MII_M1011_PHY_SCR_AUTO_CROSS  0x0060
  
 -#define MII_M1145_PHY_EXT_ADDR_PAGE   0x16
  #define MII_M1145_PHY_EXT_SR          0x1b
  #define MII_M1145_PHY_EXT_CR          0x14
  #define MII_M1145_RGMII_RX_DELAY      0x0080
  #define MII_M1111_HWCFG_FIBER_COPPER_AUTO     0x8000
  #define MII_M1111_HWCFG_FIBER_COPPER_RES      0x2000
  
 -#define MII_M1111_COPPER              0
 -#define MII_M1111_FIBER                       1
 -
 -#define MII_88E1121_PHY_MSCR_PAGE     2
  #define MII_88E1121_PHY_MSCR_REG      21
  #define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5)
  #define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4)
  #define MII_88E1318S_PHY_CSIER_WOL_EIE                      BIT(7)
  
  /* LED Timer Control Register */
 -#define MII_88E1318S_PHY_LED_PAGE                           0x03
  #define MII_88E1318S_PHY_LED_TCR                            0x12
  #define MII_88E1318S_PHY_LED_TCR_FORCE_INT                  BIT(15)
  #define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE                BIT(7)
  #define MII_88E1318S_PHY_MAGIC_PACKET_WORD1                 0x18
  #define MII_88E1318S_PHY_MAGIC_PACKET_WORD0                 0x19
  
 -#define MII_88E1318S_PHY_WOL_PAGE                           0x11
  #define MII_88E1318S_PHY_WOL_CTRL                           0x10
  #define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS          BIT(12)
  #define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE BIT(14)
  
  #define MII_88E1121_PHY_LED_CTRL      16
 -#define MII_88E1121_PHY_LED_PAGE      3
  #define MII_88E1121_PHY_LED_DEF               0x0030
  
  #define MII_M1011_PHY_STATUS          0x11
@@@ -187,29 -189,6 +187,29 @@@ struct marvell_priv 
        struct device *hwmon_dev;
  };
  
 +static int marvell_get_page(struct phy_device *phydev)
 +{
 +      return phy_read(phydev, MII_MARVELL_PHY_PAGE);
 +}
 +
 +static int marvell_set_page(struct phy_device *phydev, int page)
 +{
 +      return phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
 +}
 +
 +static int marvell_get_set_page(struct phy_device *phydev, int page)
 +{
 +      int oldpage = marvell_get_page(phydev);
 +
 +      if (oldpage < 0)
 +              return oldpage;
 +
 +      if (page != oldpage)
 +              return marvell_set_page(phydev, page);
 +
 +      return 0;
 +}
 +
  static int marvell_ack_interrupt(struct phy_device *phydev)
  {
        int err;
@@@ -228,11 -207,9 +228,11 @@@ static int marvell_config_intr(struct p
        int err;
  
        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
 -              err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
 +              err = phy_write(phydev, MII_M1011_IMASK,
 +                              MII_M1011_IMASK_INIT);
        else
 -              err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
 +              err = phy_write(phydev, MII_M1011_IMASK,
 +                              MII_M1011_IMASK_CLEAR);
  
        return err;
  }
@@@ -278,35 -255,6 +278,6 @@@ static int marvell_config_aneg(struct p
  {
        int err;
  
-       /* The Marvell PHY has an errata which requires
-        * that certain registers get written in order
-        * to restart autonegotiation
-        */
-       err = phy_write(phydev, MII_BMCR, BMCR_RESET);
-       if (err < 0)
-               return err;
-       err = phy_write(phydev, 0x1d, 0x1f);
-       if (err < 0)
-               return err;
-       err = phy_write(phydev, 0x1e, 0x200c);
-       if (err < 0)
-               return err;
-       err = phy_write(phydev, 0x1d, 0x5);
-       if (err < 0)
-               return err;
-       err = phy_write(phydev, 0x1e, 0);
-       if (err < 0)
-               return err;
-       err = phy_write(phydev, 0x1e, 0x100);
-       if (err < 0)
-               return err;
        err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
        if (err < 0)
                return err;
        if (phydev->autoneg != AUTONEG_ENABLE) {
                int bmcr;
  
 -              /*
 -               * A write to speed/duplex bits (that is performed by
 +              /* A write to speed/duplex bits (that is performed by
                 * genphy_config_aneg() call above) must be followed by
                 * a software reset. Otherwise, the write has no effect.
                 */
        return 0;
  }
  
+ static int m88e1101_config_aneg(struct phy_device *phydev)
+ {
+       int err;
+       /* This Marvell PHY has an errata which requires
+        * that certain registers get written in order
+        * to restart autonegotiation
+        */
+       err = phy_write(phydev, MII_BMCR, BMCR_RESET);
+       if (err < 0)
+               return err;
+       err = phy_write(phydev, 0x1d, 0x1f);
+       if (err < 0)
+               return err;
+       err = phy_write(phydev, 0x1e, 0x200c);
+       if (err < 0)
+               return err;
+       err = phy_write(phydev, 0x1d, 0x5);
+       if (err < 0)
+               return err;
+       err = phy_write(phydev, 0x1e, 0);
+       if (err < 0)
+               return err;
+       err = phy_write(phydev, 0x1e, 0x100);
+       if (err < 0)
+               return err;
+       return marvell_config_aneg(phydev);
+ }
  static int m88e1111_config_aneg(struct phy_device *phydev)
  {
        int err;
  }
  
  #ifdef CONFIG_OF_MDIO
 -/*
 - * Set and/or override some configuration registers based on the
 +/* Set and/or override some configuration registers based on the
   * marvell,reg-init property stored in the of_node for the phydev.
   *
   * marvell,reg-init = <reg-page reg mask value>,...;
@@@ -408,7 -394,7 +415,7 @@@ static int marvell_of_reg_init(struct p
        if (!paddr || len < (4 * sizeof(*paddr)))
                return 0;
  
 -      saved_page = phy_read(phydev, MII_MARVELL_PHY_PAGE);
 +      saved_page = marvell_get_page(phydev);
        if (saved_page < 0)
                return saved_page;
        current_page = saved_page;
        ret = 0;
        len /= sizeof(*paddr);
        for (i = 0; i < len - 3; i += 4) {
 -              u16 reg_page = be32_to_cpup(paddr + i);
 +              u16 page = be32_to_cpup(paddr + i);
                u16 reg = be32_to_cpup(paddr + i + 1);
                u16 mask = be32_to_cpup(paddr + i + 2);
                u16 val_bits = be32_to_cpup(paddr + i + 3);
                int val;
  
 -              if (reg_page != current_page) {
 -                      current_page = reg_page;
 -                      ret = phy_write(phydev, MII_MARVELL_PHY_PAGE, reg_page);
 +              if (page != current_page) {
 +                      current_page = page;
 +                      ret = marvell_set_page(phydev, page);
                        if (ret < 0)
                                goto err;
                }
                ret = phy_write(phydev, reg, val);
                if (ret < 0)
                        goto err;
 -
        }
  err:
        if (current_page != saved_page) {
 -              i = phy_write(phydev, MII_MARVELL_PHY_PAGE, saved_page);
 +              i = marvell_set_page(phydev, saved_page);
                if (ret == 0)
                        ret = i;
        }
@@@ -463,11 -450,15 +470,11 @@@ static int m88e1121_config_aneg(struct 
  {
        int err, oldpage, mscr;
  
 -      oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
 -
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
 -                      MII_88E1121_PHY_MSCR_PAGE);
 -      if (err < 0)
 -              return err;
 +      oldpage = marvell_get_set_page(phydev, MII_MARVELL_MSCR_PAGE);
 +      if (oldpage < 0)
 +              return oldpage;
  
        if (phy_interface_is_rgmii(phydev)) {
 -
                mscr = phy_read(phydev, MII_88E1121_PHY_MSCR_REG) &
                        MII_88E1121_PHY_MSCR_DELAY_MASK;
  
                        return err;
        }
  
 -      phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
 +      marvell_set_page(phydev, oldpage);
  
        err = phy_write(phydev, MII_BMCR, BMCR_RESET);
        if (err < 0)
@@@ -502,9 -493,12 +509,9 @@@ static int m88e1318_config_aneg(struct 
  {
        int err, oldpage, mscr;
  
 -      oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
 -
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
 -                      MII_88E1121_PHY_MSCR_PAGE);
 -      if (err < 0)
 -              return err;
 +      oldpage = marvell_get_set_page(phydev, MII_MARVELL_MSCR_PAGE);
 +      if (oldpage < 0)
 +              return oldpage;
  
        mscr = phy_read(phydev, MII_88E1318S_PHY_MSCR1_REG);
        mscr |= MII_88E1318S_PHY_MSCR1_PAD_ODD;
        if (err < 0)
                return err;
  
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
 +      err = marvell_set_page(phydev, oldpage);
        if (err < 0)
                return err;
  
@@@ -613,7 -607,7 +620,7 @@@ static int m88e1510_config_aneg(struct 
  {
        int err;
  
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
 +      err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
        if (err < 0)
                goto error;
  
                goto error;
  
        /* Then the fiber link */
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_FIBER);
 +      err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
        if (err < 0)
                goto error;
  
        if (err < 0)
                goto error;
  
 -      return phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
 +      return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
  
  error:
 -      phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
 +      marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
        return err;
  }
  
@@@ -657,7 -651,7 +664,7 @@@ static int m88e1116r_config_init(struc
  
        mdelay(500);
  
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0);
 +      err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
        if (err < 0)
                return err;
  
        if (err < 0)
                return err;
  
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 2);
 +      err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
        if (err < 0)
                return err;
        temp = phy_read(phydev, MII_M1116R_CONTROL_REG_MAC);
        err = phy_write(phydev, MII_M1116R_CONTROL_REG_MAC, temp);
        if (err < 0)
                return err;
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0);
 +      err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
        if (err < 0)
                return err;
  
@@@ -712,129 -706,103 +719,129 @@@ static int m88e3016_config_init(struct 
        return marvell_config_init(phydev);
  }
  
 -static int m88e1111_config_init(struct phy_device *phydev)
 +static int m88e1111_config_init_rgmii(struct phy_device *phydev)
  {
        int err;
        int temp;
  
 -      if (phy_interface_is_rgmii(phydev)) {
 +      temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
 +      if (temp < 0)
 +              return temp;
  
 -              temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
 -              if (temp < 0)
 -                      return temp;
 +      if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
 +              temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
 +      } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
 +              temp &= ~MII_M1111_TX_DELAY;
 +              temp |= MII_M1111_RX_DELAY;
 +      } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
 +              temp &= ~MII_M1111_RX_DELAY;
 +              temp |= MII_M1111_TX_DELAY;
 +      }
  
 -              if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
 -                      temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
 -              } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
 -                      temp &= ~MII_M1111_TX_DELAY;
 -                      temp |= MII_M1111_RX_DELAY;
 -              } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
 -                      temp &= ~MII_M1111_RX_DELAY;
 -                      temp |= MII_M1111_TX_DELAY;
 -              }
 +      err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
 +      if (err < 0)
 +              return err;
  
 -              err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
 -              if (err < 0)
 -                      return err;
 +      temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
 +      if (temp < 0)
 +              return temp;
  
 -              temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
 -              if (temp < 0)
 -                      return temp;
 +      temp &= ~(MII_M1111_HWCFG_MODE_MASK);
  
 -              temp &= ~(MII_M1111_HWCFG_MODE_MASK);
 +      if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
 +              temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
 +      else
 +              temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
  
 -              if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
 -                      temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
 -              else
 -                      temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
 +      return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
 +}
  
 -              err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
 -              if (err < 0)
 -                      return err;
 -      }
 +static int m88e1111_config_init_sgmii(struct phy_device *phydev)
 +{
 +      int err;
 +      int temp;
  
 -      if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
 -              temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
 -              if (temp < 0)
 -                      return temp;
 +      temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
 +      if (temp < 0)
 +              return temp;
  
 -              temp &= ~(MII_M1111_HWCFG_MODE_MASK);
 -              temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK;
 -              temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
 +      temp &= ~(MII_M1111_HWCFG_MODE_MASK);
 +      temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK;
 +      temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
  
 -              err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
 -              if (err < 0)
 -                      return err;
 +      err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
 +      if (err < 0)
 +              return err;
  
 -              /* make sure copper is selected */
 -              err = phy_read(phydev, MII_M1145_PHY_EXT_ADDR_PAGE);
 -              if (err < 0)
 -                      return err;
 +      /* make sure copper is selected */
 +      return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
 +}
  
 -              err = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE,
 -                              err & (~0xff));
 -              if (err < 0)
 -                      return err;
 -      }
 +static int m88e1111_config_init_rtbi(struct phy_device *phydev)
 +{
 +      int err;
 +      int temp;
  
 -      if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
 -              temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
 -              if (temp < 0)
 -                      return temp;
 -              temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
 -              err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
 -              if (err < 0)
 -                      return err;
 +      temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
 +      if (temp < 0)
 +              return temp;
  
 -              temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
 -              if (temp < 0)
 -                      return temp;
 -              temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES);
 -              temp |= 0x7 | MII_M1111_HWCFG_FIBER_COPPER_AUTO;
 -              err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
 -              if (err < 0)
 +      temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
 +      err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
 +      if (err < 0)
 +              return err;
 +
 +      temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
 +      if (temp < 0)
 +              return temp;
 +
 +      temp &= ~(MII_M1111_HWCFG_MODE_MASK |
 +                MII_M1111_HWCFG_FIBER_COPPER_RES);
 +      temp |= 0x7 | MII_M1111_HWCFG_FIBER_COPPER_AUTO;
 +
 +      err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
 +      if (err < 0)
 +              return err;
 +
 +      /* soft reset */
 +      err = phy_write(phydev, MII_BMCR, BMCR_RESET);
 +      if (err < 0)
 +              return err;
 +
 +      do
 +              temp = phy_read(phydev, MII_BMCR);
 +      while (temp & BMCR_RESET);
 +
 +      temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
 +      if (temp < 0)
 +              return temp;
 +
 +      temp &= ~(MII_M1111_HWCFG_MODE_MASK |
 +                MII_M1111_HWCFG_FIBER_COPPER_RES);
 +      temp |= MII_M1111_HWCFG_MODE_COPPER_RTBI |
 +              MII_M1111_HWCFG_FIBER_COPPER_AUTO;
 +
 +      return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
 +}
 +
 +static int m88e1111_config_init(struct phy_device *phydev)
 +{
 +      int err;
 +
 +      if (phy_interface_is_rgmii(phydev)) {
 +              err = m88e1111_config_init_rgmii(phydev);
 +              if (err)
                        return err;
 +      }
  
 -              /* soft reset */
 -              err = phy_write(phydev, MII_BMCR, BMCR_RESET);
 +      if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
 +              err = m88e1111_config_init_sgmii(phydev);
                if (err < 0)
                        return err;
 -              do
 -                      temp = phy_read(phydev, MII_BMCR);
 -              while (temp & BMCR_RESET);
 +      }
  
 -              temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
 -              if (temp < 0)
 -                      return temp;
 -              temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES);
 -              temp |= MII_M1111_HWCFG_MODE_COPPER_RTBI | MII_M1111_HWCFG_FIBER_COPPER_AUTO;
 -              err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
 +      if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
 +              err = m88e1111_config_init_rtbi(phydev);
                if (err < 0)
                        return err;
        }
@@@ -850,9 -818,11 +857,9 @@@ static int m88e1121_config_init(struct 
  {
        int err, oldpage;
  
 -      oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
 -
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_88E1121_PHY_LED_PAGE);
 -      if (err < 0)
 -              return err;
 +      oldpage = marvell_get_set_page(phydev, MII_MARVELL_LED_PAGE);
 +      if (oldpage < 0)
 +              return oldpage;
  
        /* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
        err = phy_write(phydev, MII_88E1121_PHY_LED_CTRL,
        if (err < 0)
                return err;
  
 -      phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
 +      marvell_set_page(phydev, oldpage);
  
        /* Set marvell,reg-init configuration from device tree */
        return marvell_config_init(phydev);
@@@ -874,7 -844,7 +881,7 @@@ static int m88e1510_config_init(struct 
        /* SGMII-to-Copper mode initialization */
        if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
                /* Select page 18 */
 -              err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 18);
 +              err = marvell_set_page(phydev, 18);
                if (err < 0)
                        return err;
  
                        return err;
  
                /* Reset page selection */
 -              err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0);
 +              err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
                if (err < 0)
                        return err;
        }
@@@ -923,7 -893,7 +930,7 @@@ static int m88e1118_config_init(struct 
        int err;
  
        /* Change address */
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002);
 +      err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
        if (err < 0)
                return err;
  
                return err;
  
        /* Change address */
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0003);
 +      err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
        if (err < 0)
                return err;
  
                return err;
  
        /* Reset address */
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0);
 +      err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
        if (err < 0)
                return err;
  
@@@ -962,7 -932,7 +969,7 @@@ static int m88e1149_config_init(struct 
        int err;
  
        /* Change address */
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002);
 +      err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
        if (err < 0)
                return err;
  
                return err;
  
        /* Reset address */
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0);
 +      err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
        if (err < 0)
                return err;
  
        return phy_write(phydev, MII_BMCR, BMCR_RESET);
  }
  
 +static int m88e1145_config_init_rgmii(struct phy_device *phydev)
 +{
 +      int err;
 +      int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR);
 +
 +      if (temp < 0)
 +              return temp;
 +
 +      temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY);
 +
 +      err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp);
 +      if (err < 0)
 +              return err;
 +
 +      if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
 +              err = phy_write(phydev, 0x1d, 0x0012);
 +              if (err < 0)
 +                      return err;
 +
 +              temp = phy_read(phydev, 0x1e);
 +              if (temp < 0)
 +                      return temp;
 +
 +              temp &= 0xf03f;
 +              temp |= 2 << 9; /* 36 ohm */
 +              temp |= 2 << 6; /* 39 ohm */
 +
 +              err = phy_write(phydev, 0x1e, temp);
 +              if (err < 0)
 +                      return err;
 +
 +              err = phy_write(phydev, 0x1d, 0x3);
 +              if (err < 0)
 +                      return err;
 +
 +              err = phy_write(phydev, 0x1e, 0x8000);
 +      }
 +      return err;
 +}
 +
 +static int m88e1145_config_init_sgmii(struct phy_device *phydev)
 +{
 +      int temp = phy_read(phydev, MII_M1145_PHY_EXT_SR);
 +
 +      if (temp < 0)
 +              return temp;
 +
 +      temp &= ~MII_M1145_HWCFG_MODE_MASK;
 +      temp |= MII_M1145_HWCFG_MODE_SGMII_NO_CLK;
 +      temp |= MII_M1145_HWCFG_FIBER_COPPER_AUTO;
 +
 +      return phy_write(phydev, MII_M1145_PHY_EXT_SR, temp);
 +}
 +
  static int m88e1145_config_init(struct phy_device *phydev)
  {
        int err;
 -      int temp;
  
        /* Take care of errata E0 & E1 */
        err = phy_write(phydev, 0x1d, 0x001b);
                return err;
  
        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
 -              int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR);
 -              if (temp < 0)
 -                      return temp;
 -
 -              temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY);
 -
 -              err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp);
 +              err = m88e1145_config_init_rgmii(phydev);
                if (err < 0)
                        return err;
 -
 -              if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
 -                      err = phy_write(phydev, 0x1d, 0x0012);
 -                      if (err < 0)
 -                              return err;
 -
 -                      temp = phy_read(phydev, 0x1e);
 -                      if (temp < 0)
 -                              return temp;
 -
 -                      temp &= 0xf03f;
 -                      temp |= 2 << 9; /* 36 ohm */
 -                      temp |= 2 << 6; /* 39 ohm */
 -
 -                      err = phy_write(phydev, 0x1e, temp);
 -                      if (err < 0)
 -                              return err;
 -
 -                      err = phy_write(phydev, 0x1d, 0x3);
 -                      if (err < 0)
 -                              return err;
 -
 -                      err = phy_write(phydev, 0x1e, 0x8000);
 -                      if (err < 0)
 -                              return err;
 -              }
        }
  
        if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
 -              temp = phy_read(phydev, MII_M1145_PHY_EXT_SR);
 -              if (temp < 0)
 -                      return temp;
 -
 -              temp &= ~MII_M1145_HWCFG_MODE_MASK;
 -              temp |= MII_M1145_HWCFG_MODE_SGMII_NO_CLK;
 -              temp |= MII_M1145_HWCFG_FIBER_COPPER_AUTO;
 -
 -              err = phy_write(phydev, MII_M1145_PHY_EXT_SR, temp);
 +              err = m88e1145_config_init_sgmii(phydev);
                if (err < 0)
                        return err;
        }
@@@ -1108,8 -1065,7 +1115,8 @@@ static int marvell_update_link(struct p
        int status;
  
        /* Use the generic register for copper link, or specific
 -       * register for fiber case */
 +       * register for fiber case
 +       */
        if (fiber) {
                status = phy_read(phydev, MII_M1011_PHY_STATUS);
                if (status < 0)
        return 0;
  }
  
 -/* marvell_read_status_page
 - *
 - * Description:
 - *   Check the link, then figure out the current state
 - *   by comparing what we advertise with what the link partner
 - *   advertises.  Start by checking the gigabit possibilities,
 - *   then move on to 10/100.
 - */
 -static int marvell_read_status_page(struct phy_device *phydev, int page)
 +static int marvell_read_status_page_an(struct phy_device *phydev,
 +                                     int fiber)
  {
 -      int adv;
 -      int err;
 +      int status;
        int lpa;
        int lpagb;
 -      int status = 0;
 -      int fiber;
 +      int adv;
  
 -      /* Detect and update the link, but return if there
 -       * was an error */
 -      if (page == MII_M1111_FIBER)
 -              fiber = 1;
 -      else
 -              fiber = 0;
 +      status = phy_read(phydev, MII_M1011_PHY_STATUS);
 +      if (status < 0)
 +              return status;
  
 -      err = marvell_update_link(phydev, fiber);
 -      if (err)
 -              return err;
 +      lpa = phy_read(phydev, MII_LPA);
 +      if (lpa < 0)
 +              return lpa;
  
 -      if (AUTONEG_ENABLE == phydev->autoneg) {
 -              status = phy_read(phydev, MII_M1011_PHY_STATUS);
 -              if (status < 0)
 -                      return status;
 +      lpagb = phy_read(phydev, MII_STAT1000);
 +      if (lpagb < 0)
 +              return lpagb;
  
 -              lpa = phy_read(phydev, MII_LPA);
 -              if (lpa < 0)
 -                      return lpa;
 +      adv = phy_read(phydev, MII_ADVERTISE);
 +      if (adv < 0)
 +              return adv;
  
 -              lpagb = phy_read(phydev, MII_STAT1000);
 -              if (lpagb < 0)
 -                      return lpagb;
 +      lpa &= adv;
  
 -              adv = phy_read(phydev, MII_ADVERTISE);
 -              if (adv < 0)
 -                      return adv;
 +      if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
 +              phydev->duplex = DUPLEX_FULL;
 +      else
 +              phydev->duplex = DUPLEX_HALF;
  
 -              lpa &= adv;
 +      status = status & MII_M1011_PHY_STATUS_SPD_MASK;
 +      phydev->pause = 0;
 +      phydev->asym_pause = 0;
  
 -              if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
 -                      phydev->duplex = DUPLEX_FULL;
 -              else
 -                      phydev->duplex = DUPLEX_HALF;
 +      switch (status) {
 +      case MII_M1011_PHY_STATUS_1000:
 +              phydev->speed = SPEED_1000;
 +              break;
  
 -              status = status & MII_M1011_PHY_STATUS_SPD_MASK;
 -              phydev->pause = phydev->asym_pause = 0;
 +      case MII_M1011_PHY_STATUS_100:
 +              phydev->speed = SPEED_100;
 +              break;
  
 -              switch (status) {
 -              case MII_M1011_PHY_STATUS_1000:
 -                      phydev->speed = SPEED_1000;
 -                      break;
 +      default:
 +              phydev->speed = SPEED_10;
 +              break;
 +      }
  
 -              case MII_M1011_PHY_STATUS_100:
 -                      phydev->speed = SPEED_100;
 -                      break;
 +      if (!fiber) {
 +              phydev->lp_advertising =
 +                      mii_stat1000_to_ethtool_lpa_t(lpagb) |
 +                      mii_lpa_to_ethtool_lpa_t(lpa);
  
 -              default:
 -                      phydev->speed = SPEED_10;
 -                      break;
 +              if (phydev->duplex == DUPLEX_FULL) {
 +                      phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
 +                      phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
                }
 -
 -              if (!fiber) {
 -                      phydev->lp_advertising = mii_stat1000_to_ethtool_lpa_t(lpagb) |
 -                                       mii_lpa_to_ethtool_lpa_t(lpa);
 -
 -                      if (phydev->duplex == DUPLEX_FULL) {
 -                              phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
 -                              phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
 -                      }
 -              } else {
 -                      /* The fiber link is only 1000M capable */
 -                      phydev->lp_advertising = fiber_lpa_to_ethtool_lpa_t(lpa);
 -
 -                      if (phydev->duplex == DUPLEX_FULL) {
 -                              if (!(lpa & LPA_PAUSE_FIBER)) {
 -                                      phydev->pause = 0;
 -                                      phydev->asym_pause = 0;
 -                              } else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
 -                                      phydev->pause = 1;
 -                                      phydev->asym_pause = 1;
 -                              } else {
 -                                      phydev->pause = 1;
 -                                      phydev->asym_pause = 0;
 -                              }
 +      } else {
 +              /* The fiber link is only 1000M capable */
 +              phydev->lp_advertising = fiber_lpa_to_ethtool_lpa_t(lpa);
 +
 +              if (phydev->duplex == DUPLEX_FULL) {
 +                      if (!(lpa & LPA_PAUSE_FIBER)) {
 +                              phydev->pause = 0;
 +                              phydev->asym_pause = 0;
 +                      } else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
 +                              phydev->pause = 1;
 +                              phydev->asym_pause = 1;
 +                      } else {
 +                              phydev->pause = 1;
 +                              phydev->asym_pause = 0;
                        }
                }
 -      } else {
 -              int bmcr = phy_read(phydev, MII_BMCR);
 +      }
 +      return 0;
 +}
  
 -              if (bmcr < 0)
 -                      return bmcr;
 +static int marvell_read_status_page_fixed(struct phy_device *phydev)
 +{
 +      int bmcr = phy_read(phydev, MII_BMCR);
  
 -              if (bmcr & BMCR_FULLDPLX)
 -                      phydev->duplex = DUPLEX_FULL;
 -              else
 -                      phydev->duplex = DUPLEX_HALF;
 +      if (bmcr < 0)
 +              return bmcr;
  
 -              if (bmcr & BMCR_SPEED1000)
 -                      phydev->speed = SPEED_1000;
 -              else if (bmcr & BMCR_SPEED100)
 -                      phydev->speed = SPEED_100;
 -              else
 -                      phydev->speed = SPEED_10;
 +      if (bmcr & BMCR_FULLDPLX)
 +              phydev->duplex = DUPLEX_FULL;
 +      else
 +              phydev->duplex = DUPLEX_HALF;
  
 -              phydev->pause = phydev->asym_pause = 0;
 -              phydev->lp_advertising = 0;
 -      }
 +      if (bmcr & BMCR_SPEED1000)
 +              phydev->speed = SPEED_1000;
 +      else if (bmcr & BMCR_SPEED100)
 +              phydev->speed = SPEED_100;
 +      else
 +              phydev->speed = SPEED_10;
 +
 +      phydev->pause = 0;
 +      phydev->asym_pause = 0;
 +      phydev->lp_advertising = 0;
  
        return 0;
  }
  
 +/* marvell_read_status_page
 + *
 + * Description:
 + *   Check the link, then figure out the current state
 + *   by comparing what we advertise with what the link partner
 + *   advertises.  Start by checking the gigabit possibilities,
 + *   then move on to 10/100.
 + */
 +static int marvell_read_status_page(struct phy_device *phydev, int page)
 +{
 +      int fiber;
 +      int err;
 +
 +      /* Detect and update the link, but return if there
 +       * was an error
 +       */
 +      if (page == MII_MARVELL_FIBER_PAGE)
 +              fiber = 1;
 +      else
 +              fiber = 0;
 +
 +      err = marvell_update_link(phydev, fiber);
 +      if (err)
 +              return err;
 +
 +      if (phydev->autoneg == AUTONEG_ENABLE)
 +              err = marvell_read_status_page_an(phydev, fiber);
 +      else
 +              err = marvell_read_status_page_fixed(phydev);
 +
 +      return err;
 +}
 +
  /* marvell_read_status
   *
   * Some Marvell's phys have two modes: fiber and copper.
@@@ -1279,34 -1217,33 +1286,34 @@@ static int marvell_read_status(struct p
        /* Check the fiber mode first */
        if (phydev->supported & SUPPORTED_FIBRE &&
            phydev->interface != PHY_INTERFACE_MODE_SGMII) {
 -              err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_FIBER);
 +              err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
                if (err < 0)
                        goto error;
  
 -              err = marvell_read_status_page(phydev, MII_M1111_FIBER);
 +              err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE);
                if (err < 0)
                        goto error;
  
 -              /* If the fiber link is up, it is the selected and used link.
 -               * In this case, we need to stay in the fiber page.
 -               * Please to be careful about that, avoid to restore Copper page
 -               * in other functions which could break the behaviour
 -               * for some fiber phy like 88E1512.
 -               * */
 +              /* If the fiber link is up, it is the selected and
 +               * used link. In this case, we need to stay in the
 +               * fiber page. Please to be careful about that, avoid
 +               * to restore Copper page in other functions which
 +               * could break the behaviour for some fiber phy like
 +               * 88E1512.
 +               */
                if (phydev->link)
                        return 0;
  
                /* If fiber link is down, check and save copper mode state */
 -              err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
 +              err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
                if (err < 0)
                        goto error;
        }
  
 -      return marvell_read_status_page(phydev, MII_M1111_COPPER);
 +      return marvell_read_status_page(phydev, MII_MARVELL_COPPER_PAGE);
  
  error:
 -      phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
 +      marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
        return err;
  }
  
@@@ -1321,7 -1258,7 +1328,7 @@@ static int marvell_suspend(struct phy_d
  
        /* Suspend the fiber mode first */
        if (!(phydev->supported & SUPPORTED_FIBRE)) {
 -              err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_FIBER);
 +              err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
                if (err < 0)
                        goto error;
  
                        goto error;
  
                /* Then, the copper link */
 -              err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
 +              err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
                if (err < 0)
                        goto error;
        }
        return genphy_suspend(phydev);
  
  error:
 -      phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
 +      marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
        return err;
  }
  
@@@ -1355,7 -1292,7 +1362,7 @@@ static int marvell_resume(struct phy_de
  
        /* Resume the fiber mode first */
        if (!(phydev->supported & SUPPORTED_FIBRE)) {
 -              err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_FIBER);
 +              err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
                if (err < 0)
                        goto error;
  
                        goto error;
  
                /* Then, the copper link */
 -              err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
 +              err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
                if (err < 0)
                        goto error;
        }
        return genphy_resume(phydev);
  
  error:
 -      phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
 +      marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
        return err;
  }
  
  static int marvell_aneg_done(struct phy_device *phydev)
  {
        int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
 +
        return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
  }
  
@@@ -1397,33 -1333,32 +1404,33 @@@ static int m88e1121_did_interrupt(struc
        return 0;
  }
  
 -static void m88e1318_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
 +static void m88e1318_get_wol(struct phy_device *phydev,
 +                           struct ethtool_wolinfo *wol)
  {
        wol->supported = WAKE_MAGIC;
        wol->wolopts = 0;
  
 -      if (phy_write(phydev, MII_MARVELL_PHY_PAGE,
 -                    MII_88E1318S_PHY_WOL_PAGE) < 0)
 +      if (marvell_set_page(phydev, MII_MARVELL_WOL_PAGE) < 0)
                return;
  
        if (phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL) &
            MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
                wol->wolopts |= WAKE_MAGIC;
  
 -      if (phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00) < 0)
 +      if (marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE) < 0)
                return;
  }
  
 -static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
 +static int m88e1318_set_wol(struct phy_device *phydev,
 +                          struct ethtool_wolinfo *wol)
  {
        int err, oldpage, temp;
  
 -      oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
 +      oldpage = marvell_get_page(phydev);
  
        if (wol->wolopts & WAKE_MAGIC) {
                /* Explicitly switch to page 0x00, just to be sure */
 -              err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00);
 +              err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
                if (err < 0)
                        return err;
  
                if (err < 0)
                        return err;
  
 -              err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
 -                              MII_88E1318S_PHY_LED_PAGE);
 +              err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
                if (err < 0)
                        return err;
  
                if (err < 0)
                        return err;
  
 -              err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
 -                              MII_88E1318S_PHY_WOL_PAGE);
 +              err = marvell_set_page(phydev, MII_MARVELL_WOL_PAGE);
                if (err < 0)
                        return err;
  
                if (err < 0)
                        return err;
        } else {
 -              err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
 -                              MII_88E1318S_PHY_WOL_PAGE);
 +              err = marvell_set_page(phydev, MII_MARVELL_WOL_PAGE);
                if (err < 0)
                        return err;
  
                        return err;
        }
  
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
 +      err = marvell_set_page(phydev, oldpage);
        if (err < 0)
                return err;
  
@@@ -1521,11 -1459,13 +1528,11 @@@ static u64 marvell_get_stat(struct phy_
  {
        struct marvell_hw_stat stat = marvell_hw_stats[i];
        struct marvell_priv *priv = phydev->priv;
 -      int err, oldpage, val;
 +      int oldpage, val;
        u64 ret;
  
 -      oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
 -      err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
 -                      stat.page);
 -      if (err < 0)
 +      oldpage = marvell_get_set_page(phydev, stat.page);
 +      if (oldpage < 0)
                return UINT64_MAX;
  
        val = phy_read(phydev, stat.reg);
                ret = priv->stats[i];
        }
  
 -      phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
 +      marvell_set_page(phydev, oldpage);
  
        return ret;
  }
@@@ -1554,7 -1494,6 +1561,7 @@@ static void marvell_get_stats(struct ph
  #ifdef CONFIG_HWMON
  static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
  {
 +      int oldpage;
        int ret;
        int val;
  
  
        mutex_lock(&phydev->lock);
  
 -      ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
 -      if (ret < 0)
 -              goto error;
 +      oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
 +      if (oldpage < 0) {
 +              mutex_unlock(&phydev->lock);
 +              return oldpage;
 +      }
  
        /* Enable temperature sensor */
        ret = phy_read(phydev, MII_88E1121_MISC_TEST);
        *temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
  
  error:
 -      phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
 +      marvell_set_page(phydev, oldpage);
        mutex_unlock(&phydev->lock);
  
        return ret;
@@@ -1673,18 -1610,15 +1680,18 @@@ static const struct hwmon_chip_info m88
  
  static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
  {
 +      int oldpage;
        int ret;
  
        *temp = 0;
  
        mutex_lock(&phydev->lock);
  
 -      ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
 -      if (ret < 0)
 -              goto error;
 +      oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
 +      if (oldpage < 0) {
 +              mutex_unlock(&phydev->lock);
 +              return oldpage;
 +      }
  
        ret = phy_read(phydev, MII_88E1510_TEMP_SENSOR);
        if (ret < 0)
        *temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
  
  error:
 -      phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
 +      marvell_set_page(phydev, oldpage);
        mutex_unlock(&phydev->lock);
  
        return ret;
  
  int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
  {
 +      int oldpage;
        int ret;
  
        *temp = 0;
  
        mutex_lock(&phydev->lock);
  
 -      ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
 -      if (ret < 0)
 -              goto error;
 +      oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
 +      if (oldpage < 0) {
 +              mutex_unlock(&phydev->lock);
 +              return oldpage;
 +      }
  
        ret = phy_read(phydev, MII_88E1121_MISC_TEST);
        if (ret < 0)
        *temp *= 1000;
  
  error:
 -      phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
 +      marvell_set_page(phydev, oldpage);
        mutex_unlock(&phydev->lock);
  
        return ret;
  
  int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
  {
 +      int oldpage;
        int ret;
  
        mutex_lock(&phydev->lock);
  
 -      ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
 -      if (ret < 0)
 -              goto error;
 +      oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
 +      if (oldpage < 0) {
 +              mutex_unlock(&phydev->lock);
 +              return oldpage;
 +      }
  
        ret = phy_read(phydev, MII_88E1121_MISC_TEST);
        if (ret < 0)
                        (temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT));
  
  error:
 -      phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
 +      marvell_set_page(phydev, oldpage);
        mutex_unlock(&phydev->lock);
  
        return ret;
  
  int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
  {
 +      int oldpage;
        int ret;
  
        *alarm = false;
  
        mutex_lock(&phydev->lock);
  
 -      ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
 -      if (ret < 0)
 -              goto error;
 +      oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
 +      if (oldpage < 0) {
 +              mutex_unlock(&phydev->lock);
 +              return oldpage;
 +      }
  
        ret = phy_read(phydev, MII_88E1121_MISC_TEST);
        if (ret < 0)
        *alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
  
  error:
 -      phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
 +      marvell_set_page(phydev, oldpage);
        mutex_unlock(&phydev->lock);
  
        return ret;
@@@ -1975,7 -1900,7 +1982,7 @@@ static struct phy_driver marvell_driver
                .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
                .config_init = &marvell_config_init,
-               .config_aneg = &marvell_config_aneg,
+               .config_aneg = &m88e1101_config_aneg,
                .read_status = &genphy_read_status,
                .ack_interrupt = &marvell_ack_interrupt,
                .config_intr = &marvell_config_intr,
index 786a43843da98baf3741ce0084b426af2bcc695e,a940ec6a046cfd0dc8c3016226c71ea9e2c343e5..b26a478930eb9010b662bd5ec1403f5b74170276
@@@ -300,8 -300,6 +300,8 @@@ enum mlx5_event 
  
        MLX5_EVENT_TYPE_PAGE_FAULT         = 0xc,
        MLX5_EVENT_TYPE_NIC_VPORT_CHANGE   = 0xd,
 +
 +      MLX5_EVENT_TYPE_FPGA_ERROR         = 0x20,
  };
  
  enum {
@@@ -789,8 -787,14 +789,14 @@@ enum 
  };
  
  enum {
-       CQE_RSS_HTYPE_IP        = 0x3 << 6,
-       CQE_RSS_HTYPE_L4        = 0x3 << 2,
+       CQE_RSS_HTYPE_IP        = 0x3 << 2,
+       /* cqe->rss_hash_type[3:2] - IP destination selected for hash
+        * (00 = none,  01 = IPv4, 10 = IPv6, 11 = Reserved)
+        */
+       CQE_RSS_HTYPE_L4        = 0x3 << 6,
+       /* cqe->rss_hash_type[7:6] - L4 destination selected for hash
+        * (00 = none, 01 = TCP. 10 = UDP, 11 = IPSEC.SPI
+        */
  };
  
  enum {
@@@ -969,7 -973,6 +975,7 @@@ enum mlx5_cap_type 
        MLX5_CAP_RESERVED,
        MLX5_CAP_VECTOR_CALC,
        MLX5_CAP_QOS,
 +      MLX5_CAP_FPGA,
        /* NUM OF CAP Types */
        MLX5_CAP_NUM
  };
@@@ -1091,9 -1094,6 +1097,9 @@@ enum mlx5_mcam_feature_groups 
  #define MLX5_CAP_MCAM_FEATURE(mdev, fld) \
        MLX5_GET(mcam_reg, (mdev)->caps.mcam, mng_feature_cap_mask.enhanced_features.fld)
  
 +#define MLX5_CAP_FPGA(mdev, cap) \
 +      MLX5_GET(fpga_cap, (mdev)->caps.hca_cur[MLX5_CAP_FPGA], cap)
 +
  enum {
        MLX5_CMD_STAT_OK                        = 0x0,
        MLX5_CMD_STAT_INT_ERR                   = 0x1,
index 55bb712643cb11233273e980db06cb41abdba300,93273d9ea4d145f125846f0c9a56a5a6e74915e4..6ea2f5734e374277f9246322cd48c74f0b1cac94
@@@ -108,8 -108,6 +108,8 @@@ enum 
        MLX5_REG_QTCT            = 0x400a,
        MLX5_REG_DCBX_PARAM      = 0x4020,
        MLX5_REG_DCBX_APP        = 0x4021,
 +      MLX5_REG_FPGA_CAP        = 0x4022,
 +      MLX5_REG_FPGA_CTRL       = 0x4023,
        MLX5_REG_PCAP            = 0x5001,
        MLX5_REG_PMTU            = 0x5003,
        MLX5_REG_PTYS            = 0x5004,
@@@ -763,9 -761,6 +763,9 @@@ struct mlx5_core_dev 
        atomic_t                num_qps;
        u32                     issi;
        struct mlx5e_resources  mlx5e_res;
 +#ifdef CONFIG_MLX5_FPGA
 +      struct mlx5_fpga_device *fpga;
 +#endif
  #ifdef CONFIG_RFS_ACCEL
        struct cpu_rmap         *rmap;
  #endif
@@@ -792,7 -787,12 +792,12 @@@ enum 
  
  typedef void (*mlx5_cmd_cbk_t)(int status, void *context);
  
+ enum {
+       MLX5_CMD_ENT_STATE_PENDING_COMP,
+ };
  struct mlx5_cmd_work_ent {
+       unsigned long           state;
        struct mlx5_cmd_msg    *in;
        struct mlx5_cmd_msg    *out;
        void                   *uout;
@@@ -895,6 -895,11 +900,6 @@@ static inline u16 cmdif_rev(struct mlx5
        return ioread32be(&dev->iseg->cmdif_rev_fw_sub) >> 16;
  }
  
 -static inline void *mlx5_vzalloc(unsigned long size)
 -{
 -      return kvzalloc(size, GFP_KERNEL);
 -}
 -
  static inline u32 mlx5_base_mkey(const u32 key)
  {
        return key & 0xffffff00u;
@@@ -920,7 -925,6 +925,7 @@@ int mlx5_health_init(struct mlx5_core_d
  void mlx5_start_health_poll(struct mlx5_core_dev *dev);
  void mlx5_stop_health_poll(struct mlx5_core_dev *dev);
  void mlx5_drain_health_wq(struct mlx5_core_dev *dev);
 +void mlx5_trigger_health_work(struct mlx5_core_dev *dev);
  int mlx5_buf_alloc_node(struct mlx5_core_dev *dev, int size,
                        struct mlx5_buf *buf, int node);
  int mlx5_buf_alloc(struct mlx5_core_dev *dev, int size, struct mlx5_buf *buf);
@@@ -977,7 -981,7 +982,7 @@@ void mlx5_cq_completion(struct mlx5_cor
  void mlx5_rsc_event(struct mlx5_core_dev *dev, u32 rsn, int event_type);
  void mlx5_srq_event(struct mlx5_core_dev *dev, u32 srqn, int event_type);
  struct mlx5_core_srq *mlx5_core_get_srq(struct mlx5_core_dev *dev, u32 srqn);
- void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec);
+ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool forced);
  void mlx5_cq_event(struct mlx5_core_dev *dev, u32 cqn, int event_type);
  int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
                       int nent, u64 mask, const char *name,
diff --combined include/net/dst.h
index 755a0eedffdefadc5f505f43f79186800cac24bb,cfc0437841665d7ed46a714915c50d723c24901c..1969008783d80f32968b7683b8ed3df558c285ff
@@@ -31,9 -31,9 +31,9 @@@
  struct sk_buff;
  
  struct dst_entry {
 +      struct net_device       *dev;
        struct rcu_head         rcu_head;
        struct dst_entry        *child;
 -      struct net_device       *dev;
        struct  dst_ops         *ops;
        unsigned long           _metrics;
        unsigned long           expires;
        };
  };
  
+ struct dst_metrics {
+       u32             metrics[RTAX_MAX];
+       atomic_t        refcnt;
+ };
+ extern const struct dst_metrics dst_default_metrics;
  u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old);
- extern const u32 dst_default_metrics[];
  
  #define DST_METRICS_READ_ONLY         0x1UL
+ #define DST_METRICS_REFCOUNTED                0x2UL
  #define DST_METRICS_FLAGS             0x3UL
  #define __DST_METRICS_PTR(Y)  \
        ((u32 *)((Y) & ~DST_METRICS_FLAGS))
diff --combined include/net/ip_fib.h
index 25f5c516afd170bf049f272f9834d83d809df555,f7f6aa789c6174c41ca9739206d586c559c1f3a1..dcbfd5dfd25e95a2d2933bf51089a66876b7c5b3
@@@ -114,11 -114,11 +114,11 @@@ struct fib_info 
        __be32                  fib_prefsrc;
        u32                     fib_tb_id;
        u32                     fib_priority;
-       u32                     *fib_metrics;
- #define fib_mtu fib_metrics[RTAX_MTU-1]
- #define fib_window fib_metrics[RTAX_WINDOW-1]
- #define fib_rtt fib_metrics[RTAX_RTT-1]
- #define fib_advmss fib_metrics[RTAX_ADVMSS-1]
+       struct dst_metrics      *fib_metrics;
+ #define fib_mtu fib_metrics->metrics[RTAX_MTU-1]
+ #define fib_window fib_metrics->metrics[RTAX_WINDOW-1]
+ #define fib_rtt fib_metrics->metrics[RTAX_RTT-1]
+ #define fib_advmss fib_metrics->metrics[RTAX_ADVMSS-1]
        int                     fib_nhs;
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
        int                     fib_weight;
@@@ -136,7 -136,6 +136,7 @@@ struct fib_rule
  
  struct fib_table;
  struct fib_result {
 +      __be32          prefix;
        unsigned char   prefixlen;
        unsigned char   nh_sel;
        unsigned char   type;
@@@ -264,8 -263,7 +264,8 @@@ struct fib_table 
  
  int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
                     struct fib_result *res, int fib_flags);
 -int fib_table_insert(struct net *, struct fib_table *, struct fib_config *);
 +int fib_table_insert(struct net *, struct fib_table *, struct fib_config *,
 +                   struct netlink_ext_ack *extack);
  int fib_table_delete(struct net *, struct fib_table *, struct fib_config *);
  int fib_table_dump(struct fib_table *table, struct sk_buff *skb,
                   struct netlink_callback *cb);
diff --combined net/core/rtnetlink.c
index dab2834f58f85df1afa9906e5638d339c8c688b5,9e2c0a7cb3256e8cb2af1d65aaf293db96b9a418..64953af4a3b1408bed71a2b28428ae3fb5ecff46
@@@ -2048,8 -2048,8 +2048,8 @@@ static int do_setlink(const struct sk_b
        }
  
        if (tb[IFLA_TXQLEN]) {
 -              unsigned long value = nla_get_u32(tb[IFLA_TXQLEN]);
 -              unsigned long orig_len = dev->tx_queue_len;
 +              unsigned int value = nla_get_u32(tb[IFLA_TXQLEN]);
 +              unsigned int orig_len = dev->tx_queue_len;
  
                if (dev->tx_queue_len ^ value) {
                        dev->tx_queue_len = value;
@@@ -3231,8 -3231,11 +3231,11 @@@ static int rtnl_fdb_dump(struct sk_buf
        int err = 0;
        int fidx = 0;
  
-       if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb,
-                       IFLA_MAX, ifla_policy, NULL) == 0) {
+       err = nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb,
+                         IFLA_MAX, ifla_policy, NULL);
+       if (err < 0) {
+               return -EINVAL;
+       } else if (err == 0) {
                if (tb[IFLA_MASTER])
                        br_idx = nla_get_u32(tb[IFLA_MASTER]);
        }
diff --combined net/ipv4/fib_semantics.c
index 4852e183afe0efd3fe2e71e9380ee365ae779b2a,ad9ad4aab5da7c7d11c3b80edbdfcbdd3d7153fe..de9484658232b9caeba86fb0e7c7ce5d551129de
@@@ -32,7 -32,6 +32,7 @@@
  #include <linux/skbuff.h>
  #include <linux/init.h>
  #include <linux/slab.h>
 +#include <linux/netlink.h>
  
  #include <net/arp.h>
  #include <net/ip.h>
@@@ -204,6 -203,7 +204,7 @@@ static void rt_fibinfo_free_cpus(struc
  static void free_fib_info_rcu(struct rcu_head *head)
  {
        struct fib_info *fi = container_of(head, struct fib_info, rcu);
+       struct dst_metrics *m;
  
        change_nexthops(fi) {
                if (nexthop_nh->nh_dev)
                rt_fibinfo_free(&nexthop_nh->nh_rth_input);
        } endfor_nexthops(fi);
  
-       if (fi->fib_metrics != (u32 *) dst_default_metrics)
-               kfree(fi->fib_metrics);
+       m = fi->fib_metrics;
+       if (m != &dst_default_metrics && atomic_dec_and_test(&m->refcnt))
+               kfree(m);
        kfree(fi);
  }
  
@@@ -455,8 -456,7 +457,8 @@@ static int fib_detect_death(struct fib_
  
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
  
 -static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining)
 +static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining,
 +                            struct netlink_ext_ack *extack)
  {
        int nhs = 0;
  
        }
  
        /* leftover implies invalid nexthop configuration, discard it */
 -      return remaining > 0 ? 0 : nhs;
 +      if (remaining > 0) {
 +              NL_SET_ERR_MSG(extack,
 +                             "Invalid nexthop configuration - extra data after nexthops");
 +              nhs = 0;
 +      }
 +
 +      return nhs;
  }
  
  static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
 -                     int remaining, struct fib_config *cfg)
 +                     int remaining, struct fib_config *cfg,
 +                     struct netlink_ext_ack *extack)
  {
        int ret;
  
        change_nexthops(fi) {
                int attrlen;
  
 -              if (!rtnh_ok(rtnh, remaining))
 +              if (!rtnh_ok(rtnh, remaining)) {
 +                      NL_SET_ERR_MSG(extack,
 +                                     "Invalid nexthop configuration - extra data after nexthop");
                        return -EINVAL;
 +              }
  
 -              if (rtnh->rtnh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
 +              if (rtnh->rtnh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) {
 +                      NL_SET_ERR_MSG(extack,
 +                                     "Invalid flags for nexthop - can not contain DEAD or LINKDOWN");
                        return -EINVAL;
 +              }
  
                nexthop_nh->nh_flags =
                        (cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags;
  
                                nla_entype = nla_find(attrs, attrlen,
                                                      RTA_ENCAP_TYPE);
 -                              if (!nla_entype)
 +                              if (!nla_entype) {
 +                                      NL_SET_BAD_ATTR(extack, nla);
 +                                      NL_SET_ERR_MSG(extack,
 +                                                     "Encap type is missing");
                                        goto err_inval;
 +                              }
  
                                ret = lwtunnel_build_state(nla_get_u16(
                                                           nla_entype),
@@@ -733,7 -716,7 +735,7 @@@ int fib_nh_match(struct fib_config *cfg
   *                                    |-> {local prefix} (terminal node)
   */
  static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
 -                      struct fib_nh *nh)
 +                      struct fib_nh *nh, struct netlink_ext_ack *extack)
  {
        int err = 0;
        struct net *net;
                if (nh->nh_flags & RTNH_F_ONLINK) {
                        unsigned int addr_type;
  
 -                      if (cfg->fc_scope >= RT_SCOPE_LINK)
 +                      if (cfg->fc_scope >= RT_SCOPE_LINK) {
 +                              NL_SET_ERR_MSG(extack,
 +                                             "Nexthop has invalid scope");
                                return -EINVAL;
 +                      }
                        dev = __dev_get_by_index(net, nh->nh_oif);
                        if (!dev)
                                return -ENODEV;
 -                      if (!(dev->flags & IFF_UP))
 +                      if (!(dev->flags & IFF_UP)) {
 +                              NL_SET_ERR_MSG(extack,
 +                                             "Nexthop device is not up");
                                return -ENETDOWN;
 +                      }
                        addr_type = inet_addr_type_dev_table(net, dev, nh->nh_gw);
 -                      if (addr_type != RTN_UNICAST)
 +                      if (addr_type != RTN_UNICAST) {
 +                              NL_SET_ERR_MSG(extack,
 +                                             "Nexthop has invalid gateway");
                                return -EINVAL;
 +                      }
                        if (!netif_carrier_ok(dev))
                                nh->nh_flags |= RTNH_F_LINKDOWN;
                        nh->nh_dev = dev;
                        }
  
                        if (err) {
 +                              NL_SET_ERR_MSG(extack,
 +                                             "Nexthop has invalid gateway");
                                rcu_read_unlock();
                                return err;
                        }
                }
                err = -EINVAL;
 -              if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
 +              if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) {
 +                      NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway");
                        goto out;
 +              }
                nh->nh_scope = res.scope;
                nh->nh_oif = FIB_RES_OIF(res);
                nh->nh_dev = dev = FIB_RES_DEV(res);
 -              if (!dev)
 +              if (!dev) {
 +                      NL_SET_ERR_MSG(extack,
 +                                     "No egress device for nexthop gateway");
                        goto out;
 +              }
                dev_hold(dev);
                if (!netif_carrier_ok(dev))
                        nh->nh_flags |= RTNH_F_LINKDOWN;
        } else {
                struct in_device *in_dev;
  
 -              if (nh->nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK))
 +              if (nh->nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK)) {
 +                      NL_SET_ERR_MSG(extack,
 +                                     "Invalid flags for nexthop - PERVASIVE and ONLINK can not be set");
                        return -EINVAL;
 -
 +              }
                rcu_read_lock();
                err = -ENODEV;
                in_dev = inetdev_by_index(net, nh->nh_oif);
                if (!in_dev)
                        goto out;
                err = -ENETDOWN;
 -              if (!(in_dev->dev->flags & IFF_UP))
 +              if (!(in_dev->dev->flags & IFF_UP)) {
 +                      NL_SET_ERR_MSG(extack, "Device for nexthop is not up");
                        goto out;
 +              }
                nh->nh_dev = in_dev->dev;
                dev_hold(nh->nh_dev);
                nh->nh_scope = RT_SCOPE_HOST;
@@@ -1010,17 -973,16 +1012,17 @@@ fib_convert_metrics(struct fib_info *fi
                        val = 255;
                if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK))
                        return -EINVAL;
-               fi->fib_metrics[type - 1] = val;
+               fi->fib_metrics->metrics[type - 1] = val;
        }
  
        if (ecn_ca)
-               fi->fib_metrics[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
+               fi->fib_metrics->metrics[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
  
        return 0;
  }
  
 -struct fib_info *fib_create_info(struct fib_config *cfg)
 +struct fib_info *fib_create_info(struct fib_config *cfg,
 +                               struct netlink_ext_ack *extack)
  {
        int err;
        struct fib_info *fi = NULL;
                goto err_inval;
  
        /* Fast check to catch the most weird cases */
 -      if (fib_props[cfg->fc_type].scope > cfg->fc_scope)
 +      if (fib_props[cfg->fc_type].scope > cfg->fc_scope) {
 +              NL_SET_ERR_MSG(extack, "Invalid scope");
                goto err_inval;
 +      }
  
 -      if (cfg->fc_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
 +      if (cfg->fc_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) {
 +              NL_SET_ERR_MSG(extack,
 +                             "Invalid rtm_flags - can not contain DEAD or LINKDOWN");
                goto err_inval;
 +      }
  
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
        if (cfg->fc_mp) {
 -              nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len);
 +              nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len, extack);
                if (nhs == 0)
                        goto err_inval;
        }
                goto failure;
        fib_info_cnt++;
        if (cfg->fc_mx) {
-               fi->fib_metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
+               fi->fib_metrics = kzalloc(sizeof(*fi->fib_metrics), GFP_KERNEL);
                if (!fi->fib_metrics)
                        goto failure;
+               atomic_set(&fi->fib_metrics->refcnt, 1);
        } else
-               fi->fib_metrics = (u32 *) dst_default_metrics;
+               fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics;
  
        fi->fib_net = net;
        fi->fib_protocol = cfg->fc_protocol;
  
        if (cfg->fc_mp) {
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
 -              err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg);
 +              err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg, extack);
                if (err != 0)
                        goto failure;
 -              if (cfg->fc_oif && fi->fib_nh->nh_oif != cfg->fc_oif)
 +              if (cfg->fc_oif && fi->fib_nh->nh_oif != cfg->fc_oif) {
 +                      NL_SET_ERR_MSG(extack,
 +                                     "Nexthop device index does not match RTA_OIF");
                        goto err_inval;
 -              if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw)
 +              }
 +              if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw) {
 +                      NL_SET_ERR_MSG(extack,
 +                                     "Nexthop gateway does not match RTA_GATEWAY");
                        goto err_inval;
 +              }
  #ifdef CONFIG_IP_ROUTE_CLASSID
 -              if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow)
 +              if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow) {
 +                      NL_SET_ERR_MSG(extack,
 +                                     "Nexthop class id does not match RTA_FLOW");
                        goto err_inval;
 +              }
  #endif
  #else
 +              NL_SET_ERR_MSG(extack,
 +                             "Multipath support not enabled in kernel");
                goto err_inval;
  #endif
        } else {
                if (cfg->fc_encap) {
                        struct lwtunnel_state *lwtstate;
  
 -                      if (cfg->fc_encap_type == LWTUNNEL_ENCAP_NONE)
 +                      if (cfg->fc_encap_type == LWTUNNEL_ENCAP_NONE) {
 +                              NL_SET_ERR_MSG(extack,
 +                                             "LWT encap type not specified");
                                goto err_inval;
 +                      }
                        err = lwtunnel_build_state(cfg->fc_encap_type,
                                                   cfg->fc_encap, AF_INET, cfg,
                                                   &lwtstate);
        }
  
        if (fib_props[cfg->fc_type].error) {
 -              if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp)
 +              if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) {
 +                      NL_SET_ERR_MSG(extack,
 +                                     "Gateway, device and multipath can not be specified for this route type");
                        goto err_inval;
 +              }
                goto link_it;
        } else {
                switch (cfg->fc_type) {
                case RTN_MULTICAST:
                        break;
                default:
 +                      NL_SET_ERR_MSG(extack, "Invalid route type");
                        goto err_inval;
                }
        }
  
 -      if (cfg->fc_scope > RT_SCOPE_HOST)
 +      if (cfg->fc_scope > RT_SCOPE_HOST) {
 +              NL_SET_ERR_MSG(extack, "Invalid scope");
                goto err_inval;
 +      }
  
        if (cfg->fc_scope == RT_SCOPE_HOST) {
                struct fib_nh *nh = fi->fib_nh;
  
                /* Local address is added. */
 -              if (nhs != 1 || nh->nh_gw)
 +              if (nhs != 1) {
 +                      NL_SET_ERR_MSG(extack,
 +                                     "Route with host scope can not have multiple nexthops");
 +                      goto err_inval;
 +              }
 +              if (nh->nh_gw) {
 +                      NL_SET_ERR_MSG(extack,
 +                                     "Route with host scope can not have a gateway");
                        goto err_inval;
 +              }
                nh->nh_scope = RT_SCOPE_NOWHERE;
                nh->nh_dev = dev_get_by_index(net, fi->fib_nh->nh_oif);
                err = -ENODEV;
                int linkdown = 0;
  
                change_nexthops(fi) {
 -                      err = fib_check_nh(cfg, fi, nexthop_nh);
 +                      err = fib_check_nh(cfg, fi, nexthop_nh, extack);
                        if (err != 0)
                                goto failure;
                        if (nexthop_nh->nh_flags & RTNH_F_LINKDOWN)
                        fi->fib_flags |= RTNH_F_LINKDOWN;
        }
  
 -      if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc))
 +      if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) {
 +              NL_SET_ERR_MSG(extack, "Invalid prefsrc address");
                goto err_inval;
 +      }
  
        change_nexthops(fi) {
                fib_info_update_nh_saddr(net, nexthop_nh);
@@@ -1313,7 -1241,7 +1316,7 @@@ int fib_dump_info(struct sk_buff *skb, 
        if (fi->fib_priority &&
            nla_put_u32(skb, RTA_PRIORITY, fi->fib_priority))
                goto nla_put_failure;
-       if (rtnetlink_put_metrics(skb, fi->fib_metrics) < 0)
+       if (rtnetlink_put_metrics(skb, fi->fib_metrics->metrics) < 0)
                goto nla_put_failure;
  
        if (fi->fib_prefsrc &&
diff --combined net/ipv4/route.c
index 3a7425694d8bb99db2f4ce27e06f6f0324adcdba,6883b3d4ba8f69de2cb924612d60f5671a219a84..f1f2e5aaa2d6de48130572c1ec38f16d4b632bc0
  #include <net/ip_tunnels.h>
  #include <net/l3mdev.h>
  
 +#include "fib_lookup.h"
 +
  #define RT_FL_TOS(oldflp4) \
        ((oldflp4)->flowi4_tos & (IPTOS_RT_MASK | RTO_ONLINK))
  
@@@ -1387,8 -1385,12 +1387,12 @@@ static void rt_add_uncached_list(struc
  
  static void ipv4_dst_destroy(struct dst_entry *dst)
  {
+       struct dst_metrics *p = (struct dst_metrics *)DST_METRICS_PTR(dst);
        struct rtable *rt = (struct rtable *) dst;
  
+       if (p != &dst_default_metrics && atomic_dec_and_test(&p->refcnt))
+               kfree(p);
        if (!list_empty(&rt->rt_uncached)) {
                struct uncached_list *ul = rt->rt_uncached_list;
  
@@@ -1440,7 -1442,11 +1444,11 @@@ static void rt_set_nexthop(struct rtabl
                        rt->rt_gateway = nh->nh_gw;
                        rt->rt_uses_gateway = 1;
                }
-               dst_init_metrics(&rt->dst, fi->fib_metrics, true);
+               dst_init_metrics(&rt->dst, fi->fib_metrics->metrics, true);
+               if (fi->fib_metrics != &dst_default_metrics) {
+                       rt->dst._metrics |= DST_METRICS_REFCOUNTED;
+                       atomic_inc(&fi->fib_metrics->refcnt);
+               }
  #ifdef CONFIG_IP_ROUTE_CLASSID
                rt->dst.tclassid = nh->nh_tclassid;
  #endif
@@@ -1854,9 -1860,9 +1862,9 @@@ static int ip_mkroute_input(struct sk_b
   */
  
  static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
 -                             u8 tos, struct net_device *dev)
 +                             u8 tos, struct net_device *dev,
 +                             struct fib_result *res)
  {
 -      struct fib_result res;
        struct in_device *in_dev = __in_dev_get_rcu(dev);
        struct ip_tunnel_info *tun_info;
        struct flowi4   fl4;
        if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr))
                goto martian_source;
  
 -      res.fi = NULL;
 -      res.table = NULL;
 +      res->fi = NULL;
 +      res->table = NULL;
        if (ipv4_is_lbcast(daddr) || (saddr == 0 && daddr == 0))
                goto brd_input;
  
        fl4.daddr = daddr;
        fl4.saddr = saddr;
        fl4.flowi4_uid = sock_net_uid(net, NULL);
 -      err = fib_lookup(net, &fl4, &res, 0);
 +      err = fib_lookup(net, &fl4, res, 0);
        if (err != 0) {
                if (!IN_DEV_FORWARD(in_dev))
                        err = -EHOSTUNREACH;
                goto no_route;
        }
  
 -      if (res.type == RTN_BROADCAST)
 +      if (res->type == RTN_BROADCAST)
                goto brd_input;
  
 -      if (res.type == RTN_LOCAL) {
 +      if (res->type == RTN_LOCAL) {
                err = fib_validate_source(skb, saddr, daddr, tos,
                                          0, dev, in_dev, &itag);
                if (err < 0)
                err = -EHOSTUNREACH;
                goto no_route;
        }
 -      if (res.type != RTN_UNICAST)
 +      if (res->type != RTN_UNICAST)
                goto martian_destination;
  
 -      err = ip_mkroute_input(skb, &res, in_dev, daddr, saddr, tos);
 +      err = ip_mkroute_input(skb, res, in_dev, daddr, saddr, tos);
  out:  return err;
  
  brd_input:
                        goto martian_source;
        }
        flags |= RTCF_BROADCAST;
 -      res.type = RTN_BROADCAST;
 +      res->type = RTN_BROADCAST;
        RT_CACHE_STAT_INC(in_brd);
  
  local_input:
        do_cache = false;
 -      if (res.fi) {
 +      if (res->fi) {
                if (!itag) {
 -                      rth = rcu_dereference(FIB_RES_NH(res).nh_rth_input);
 +                      rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input);
                        if (rt_cache_valid(rth)) {
                                skb_dst_set_noref(skb, &rth->dst);
                                err = 0;
        }
  
        rth = rt_dst_alloc(l3mdev_master_dev_rcu(dev) ? : net->loopback_dev,
 -                         flags | RTCF_LOCAL, res.type,
 +                         flags | RTCF_LOCAL, res->type,
                           IN_DEV_CONF_GET(in_dev, NOPOLICY), false, do_cache);
        if (!rth)
                goto e_nobufs;
        rth->dst.tclassid = itag;
  #endif
        rth->rt_is_input = 1;
 -      if (res.table)
 -              rth->rt_table_id = res.table->tb_id;
 +      if (res->table)
 +              rth->rt_table_id = res->table->tb_id;
  
        RT_CACHE_STAT_INC(in_slow_tot);
 -      if (res.type == RTN_UNREACHABLE) {
 +      if (res->type == RTN_UNREACHABLE) {
                rth->dst.input= ip_error;
                rth->dst.error= -err;
                rth->rt_flags   &= ~RTCF_LOCAL;
        }
  
        if (do_cache) {
 -              struct fib_nh *nh = &FIB_RES_NH(res);
 +              struct fib_nh *nh = &FIB_RES_NH(*res);
  
                rth->dst.lwtstate = lwtstate_get(nh->nh_lwtstate);
                if (lwtunnel_input_redirect(rth->dst.lwtstate)) {
  
  no_route:
        RT_CACHE_STAT_INC(in_no_route);
 -      res.type = RTN_UNREACHABLE;
 -      res.fi = NULL;
 -      res.table = NULL;
 +      res->type = RTN_UNREACHABLE;
 +      res->fi = NULL;
 +      res->table = NULL;
        goto local_input;
  
        /*
@@@ -2053,22 -2059,11 +2061,22 @@@ martian_source
  int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr,
                         u8 tos, struct net_device *dev)
  {
 -      int res;
 +      struct fib_result res;
 +      int err;
  
        tos &= IPTOS_RT_MASK;
        rcu_read_lock();
 +      err = ip_route_input_rcu(skb, daddr, saddr, tos, dev, &res);
 +      rcu_read_unlock();
  
 +      return err;
 +}
 +EXPORT_SYMBOL(ip_route_input_noref);
 +
 +/* called with rcu_read_lock held */
 +int ip_route_input_rcu(struct sk_buff *skb, __be32 daddr, __be32 saddr,
 +                     u8 tos, struct net_device *dev, struct fib_result *res)
 +{
        /* Multicast recognition logic is moved from route cache to here.
           The problem was that too many Ethernet cards have broken/missing
           hardware multicast filters :-( As result the host on multicasting
        if (ipv4_is_multicast(daddr)) {
                struct in_device *in_dev = __in_dev_get_rcu(dev);
                int our = 0;
 +              int err = -EINVAL;
  
                if (in_dev)
                        our = ip_check_mc_rcu(in_dev, daddr, saddr,
                                                      ip_hdr(skb)->protocol);
                }
  
 -              res = -EINVAL;
                if (our
  #ifdef CONFIG_IP_MROUTE
                        ||
                     IN_DEV_MFORWARD(in_dev))
  #endif
                   ) {
 -                      res = ip_route_input_mc(skb, daddr, saddr,
 +                      err = ip_route_input_mc(skb, daddr, saddr,
                                                tos, dev, our);
                }
 -              rcu_read_unlock();
 -              return res;
 +              return err;
        }
 -      res = ip_route_input_slow(skb, daddr, saddr, tos, dev);
 -      rcu_read_unlock();
 -      return res;
 +
 +      return ip_route_input_slow(skb, daddr, saddr, tos, dev, res);
  }
 -EXPORT_SYMBOL(ip_route_input_noref);
  
  /* called with rcu_read_lock() */
  static struct rtable *__mkroute_output(const struct fib_result *res,
   * Major route resolver routine.
   */
  
 -struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4,
 -                                        const struct sk_buff *skb)
 +struct rtable *ip_route_output_key_hash(struct net *net, struct flowi4 *fl4,
 +                                      const struct sk_buff *skb)
  {
 -      struct net_device *dev_out = NULL;
        __u8 tos = RT_FL_TOS(fl4);
 -      unsigned int flags = 0;
        struct fib_result res;
        struct rtable *rth;
 -      int orig_oif;
 -      int err = -ENETUNREACH;
  
        res.tclassid    = 0;
        res.fi          = NULL;
        res.table       = NULL;
  
 -      orig_oif = fl4->flowi4_oif;
 -
        fl4->flowi4_iif = LOOPBACK_IFINDEX;
        fl4->flowi4_tos = tos & IPTOS_RT_MASK;
        fl4->flowi4_scope = ((tos & RTO_ONLINK) ?
                         RT_SCOPE_LINK : RT_SCOPE_UNIVERSE);
  
        rcu_read_lock();
 +      rth = ip_route_output_key_hash_rcu(net, fl4, &res, skb);
 +      rcu_read_unlock();
 +
 +      return rth;
 +}
 +EXPORT_SYMBOL_GPL(ip_route_output_key_hash);
 +
 +struct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *fl4,
 +                                          struct fib_result *res,
 +                                          const struct sk_buff *skb)
 +{
 +      struct net_device *dev_out = NULL;
 +      int orig_oif = fl4->flowi4_oif;
 +      unsigned int flags = 0;
 +      struct rtable *rth;
 +      int err = -ENETUNREACH;
 +
        if (fl4->saddr) {
                rth = ERR_PTR(-EINVAL);
                if (ipv4_is_multicast(fl4->saddr) ||
                        fl4->daddr = fl4->saddr = htonl(INADDR_LOOPBACK);
                dev_out = net->loopback_dev;
                fl4->flowi4_oif = LOOPBACK_IFINDEX;
 -              res.type = RTN_LOCAL;
 +              res->type = RTN_LOCAL;
                flags |= RTCF_LOCAL;
                goto make_route;
        }
  
 -      err = fib_lookup(net, fl4, &res, 0);
 +      err = fib_lookup(net, fl4, res, 0);
        if (err) {
 -              res.fi = NULL;
 -              res.table = NULL;
 +              res->fi = NULL;
 +              res->table = NULL;
                if (fl4->flowi4_oif &&
                    (ipv4_is_multicast(fl4->daddr) ||
                    !netif_index_is_l3_master(net, fl4->flowi4_oif))) {
                        if (fl4->saddr == 0)
                                fl4->saddr = inet_select_addr(dev_out, 0,
                                                              RT_SCOPE_LINK);
 -                      res.type = RTN_UNICAST;
 +                      res->type = RTN_UNICAST;
                        goto make_route;
                }
                rth = ERR_PTR(err);
                goto out;
        }
  
 -      if (res.type == RTN_LOCAL) {
 +      if (res->type == RTN_LOCAL) {
                if (!fl4->saddr) {
 -                      if (res.fi->fib_prefsrc)
 -                              fl4->saddr = res.fi->fib_prefsrc;
 +                      if (res->fi->fib_prefsrc)
 +                              fl4->saddr = res->fi->fib_prefsrc;
                        else
                                fl4->saddr = fl4->daddr;
                }
  
                /* L3 master device is the loopback for that domain */
 -              dev_out = l3mdev_master_dev_rcu(FIB_RES_DEV(res)) ? :
 +              dev_out = l3mdev_master_dev_rcu(FIB_RES_DEV(*res)) ? :
                        net->loopback_dev;
                fl4->flowi4_oif = dev_out->ifindex;
                flags |= RTCF_LOCAL;
                goto make_route;
        }
  
 -      fib_select_path(net, &res, fl4, skb);
 +      fib_select_path(net, res, fl4, skb);
  
 -      dev_out = FIB_RES_DEV(res);
 +      dev_out = FIB_RES_DEV(*res);
        fl4->flowi4_oif = dev_out->ifindex;
  
  
  make_route:
 -      rth = __mkroute_output(&res, fl4, orig_oif, dev_out, flags);
 +      rth = __mkroute_output(res, fl4, orig_oif, dev_out, flags);
  
  out:
 -      rcu_read_unlock();
        return rth;
  }
 -EXPORT_SYMBOL_GPL(__ip_route_output_key_hash);
  
  static struct dst_entry *ipv4_blackhole_dst_check(struct dst_entry *dst, u32 cookie)
  {
@@@ -2536,18 -2525,18 +2544,18 @@@ struct rtable *ip_route_output_flow(str
  }
  EXPORT_SYMBOL_GPL(ip_route_output_flow);
  
 +/* called with rcu_read_lock held */
  static int rt_fill_info(struct net *net,  __be32 dst, __be32 src, u32 table_id,
                        struct flowi4 *fl4, struct sk_buff *skb, u32 portid,
 -                      u32 seq, int event)
 +                      u32 seq, struct rtable *rt)
  {
 -      struct rtable *rt = skb_rtable(skb);
        struct rtmsg *r;
        struct nlmsghdr *nlh;
        unsigned long expires = 0;
        u32 error;
        u32 metrics[RTAX_MAX];
  
 -      nlh = nlmsg_put(skb, portid, seq, event, sizeof(*r), 0);
 +      nlh = nlmsg_put(skb, portid, seq, RTM_NEWROUTE, sizeof(*r), 0);
        if (!nlh)
                return -EMSGSIZE;
  
@@@ -2655,7 -2644,6 +2663,7 @@@ static int inet_rtm_getroute(struct sk_
        struct net *net = sock_net(in_skb->sk);
        struct rtmsg *rtm;
        struct nlattr *tb[RTA_MAX+1];
 +      struct fib_result res = {};
        struct rtable *rt = NULL;
        struct flowi4 fl4;
        __be32 dst = 0;
        fl4.flowi4_mark = mark;
        fl4.flowi4_uid = uid;
  
 +      rcu_read_lock();
 +
        if (iif) {
                struct net_device *dev;
  
 -              dev = __dev_get_by_index(net, iif);
 +              dev = dev_get_by_index_rcu(net, iif);
                if (!dev) {
                        err = -ENODEV;
                        goto errout_free;
                skb->protocol   = htons(ETH_P_IP);
                skb->dev        = dev;
                skb->mark       = mark;
 -              err = ip_route_input(skb, dst, src, rtm->rtm_tos, dev);
 +              err = ip_route_input_rcu(skb, dst, src, rtm->rtm_tos,
 +                                       dev, &res);
  
                rt = skb_rtable(skb);
                if (err == 0 && rt->dst.error)
                        err = -rt->dst.error;
        } else {
 -              rt = ip_route_output_key(net, &fl4);
 -
 +              rt = ip_route_output_key_hash_rcu(net, &fl4, &res, skb);
                err = 0;
                if (IS_ERR(rt))
                        err = PTR_ERR(rt);
        if (err)
                goto errout_free;
  
 -      skb_dst_set(skb, &rt->dst);
        if (rtm->rtm_flags & RTM_F_NOTIFY)
                rt->rt_flags |= RTCF_NOTIFY;
  
        if (rtm->rtm_flags & RTM_F_LOOKUP_TABLE)
                table_id = rt->rt_table_id;
  
 -      err = rt_fill_info(net, dst, src, table_id, &fl4, skb,
 -                         NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
 -                         RTM_NEWROUTE);
 +      if (rtm->rtm_flags & RTM_F_FIB_MATCH)
 +              err = fib_dump_info(skb, NETLINK_CB(in_skb).portid,
 +                                  nlh->nlmsg_seq, RTM_NEWROUTE, table_id,
 +                                  rt->rt_type, res.prefix, res.prefixlen,
 +                                  fl4.flowi4_tos, res.fi, 0);
 +      else
 +              err = rt_fill_info(net, dst, src, table_id, &fl4, skb,
 +                                 NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
 +                                 rt);
        if (err < 0)
                goto errout_free;
  
 +      rcu_read_unlock();
 +
        err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
  errout:
        return err;
  
  errout_free:
 +      rcu_read_unlock();
        kfree_skb(skb);
        goto errout;
  }
diff --combined net/ipv4/tcp.c
index aaf663a1e571b565d72738ac6c306aaabd696abd,59792d283ff8c19048904cb790dbeebef14da73d..f7be94fc8431d30a6b8c5de62a70b529789f7873
@@@ -386,7 -386,7 +386,7 @@@ void tcp_init_sock(struct sock *sk
  
        icsk->icsk_rto = TCP_TIMEOUT_INIT;
        tp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT);
 -      minmax_reset(&tp->rtt_min, tcp_time_stamp, ~0U);
 +      minmax_reset(&tp->rtt_min, tcp_jiffies32, ~0U);
  
        /* So many TCP implementations out there (incorrectly) count the
         * initial SYN frame in their delayed-ACK and congestion control
@@@ -1084,9 -1084,12 +1084,12 @@@ static int tcp_sendmsg_fastopen(struct 
  {
        struct tcp_sock *tp = tcp_sk(sk);
        struct inet_sock *inet = inet_sk(sk);
+       struct sockaddr *uaddr = msg->msg_name;
        int err, flags;
  
-       if (!(sysctl_tcp_fastopen & TFO_CLIENT_ENABLE))
+       if (!(sysctl_tcp_fastopen & TFO_CLIENT_ENABLE) ||
+           (uaddr && msg->msg_namelen >= sizeof(uaddr->sa_family) &&
+            uaddr->sa_family == AF_UNSPEC))
                return -EOPNOTSUPP;
        if (tp->fastopen_req)
                return -EALREADY; /* Another Fast Open is in progress */
                }
        }
        flags = (msg->msg_flags & MSG_DONTWAIT) ? O_NONBLOCK : 0;
-       err = __inet_stream_connect(sk->sk_socket, msg->msg_name,
+       err = __inet_stream_connect(sk->sk_socket, uaddr,
                                    msg->msg_namelen, flags, 1);
        /* fastopen_req could already be freed in __inet_stream_connect
         * if the connection times out or gets rst
@@@ -2183,7 -2186,7 +2186,7 @@@ adjudge_to_death
  
  
        /* Now socket is owned by kernel and we acquire BH lock
 -         to finish close. No need to check for user refs.
 +       *  to finish close. No need to check for user refs.
         */
        local_bh_disable();
        bh_lock_sock(sk);
@@@ -2475,8 -2478,7 +2478,8 @@@ static int do_tcp_setsockopt(struct soc
        case TCP_MAXSEG:
                /* Values greater than interface MTU won't take effect. However
                 * at the point when this call is done we typically don't yet
 -               * know which interface is going to be used */
 +               * know which interface is going to be used
 +               */
                if (val && (val < TCP_MIN_MSS || val > MAX_TCP_WINDOW)) {
                        err = -EINVAL;
                        break;
                if (!tp->repair)
                        err = -EPERM;
                else
 -                      tp->tsoffset = val - tcp_time_stamp;
 +                      tp->tsoffset = val - tcp_time_stamp_raw();
                break;
        case TCP_REPAIR_WINDOW:
                err = tcp_repair_set_window(tp, optval, optlen);
@@@ -2762,7 -2764,7 +2765,7 @@@ static void tcp_get_info_chrono_stats(c
        for (i = TCP_CHRONO_BUSY; i < __TCP_CHRONO_MAX; ++i) {
                stats[i] = tp->chrono_stat[i - 1];
                if (i == tp->chrono_type)
 -                      stats[i] += tcp_time_stamp - tp->chrono_start;
 +                      stats[i] += tcp_jiffies32 - tp->chrono_start;
                stats[i] *= USEC_PER_SEC / HZ;
                total += stats[i];
        }
@@@ -2846,7 -2848,7 +2849,7 @@@ void tcp_get_info(struct sock *sk, stru
        info->tcpi_retrans = tp->retrans_out;
        info->tcpi_fackets = tp->fackets_out;
  
 -      now = tcp_time_stamp;
 +      now = tcp_jiffies32;
        info->tcpi_last_data_sent = jiffies_to_msecs(now - tp->lsndtime);
        info->tcpi_last_data_recv = jiffies_to_msecs(now - icsk->icsk_ack.lrcvtime);
        info->tcpi_last_ack_recv = jiffies_to_msecs(now - tp->rcv_tstamp);
@@@ -3077,7 -3079,7 +3080,7 @@@ static int do_tcp_getsockopt(struct soc
                break;
  
        case TCP_TIMESTAMP:
 -              val = tcp_time_stamp + tp->tsoffset;
 +              val = tcp_time_stamp_raw() + tp->tsoffset;
                break;
        case TCP_NOTSENT_LOWAT:
                val = tp->notsent_lowat;