~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ncsi/ncsi-aen.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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         nc->modes[NCSI_MODE_TX_ENABLE].enable = 0;
169 
170         return ncsi_process_next_channel(ndp);
171 }
172 
173 static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
174                                    struct ncsi_aen_pkt_hdr *h)
175 {
176         struct ncsi_channel *nc;
177         struct ncsi_channel_mode *ncm;
178         struct ncsi_aen_hncdsc_pkt *hncdsc;
179         unsigned long flags;
180 
181         /* Find the NCSI channel */
182         ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
183         if (!nc)
184                 return -ENODEV;
185 
186         spin_lock_irqsave(&nc->lock, flags);
187         ncm = &nc->modes[NCSI_MODE_LINK];
188         hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
189         ncm->data[3] = ntohl(hncdsc->status);
190         spin_unlock_irqrestore(&nc->lock, flags);
191         netdev_dbg(ndp->ndev.dev,
192                    "NCSI: host driver %srunning on channel %u\n",
193                    ncm->data[3] & 0x1 ? "" : "not ", nc->id);
194 
195         return 0;
196 }
197 
198 static struct ncsi_aen_handler {
199         unsigned char type;
200         int           payload;
201         int           (*handler)(struct ncsi_dev_priv *ndp,
202                                  struct ncsi_aen_pkt_hdr *h);
203 } ncsi_aen_handlers[] = {
204         { NCSI_PKT_AEN_LSC,    12, ncsi_aen_handler_lsc    },
205         { NCSI_PKT_AEN_CR,      4, ncsi_aen_handler_cr     },
206         { NCSI_PKT_AEN_HNCDSC,  8, ncsi_aen_handler_hncdsc }
207 };
208 
209 int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb)
210 {
211         struct ncsi_aen_pkt_hdr *h;
212         struct ncsi_aen_handler *nah = NULL;
213         int i, ret;
214 
215         /* Find the handler */
216         h = (struct ncsi_aen_pkt_hdr *)skb_network_header(skb);
217         for (i = 0; i < ARRAY_SIZE(ncsi_aen_handlers); i++) {
218                 if (ncsi_aen_handlers[i].type == h->type) {
219                         nah = &ncsi_aen_handlers[i];
220                         break;
221                 }
222         }
223 
224         if (!nah) {
225                 netdev_warn(ndp->ndev.dev, "Invalid AEN (0x%x) received\n",
226                             h->type);
227                 return -ENOENT;
228         }
229 
230         ret = ncsi_validate_aen_pkt(h, nah->payload);
231         if (ret) {
232                 netdev_warn(ndp->ndev.dev,
233                             "NCSI: 'bad' packet ignored for AEN type 0x%x\n",
234                             h->type);
235                 goto out;
236         }
237 
238         ret = nah->handler(ndp, h);
239         if (ret)
240                 netdev_err(ndp->ndev.dev,
241                            "NCSI: Handler for AEN type 0x%x returned %d\n",
242                            h->type, ret);
243 out:
244         consume_skb(skb);
245         return ret;
246 }
247 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php