Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright Gavin Shan, IBM Corporation 2016.
0004  */
0005 
0006 #include <linux/module.h>
0007 #include <linux/kernel.h>
0008 #include <linux/init.h>
0009 #include <linux/netdevice.h>
0010 #include <linux/skbuff.h>
0011 
0012 #include <net/ncsi.h>
0013 #include <net/net_namespace.h>
0014 #include <net/sock.h>
0015 
0016 #include "internal.h"
0017 #include "ncsi-pkt.h"
0018 
0019 static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
0020                  const unsigned short payload)
0021 {
0022     u32 checksum;
0023     __be32 *pchecksum;
0024 
0025     if (h->common.revision != NCSI_PKT_REVISION)
0026         return -EINVAL;
0027     if (ntohs(h->common.length) != payload)
0028         return -EINVAL;
0029 
0030     /* Validate checksum, which might be zeroes if the
0031      * sender doesn't support checksum according to NCSI
0032      * specification.
0033      */
0034     pchecksum = (__be32 *)((void *)(h + 1) + payload - 4);
0035     if (ntohl(*pchecksum) == 0)
0036         return 0;
0037 
0038     checksum = ncsi_calculate_checksum((unsigned char *)h,
0039                        sizeof(*h) + payload - 4);
0040     if (*pchecksum != htonl(checksum))
0041         return -EINVAL;
0042 
0043     return 0;
0044 }
0045 
0046 static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
0047                 struct ncsi_aen_pkt_hdr *h)
0048 {
0049     struct ncsi_channel *nc, *tmp;
0050     struct ncsi_channel_mode *ncm;
0051     unsigned long old_data, data;
0052     struct ncsi_aen_lsc_pkt *lsc;
0053     struct ncsi_package *np;
0054     bool had_link, has_link;
0055     unsigned long flags;
0056     bool chained;
0057     int state;
0058 
0059     /* Find the NCSI channel */
0060     ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
0061     if (!nc)
0062         return -ENODEV;
0063 
0064     /* Update the link status */
0065     lsc = (struct ncsi_aen_lsc_pkt *)h;
0066 
0067     spin_lock_irqsave(&nc->lock, flags);
0068     ncm = &nc->modes[NCSI_MODE_LINK];
0069     old_data = ncm->data[2];
0070     data = ntohl(lsc->status);
0071     ncm->data[2] = data;
0072     ncm->data[4] = ntohl(lsc->oem_status);
0073 
0074     had_link = !!(old_data & 0x1);
0075     has_link = !!(data & 0x1);
0076 
0077     netdev_dbg(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n",
0078            nc->id, data & 0x1 ? "up" : "down");
0079 
0080     chained = !list_empty(&nc->link);
0081     state = nc->state;
0082     spin_unlock_irqrestore(&nc->lock, flags);
0083 
0084     if (state == NCSI_CHANNEL_INACTIVE)
0085         netdev_warn(ndp->ndev.dev,
0086                 "NCSI: Inactive channel %u received AEN!\n",
0087                 nc->id);
0088 
0089     if ((had_link == has_link) || chained)
0090         return 0;
0091 
0092     if (!ndp->multi_package && !nc->package->multi_channel) {
0093         if (had_link) {
0094             ndp->flags |= NCSI_DEV_RESHUFFLE;
0095             ncsi_stop_channel_monitor(nc);
0096             spin_lock_irqsave(&ndp->lock, flags);
0097             list_add_tail_rcu(&nc->link, &ndp->channel_queue);
0098             spin_unlock_irqrestore(&ndp->lock, flags);
0099             return ncsi_process_next_channel(ndp);
0100         }
0101         /* Configured channel came up */
0102         return 0;
0103     }
0104 
0105     if (had_link) {
0106         ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
0107         if (ncsi_channel_is_last(ndp, nc)) {
0108             /* No channels left, reconfigure */
0109             return ncsi_reset_dev(&ndp->ndev);
0110         } else if (ncm->enable) {
0111             /* Need to failover Tx channel */
0112             ncsi_update_tx_channel(ndp, nc->package, nc, NULL);
0113         }
0114     } else if (has_link && nc->package->preferred_channel == nc) {
0115         /* Return Tx to preferred channel */
0116         ncsi_update_tx_channel(ndp, nc->package, NULL, nc);
0117     } else if (has_link) {
0118         NCSI_FOR_EACH_PACKAGE(ndp, np) {
0119             NCSI_FOR_EACH_CHANNEL(np, tmp) {
0120                 /* Enable Tx on this channel if the current Tx
0121                  * channel is down.
0122                  */
0123                 ncm = &tmp->modes[NCSI_MODE_TX_ENABLE];
0124                 if (ncm->enable &&
0125                     !ncsi_channel_has_link(tmp)) {
0126                     ncsi_update_tx_channel(ndp, nc->package,
0127                                    tmp, nc);
0128                     break;
0129                 }
0130             }
0131         }
0132     }
0133 
0134     /* Leave configured channels active in a multi-channel scenario so
0135      * AEN events are still received.
0136      */
0137     return 0;
0138 }
0139 
0140 static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
0141                    struct ncsi_aen_pkt_hdr *h)
0142 {
0143     struct ncsi_channel *nc;
0144     unsigned long flags;
0145 
0146     /* Find the NCSI channel */
0147     ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
0148     if (!nc)
0149         return -ENODEV;
0150 
0151     spin_lock_irqsave(&nc->lock, flags);
0152     if (!list_empty(&nc->link) ||
0153         nc->state != NCSI_CHANNEL_ACTIVE) {
0154         spin_unlock_irqrestore(&nc->lock, flags);
0155         return 0;
0156     }
0157     spin_unlock_irqrestore(&nc->lock, flags);
0158 
0159     ncsi_stop_channel_monitor(nc);
0160     spin_lock_irqsave(&nc->lock, flags);
0161     nc->state = NCSI_CHANNEL_INVISIBLE;
0162     spin_unlock_irqrestore(&nc->lock, flags);
0163 
0164     spin_lock_irqsave(&ndp->lock, flags);
0165     nc->state = NCSI_CHANNEL_INACTIVE;
0166     list_add_tail_rcu(&nc->link, &ndp->channel_queue);
0167     spin_unlock_irqrestore(&ndp->lock, flags);
0168 
0169     return ncsi_process_next_channel(ndp);
0170 }
0171 
0172 static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
0173                    struct ncsi_aen_pkt_hdr *h)
0174 {
0175     struct ncsi_channel *nc;
0176     struct ncsi_channel_mode *ncm;
0177     struct ncsi_aen_hncdsc_pkt *hncdsc;
0178     unsigned long flags;
0179 
0180     /* Find the NCSI channel */
0181     ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
0182     if (!nc)
0183         return -ENODEV;
0184 
0185     spin_lock_irqsave(&nc->lock, flags);
0186     ncm = &nc->modes[NCSI_MODE_LINK];
0187     hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
0188     ncm->data[3] = ntohl(hncdsc->status);
0189     spin_unlock_irqrestore(&nc->lock, flags);
0190     netdev_dbg(ndp->ndev.dev,
0191            "NCSI: host driver %srunning on channel %u\n",
0192            ncm->data[3] & 0x1 ? "" : "not ", nc->id);
0193 
0194     return 0;
0195 }
0196 
0197 static struct ncsi_aen_handler {
0198     unsigned char type;
0199     int           payload;
0200     int           (*handler)(struct ncsi_dev_priv *ndp,
0201                  struct ncsi_aen_pkt_hdr *h);
0202 } ncsi_aen_handlers[] = {
0203     { NCSI_PKT_AEN_LSC,    12, ncsi_aen_handler_lsc    },
0204     { NCSI_PKT_AEN_CR,      4, ncsi_aen_handler_cr     },
0205     { NCSI_PKT_AEN_HNCDSC,  8, ncsi_aen_handler_hncdsc }
0206 };
0207 
0208 int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb)
0209 {
0210     struct ncsi_aen_pkt_hdr *h;
0211     struct ncsi_aen_handler *nah = NULL;
0212     int i, ret;
0213 
0214     /* Find the handler */
0215     h = (struct ncsi_aen_pkt_hdr *)skb_network_header(skb);
0216     for (i = 0; i < ARRAY_SIZE(ncsi_aen_handlers); i++) {
0217         if (ncsi_aen_handlers[i].type == h->type) {
0218             nah = &ncsi_aen_handlers[i];
0219             break;
0220         }
0221     }
0222 
0223     if (!nah) {
0224         netdev_warn(ndp->ndev.dev, "Invalid AEN (0x%x) received\n",
0225                 h->type);
0226         return -ENOENT;
0227     }
0228 
0229     ret = ncsi_validate_aen_pkt(h, nah->payload);
0230     if (ret) {
0231         netdev_warn(ndp->ndev.dev,
0232                 "NCSI: 'bad' packet ignored for AEN type 0x%x\n",
0233                 h->type);
0234         goto out;
0235     }
0236 
0237     ret = nah->handler(ndp, h);
0238     if (ret)
0239         netdev_err(ndp->ndev.dev,
0240                "NCSI: Handler for AEN type 0x%x returned %d\n",
0241                h->type, ret);
0242 out:
0243     consume_skb(skb);
0244     return ret;
0245 }