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

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

Version: ~ [ linux-5.1-rc5 ] ~ [ linux-5.0.7 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.34 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.111 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.168 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.178 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.138 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.65 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

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

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | 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.

osdn.jp