root/net/ncsi/ncsi-aen.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ncsi_validate_aen_pkt
  2. ncsi_aen_handler_lsc
  3. ncsi_aen_handler_cr
  4. ncsi_aen_handler_hncdsc
  5. ncsi_aen_handler

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright Gavin Shan, IBM Corporation 2016.
   4  */
   5 
   6 #include <linux/module.h>
   7 #include <linux/kernel.h>
   8 #include <linux/init.h>
   9 #include <linux/netdevice.h>
  10 #include <linux/skbuff.h>
  11 
  12 #include <net/ncsi.h>
  13 #include <net/net_namespace.h>
  14 #include <net/sock.h>
  15 
  16 #include "internal.h"
  17 #include "ncsi-pkt.h"
  18 
  19 static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
  20                                  const unsigned short payload)
  21 {
  22         u32 checksum;
  23         __be32 *pchecksum;
  24 
  25         if (h->common.revision != NCSI_PKT_REVISION)
  26                 return -EINVAL;
  27         if (ntohs(h->common.length) != payload)
  28                 return -EINVAL;
  29 
  30         /* Validate checksum, which might be zeroes if the
  31          * sender doesn't support checksum according to NCSI
  32          * specification.
  33          */
  34         pchecksum = (__be32 *)((void *)(h + 1) + payload - 4);
  35         if (ntohl(*pchecksum) == 0)
  36                 return 0;
  37 
  38         checksum = ncsi_calculate_checksum((unsigned char *)h,
  39                                            sizeof(*h) + payload - 4);
  40         if (*pchecksum != htonl(checksum))
  41                 return -EINVAL;
  42 
  43         return 0;
  44 }
  45 
  46 static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
  47                                 struct ncsi_aen_pkt_hdr *h)
  48 {
  49         struct ncsi_channel *nc, *tmp;
  50         struct ncsi_channel_mode *ncm;
  51         unsigned long old_data, data;
  52         struct ncsi_aen_lsc_pkt *lsc;
  53         struct ncsi_package *np;
  54         bool had_link, has_link;
  55         unsigned long flags;
  56         bool chained;
  57         int state;
  58 
  59         /* Find the NCSI channel */
  60         ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
  61         if (!nc)
  62                 return -ENODEV;
  63 
  64         /* Update the link status */
  65         lsc = (struct ncsi_aen_lsc_pkt *)h;
  66 
  67         spin_lock_irqsave(&nc->lock, flags);
  68         ncm = &nc->modes[NCSI_MODE_LINK];
  69         old_data = ncm->data[2];
  70         data = ntohl(lsc->status);
  71         ncm->data[2] = data;
  72         ncm->data[4] = ntohl(lsc->oem_status);
  73 
  74         had_link = !!(old_data & 0x1);
  75         has_link = !!(data & 0x1);
  76 
  77         netdev_dbg(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n",
  78                    nc->id, data & 0x1 ? "up" : "down");
  79 
  80         chained = !list_empty(&nc->link);
  81         state = nc->state;
  82         spin_unlock_irqrestore(&nc->lock, flags);
  83 
  84         if (state == NCSI_CHANNEL_INACTIVE)
  85                 netdev_warn(ndp->ndev.dev,
  86                             "NCSI: Inactive channel %u received AEN!\n",
  87                             nc->id);
  88 
  89         if ((had_link == has_link) || chained)
  90                 return 0;
  91 
  92         if (!ndp->multi_package && !nc->package->multi_channel) {
  93                 if (had_link) {
  94                         ndp->flags |= NCSI_DEV_RESHUFFLE;
  95                         ncsi_stop_channel_monitor(nc);
  96                         spin_lock_irqsave(&ndp->lock, flags);
  97                         list_add_tail_rcu(&nc->link, &ndp->channel_queue);
  98                         spin_unlock_irqrestore(&ndp->lock, flags);
  99                         return ncsi_process_next_channel(ndp);
 100                 }
 101                 /* Configured channel came up */
 102                 return 0;
 103         }
 104 
 105         if (had_link) {
 106                 ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
 107                 if (ncsi_channel_is_last(ndp, nc)) {
 108                         /* No channels left, reconfigure */
 109                         return ncsi_reset_dev(&ndp->ndev);
 110                 } else if (ncm->enable) {
 111                         /* Need to failover Tx channel */
 112                         ncsi_update_tx_channel(ndp, nc->package, nc, NULL);
 113                 }
 114         } else if (has_link && nc->package->preferred_channel == nc) {
 115                 /* Return Tx to preferred channel */
 116                 ncsi_update_tx_channel(ndp, nc->package, NULL, nc);
 117         } else if (has_link) {
 118                 NCSI_FOR_EACH_PACKAGE(ndp, np) {
 119                         NCSI_FOR_EACH_CHANNEL(np, tmp) {
 120                                 /* Enable Tx on this channel if the current Tx
 121                                  * channel is down.
 122                                  */
 123                                 ncm = &tmp->modes[NCSI_MODE_TX_ENABLE];
 124                                 if (ncm->enable &&
 125                                     !ncsi_channel_has_link(tmp)) {
 126                                         ncsi_update_tx_channel(ndp, nc->package,
 127                                                                tmp, nc);
 128                                         break;
 129                                 }
 130                         }
 131                 }
 132         }
 133 
 134         /* Leave configured channels active in a multi-channel scenario so
 135          * AEN events are still received.
 136          */
 137         return 0;
 138 }
 139 
 140 static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
 141                                struct ncsi_aen_pkt_hdr *h)
 142 {
 143         struct ncsi_channel *nc;
 144         unsigned long flags;
 145 
 146         /* Find the NCSI channel */
 147         ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
 148         if (!nc)
 149                 return -ENODEV;
 150 
 151         spin_lock_irqsave(&nc->lock, flags);
 152         if (!list_empty(&nc->link) ||
 153             nc->state != NCSI_CHANNEL_ACTIVE) {
 154                 spin_unlock_irqrestore(&nc->lock, flags);
 155                 return 0;
 156         }
 157         spin_unlock_irqrestore(&nc->lock, flags);
 158 
 159         ncsi_stop_channel_monitor(nc);
 160         spin_lock_irqsave(&nc->lock, flags);
 161         nc->state = NCSI_CHANNEL_INVISIBLE;
 162         spin_unlock_irqrestore(&nc->lock, flags);
 163 
 164         spin_lock_irqsave(&ndp->lock, flags);
 165         nc->state = NCSI_CHANNEL_INACTIVE;
 166         list_add_tail_rcu(&nc->link, &ndp->channel_queue);
 167         spin_unlock_irqrestore(&ndp->lock, flags);
 168 
 169         return ncsi_process_next_channel(ndp);
 170 }
 171 
 172 static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
 173                                    struct ncsi_aen_pkt_hdr *h)
 174 {
 175         struct ncsi_channel *nc;
 176         struct ncsi_channel_mode *ncm;
 177         struct ncsi_aen_hncdsc_pkt *hncdsc;
 178         unsigned long flags;
 179 
 180         /* Find the NCSI channel */
 181         ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
 182         if (!nc)
 183                 return -ENODEV;
 184 
 185         spin_lock_irqsave(&nc->lock, flags);
 186         ncm = &nc->modes[NCSI_MODE_LINK];
 187         hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
 188         ncm->data[3] = ntohl(hncdsc->status);
 189         spin_unlock_irqrestore(&nc->lock, flags);
 190         netdev_dbg(ndp->ndev.dev,
 191                    "NCSI: host driver %srunning on channel %u\n",
 192                    ncm->data[3] & 0x1 ? "" : "not ", nc->id);
 193 
 194         return 0;
 195 }
 196 
 197 static struct ncsi_aen_handler {
 198         unsigned char type;
 199         int           payload;
 200         int           (*handler)(struct ncsi_dev_priv *ndp,
 201                                  struct ncsi_aen_pkt_hdr *h);
 202 } ncsi_aen_handlers[] = {
 203         { NCSI_PKT_AEN_LSC,    12, ncsi_aen_handler_lsc    },
 204         { NCSI_PKT_AEN_CR,      4, ncsi_aen_handler_cr     },
 205         { NCSI_PKT_AEN_HNCDSC,  8, ncsi_aen_handler_hncdsc }
 206 };
 207 
 208 int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb)
 209 {
 210         struct ncsi_aen_pkt_hdr *h;
 211         struct ncsi_aen_handler *nah = NULL;
 212         int i, ret;
 213 
 214         /* Find the handler */
 215         h = (struct ncsi_aen_pkt_hdr *)skb_network_header(skb);
 216         for (i = 0; i < ARRAY_SIZE(ncsi_aen_handlers); i++) {
 217                 if (ncsi_aen_handlers[i].type == h->type) {
 218                         nah = &ncsi_aen_handlers[i];
 219                         break;
 220                 }
 221         }
 222 
 223         if (!nah) {
 224                 netdev_warn(ndp->ndev.dev, "Invalid AEN (0x%x) received\n",
 225                             h->type);
 226                 return -ENOENT;
 227         }
 228 
 229         ret = ncsi_validate_aen_pkt(h, nah->payload);
 230         if (ret) {
 231                 netdev_warn(ndp->ndev.dev,
 232                             "NCSI: 'bad' packet ignored for AEN type 0x%x\n",
 233                             h->type);
 234                 goto out;
 235         }
 236 
 237         ret = nah->handler(ndp, h);
 238         if (ret)
 239                 netdev_err(ndp->ndev.dev,
 240                            "NCSI: Handler for AEN type 0x%x returned %d\n",
 241                            h->type, ret);
 242 out:
 243         consume_skb(skb);
 244         return ret;
 245 }

/* [<][>][^][v][top][bottom][index][help] */