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_aen_lsc_pkt *lsc; 54 struct ncsi_channel *nc; 55 struct ncsi_channel_mode *ncm; 56 bool chained; 57 int state; 58 unsigned long old_data, data; 59 unsigned long flags; 60 61 /* Find the NCSI channel */ 62 ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc); 63 if (!nc) 64 return -ENODEV; 65 66 /* Update the link status */ 67 lsc = (struct ncsi_aen_lsc_pkt *)h; 68 69 spin_lock_irqsave(&nc->lock, flags); 70 ncm = &nc->modes[NCSI_MODE_LINK]; 71 old_data = ncm->data[2]; 72 data = ntohl(lsc->status); 73 ncm->data[2] = data; 74 ncm->data[4] = ntohl(lsc->oem_status); 75 76 netdev_info(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n", 77 nc->id, data & 0x1 ? "up" : "down"); 78 79 chained = !list_empty(&nc->link); 80 state = nc->state; 81 spin_unlock_irqrestore(&nc->lock, flags); 82 83 if (!((old_data ^ data) & 0x1) || chained) 84 return 0; 85 if (!(state == NCSI_CHANNEL_INACTIVE && (data & 0x1)) && 86 !(state == NCSI_CHANNEL_ACTIVE && !(data & 0x1))) 87 return 0; 88 89 if (!(ndp->flags & NCSI_DEV_HWA) && 90 state == NCSI_CHANNEL_ACTIVE) 91 ndp->flags |= NCSI_DEV_RESHUFFLE; 92 93 ncsi_stop_channel_monitor(nc); 94 spin_lock_irqsave(&ndp->lock, flags); 95 list_add_tail_rcu(&nc->link, &ndp->channel_queue); 96 spin_unlock_irqrestore(&ndp->lock, flags); 97 98 return ncsi_process_next_channel(ndp); 99 } 100 101 static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp, 102 struct ncsi_aen_pkt_hdr *h) 103 { 104 struct ncsi_channel *nc; 105 unsigned long flags; 106 107 /* Find the NCSI channel */ 108 ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc); 109 if (!nc) 110 return -ENODEV; 111 112 spin_lock_irqsave(&nc->lock, flags); 113 if (!list_empty(&nc->link) || 114 nc->state != NCSI_CHANNEL_ACTIVE) { 115 spin_unlock_irqrestore(&nc->lock, flags); 116 return 0; 117 } 118 spin_unlock_irqrestore(&nc->lock, flags); 119 120 ncsi_stop_channel_monitor(nc); 121 spin_lock_irqsave(&nc->lock, flags); 122 nc->state = NCSI_CHANNEL_INVISIBLE; 123 spin_unlock_irqrestore(&nc->lock, flags); 124 125 spin_lock_irqsave(&ndp->lock, flags); 126 nc->state = NCSI_CHANNEL_INACTIVE; 127 list_add_tail_rcu(&nc->link, &ndp->channel_queue); 128 spin_unlock_irqrestore(&ndp->lock, flags); 129 130 return ncsi_process_next_channel(ndp); 131 } 132 133 static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp, 134 struct ncsi_aen_pkt_hdr *h) 135 { 136 struct ncsi_channel *nc; 137 struct ncsi_channel_mode *ncm; 138 struct ncsi_aen_hncdsc_pkt *hncdsc; 139 unsigned long flags; 140 141 /* Find the NCSI channel */ 142 ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc); 143 if (!nc) 144 return -ENODEV; 145 146 spin_lock_irqsave(&nc->lock, flags); 147 ncm = &nc->modes[NCSI_MODE_LINK]; 148 hncdsc = (struct ncsi_aen_hncdsc_pkt *)h; 149 ncm->data[3] = ntohl(hncdsc->status); 150 spin_unlock_irqrestore(&nc->lock, flags); 151 netdev_printk(KERN_DEBUG, ndp->ndev.dev, 152 "NCSI: host driver %srunning on channel %u\n", 153 ncm->data[3] & 0x1 ? "" : "not ", nc->id); 154 155 return 0; 156 } 157 158 static struct ncsi_aen_handler { 159 unsigned char type; 160 int payload; 161 int (*handler)(struct ncsi_dev_priv *ndp, 162 struct ncsi_aen_pkt_hdr *h); 163 } ncsi_aen_handlers[] = { 164 { NCSI_PKT_AEN_LSC, 12, ncsi_aen_handler_lsc }, 165 { NCSI_PKT_AEN_CR, 4, ncsi_aen_handler_cr }, 166 { NCSI_PKT_AEN_HNCDSC, 8, ncsi_aen_handler_hncdsc } 167 }; 168 169 int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb) 170 { 171 struct ncsi_aen_pkt_hdr *h; 172 struct ncsi_aen_handler *nah = NULL; 173 int i, ret; 174 175 /* Find the handler */ 176 h = (struct ncsi_aen_pkt_hdr *)skb_network_header(skb); 177 for (i = 0; i < ARRAY_SIZE(ncsi_aen_handlers); i++) { 178 if (ncsi_aen_handlers[i].type == h->type) { 179 nah = &ncsi_aen_handlers[i]; 180 break; 181 } 182 } 183 184 if (!nah) { 185 netdev_warn(ndp->ndev.dev, "Invalid AEN (0x%x) received\n", 186 h->type); 187 return -ENOENT; 188 } 189 190 ret = ncsi_validate_aen_pkt(h, nah->payload); 191 if (ret) { 192 netdev_warn(ndp->ndev.dev, 193 "NCSI: 'bad' packet ignored for AEN type 0x%x\n", 194 h->type); 195 goto out; 196 } 197 198 ret = nah->handler(ndp, h); 199 if (ret) 200 netdev_err(ndp->ndev.dev, 201 "NCSI: Handler for AEN type 0x%x returned %d\n", 202 h->type, ret); 203 out: 204 consume_skb(skb); 205 return ret; 206 } 207