ncsi-aen.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. #include <linux/module.h>
  10. #include <linux/kernel.h>
  11. #include <linux/init.h>
  12. #include <linux/netdevice.h>
  13. #include <linux/skbuff.h>
  14. #include <net/ncsi.h>
  15. #include <net/net_namespace.h>
  16. #include <net/sock.h>
  17. #include "internal.h"
  18. #include "ncsi-pkt.h"
  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. if (h->common.revision != NCSI_PKT_REVISION)
  25. return -EINVAL;
  26. if (ntohs(h->common.length) != payload)
  27. return -EINVAL;
  28. /* Validate checksum, which might be zeroes if the
  29. * sender doesn't support checksum according to NCSI
  30. * specification.
  31. */
  32. pchecksum = (__be32 *)((void *)(h + 1) + payload - 4);
  33. if (ntohl(*pchecksum) == 0)
  34. return 0;
  35. checksum = ncsi_calculate_checksum((unsigned char *)h,
  36. sizeof(*h) + payload - 4);
  37. if (*pchecksum != htonl(checksum))
  38. return -EINVAL;
  39. return 0;
  40. }
  41. static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
  42. struct ncsi_aen_pkt_hdr *h)
  43. {
  44. struct ncsi_aen_lsc_pkt *lsc;
  45. struct ncsi_channel *nc;
  46. struct ncsi_channel_mode *ncm;
  47. bool chained;
  48. int state;
  49. unsigned long old_data, data;
  50. unsigned long flags;
  51. /* Find the NCSI channel */
  52. ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
  53. if (!nc)
  54. return -ENODEV;
  55. /* Update the link status */
  56. lsc = (struct ncsi_aen_lsc_pkt *)h;
  57. spin_lock_irqsave(&nc->lock, flags);
  58. ncm = &nc->modes[NCSI_MODE_LINK];
  59. old_data = ncm->data[2];
  60. data = ntohl(lsc->status);
  61. ncm->data[2] = data;
  62. ncm->data[4] = ntohl(lsc->oem_status);
  63. chained = !list_empty(&nc->link);
  64. state = nc->state;
  65. spin_unlock_irqrestore(&nc->lock, flags);
  66. if (!((old_data ^ data) & 0x1) || chained)
  67. return 0;
  68. if (!(state == NCSI_CHANNEL_INACTIVE && (data & 0x1)) &&
  69. !(state == NCSI_CHANNEL_ACTIVE && !(data & 0x1)))
  70. return 0;
  71. if (!(ndp->flags & NCSI_DEV_HWA) &&
  72. state == NCSI_CHANNEL_ACTIVE)
  73. ndp->flags |= NCSI_DEV_RESHUFFLE;
  74. ncsi_stop_channel_monitor(nc);
  75. spin_lock_irqsave(&ndp->lock, flags);
  76. list_add_tail_rcu(&nc->link, &ndp->channel_queue);
  77. spin_unlock_irqrestore(&ndp->lock, flags);
  78. return ncsi_process_next_channel(ndp);
  79. }
  80. static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
  81. struct ncsi_aen_pkt_hdr *h)
  82. {
  83. struct ncsi_channel *nc;
  84. unsigned long flags;
  85. /* Find the NCSI channel */
  86. ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
  87. if (!nc)
  88. return -ENODEV;
  89. spin_lock_irqsave(&nc->lock, flags);
  90. if (!list_empty(&nc->link) ||
  91. nc->state != NCSI_CHANNEL_ACTIVE) {
  92. spin_unlock_irqrestore(&nc->lock, flags);
  93. return 0;
  94. }
  95. spin_unlock_irqrestore(&nc->lock, flags);
  96. ncsi_stop_channel_monitor(nc);
  97. spin_lock_irqsave(&nc->lock, flags);
  98. nc->state = NCSI_CHANNEL_INVISIBLE;
  99. spin_unlock_irqrestore(&nc->lock, flags);
  100. spin_lock_irqsave(&ndp->lock, flags);
  101. nc->state = NCSI_CHANNEL_INACTIVE;
  102. list_add_tail_rcu(&nc->link, &ndp->channel_queue);
  103. spin_unlock_irqrestore(&ndp->lock, flags);
  104. return ncsi_process_next_channel(ndp);
  105. }
  106. static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
  107. struct ncsi_aen_pkt_hdr *h)
  108. {
  109. struct ncsi_channel *nc;
  110. struct ncsi_channel_mode *ncm;
  111. struct ncsi_aen_hncdsc_pkt *hncdsc;
  112. unsigned long flags;
  113. /* Find the NCSI channel */
  114. ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
  115. if (!nc)
  116. return -ENODEV;
  117. /* If the channel is active one, we need reconfigure it */
  118. spin_lock_irqsave(&nc->lock, flags);
  119. ncm = &nc->modes[NCSI_MODE_LINK];
  120. hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
  121. ncm->data[3] = ntohl(hncdsc->status);
  122. if (!list_empty(&nc->link) ||
  123. nc->state != NCSI_CHANNEL_ACTIVE) {
  124. spin_unlock_irqrestore(&nc->lock, flags);
  125. return 0;
  126. }
  127. spin_unlock_irqrestore(&nc->lock, flags);
  128. if (!(ndp->flags & NCSI_DEV_HWA) && !(ncm->data[3] & 0x1))
  129. ndp->flags |= NCSI_DEV_RESHUFFLE;
  130. /* If this channel is the active one and the link doesn't
  131. * work, we have to choose another channel to be active one.
  132. * The logic here is exactly similar to what we do when link
  133. * is down on the active channel.
  134. *
  135. * On the other hand, we need configure it when host driver
  136. * state on the active channel becomes ready.
  137. */
  138. ncsi_stop_channel_monitor(nc);
  139. spin_lock_irqsave(&nc->lock, flags);
  140. nc->state = (ncm->data[3] & 0x1) ? NCSI_CHANNEL_INACTIVE :
  141. NCSI_CHANNEL_ACTIVE;
  142. spin_unlock_irqrestore(&nc->lock, flags);
  143. spin_lock_irqsave(&ndp->lock, flags);
  144. list_add_tail_rcu(&nc->link, &ndp->channel_queue);
  145. spin_unlock_irqrestore(&ndp->lock, flags);
  146. ncsi_process_next_channel(ndp);
  147. return 0;
  148. }
  149. static struct ncsi_aen_handler {
  150. unsigned char type;
  151. int payload;
  152. int (*handler)(struct ncsi_dev_priv *ndp,
  153. struct ncsi_aen_pkt_hdr *h);
  154. } ncsi_aen_handlers[] = {
  155. { NCSI_PKT_AEN_LSC, 12, ncsi_aen_handler_lsc },
  156. { NCSI_PKT_AEN_CR, 4, ncsi_aen_handler_cr },
  157. { NCSI_PKT_AEN_HNCDSC, 4, ncsi_aen_handler_hncdsc }
  158. };
  159. int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb)
  160. {
  161. struct ncsi_aen_pkt_hdr *h;
  162. struct ncsi_aen_handler *nah = NULL;
  163. int i, ret;
  164. /* Find the handler */
  165. h = (struct ncsi_aen_pkt_hdr *)skb_network_header(skb);
  166. for (i = 0; i < ARRAY_SIZE(ncsi_aen_handlers); i++) {
  167. if (ncsi_aen_handlers[i].type == h->type) {
  168. nah = &ncsi_aen_handlers[i];
  169. break;
  170. }
  171. }
  172. if (!nah) {
  173. netdev_warn(ndp->ndev.dev, "Invalid AEN (0x%x) received\n",
  174. h->type);
  175. return -ENOENT;
  176. }
  177. ret = ncsi_validate_aen_pkt(h, nah->payload);
  178. if (ret)
  179. goto out;
  180. ret = nah->handler(ndp, h);
  181. out:
  182. consume_skb(skb);
  183. return ret;
  184. }