diff options
-rw-r--r-- | drivers/net/sunvnet.c | 258 | ||||
-rw-r--r-- | drivers/net/sunvnet.h | 4 |
2 files changed, 130 insertions, 132 deletions
diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c index 8a667c13faef..b69f55226bd6 100644 --- a/drivers/net/sunvnet.c +++ b/drivers/net/sunvnet.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/netdevice.h> | 12 | #include <linux/netdevice.h> |
13 | #include <linux/ethtool.h> | 13 | #include <linux/ethtool.h> |
14 | #include <linux/etherdevice.h> | 14 | #include <linux/etherdevice.h> |
15 | #include <linux/mutex.h> | ||
15 | 16 | ||
16 | #include <asm/vio.h> | 17 | #include <asm/vio.h> |
17 | #include <asm/ldc.h> | 18 | #include <asm/ldc.h> |
@@ -875,6 +876,115 @@ err_out: | |||
875 | return err; | 876 | return err; |
876 | } | 877 | } |
877 | 878 | ||
879 | static LIST_HEAD(vnet_list); | ||
880 | static DEFINE_MUTEX(vnet_list_mutex); | ||
881 | |||
882 | static struct vnet * __devinit vnet_new(const u64 *local_mac) | ||
883 | { | ||
884 | struct net_device *dev; | ||
885 | struct vnet *vp; | ||
886 | int err, i; | ||
887 | |||
888 | dev = alloc_etherdev(sizeof(*vp)); | ||
889 | if (!dev) { | ||
890 | printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); | ||
891 | return ERR_PTR(-ENOMEM); | ||
892 | } | ||
893 | |||
894 | for (i = 0; i < ETH_ALEN; i++) | ||
895 | dev->dev_addr[i] = (*local_mac >> (5 - i) * 8) & 0xff; | ||
896 | |||
897 | memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); | ||
898 | |||
899 | vp = netdev_priv(dev); | ||
900 | |||
901 | spin_lock_init(&vp->lock); | ||
902 | vp->dev = dev; | ||
903 | |||
904 | INIT_LIST_HEAD(&vp->port_list); | ||
905 | for (i = 0; i < VNET_PORT_HASH_SIZE; i++) | ||
906 | INIT_HLIST_HEAD(&vp->port_hash[i]); | ||
907 | INIT_LIST_HEAD(&vp->list); | ||
908 | vp->local_mac = *local_mac; | ||
909 | |||
910 | dev->open = vnet_open; | ||
911 | dev->stop = vnet_close; | ||
912 | dev->set_multicast_list = vnet_set_rx_mode; | ||
913 | dev->set_mac_address = vnet_set_mac_addr; | ||
914 | dev->tx_timeout = vnet_tx_timeout; | ||
915 | dev->ethtool_ops = &vnet_ethtool_ops; | ||
916 | dev->watchdog_timeo = VNET_TX_TIMEOUT; | ||
917 | dev->change_mtu = vnet_change_mtu; | ||
918 | dev->hard_start_xmit = vnet_start_xmit; | ||
919 | |||
920 | err = register_netdev(dev); | ||
921 | if (err) { | ||
922 | printk(KERN_ERR PFX "Cannot register net device, " | ||
923 | "aborting.\n"); | ||
924 | goto err_out_free_dev; | ||
925 | } | ||
926 | |||
927 | printk(KERN_INFO "%s: Sun LDOM vnet ", dev->name); | ||
928 | |||
929 | for (i = 0; i < 6; i++) | ||
930 | printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); | ||
931 | |||
932 | list_add(&vp->list, &vnet_list); | ||
933 | |||
934 | return vp; | ||
935 | |||
936 | err_out_free_dev: | ||
937 | free_netdev(dev); | ||
938 | |||
939 | return ERR_PTR(err); | ||
940 | } | ||
941 | |||
942 | static struct vnet * __devinit vnet_find_or_create(const u64 *local_mac) | ||
943 | { | ||
944 | struct vnet *iter, *vp; | ||
945 | |||
946 | mutex_lock(&vnet_list_mutex); | ||
947 | vp = NULL; | ||
948 | list_for_each_entry(iter, &vnet_list, list) { | ||
949 | if (iter->local_mac == *local_mac) { | ||
950 | vp = iter; | ||
951 | break; | ||
952 | } | ||
953 | } | ||
954 | if (!vp) | ||
955 | vp = vnet_new(local_mac); | ||
956 | mutex_unlock(&vnet_list_mutex); | ||
957 | |||
958 | return vp; | ||
959 | } | ||
960 | |||
961 | static const char *local_mac_prop = "local-mac-address"; | ||
962 | |||
963 | static struct vnet * __devinit vnet_find_parent(struct mdesc_handle *hp, | ||
964 | u64 port_node) | ||
965 | { | ||
966 | const u64 *local_mac = NULL; | ||
967 | u64 a; | ||
968 | |||
969 | mdesc_for_each_arc(a, hp, port_node, MDESC_ARC_TYPE_BACK) { | ||
970 | u64 target = mdesc_arc_target(hp, a); | ||
971 | const char *name; | ||
972 | |||
973 | name = mdesc_get_property(hp, target, "name", NULL); | ||
974 | if (!name || strcmp(name, "network")) | ||
975 | continue; | ||
976 | |||
977 | local_mac = mdesc_get_property(hp, target, | ||
978 | local_mac_prop, NULL); | ||
979 | if (local_mac) | ||
980 | break; | ||
981 | } | ||
982 | if (!local_mac) | ||
983 | return ERR_PTR(-ENODEV); | ||
984 | |||
985 | return vnet_find_or_create(local_mac); | ||
986 | } | ||
987 | |||
878 | static struct ldc_channel_config vnet_ldc_cfg = { | 988 | static struct ldc_channel_config vnet_ldc_cfg = { |
879 | .event = vnet_event, | 989 | .event = vnet_event, |
880 | .mtu = 64, | 990 | .mtu = 64, |
@@ -887,6 +997,14 @@ static struct vio_driver_ops vnet_vio_ops = { | |||
887 | .handshake_complete = vnet_handshake_complete, | 997 | .handshake_complete = vnet_handshake_complete, |
888 | }; | 998 | }; |
889 | 999 | ||
1000 | static void print_version(void) | ||
1001 | { | ||
1002 | static int version_printed; | ||
1003 | |||
1004 | if (version_printed++ == 0) | ||
1005 | printk(KERN_INFO "%s", version); | ||
1006 | } | ||
1007 | |||
890 | const char *remote_macaddr_prop = "remote-mac-address"; | 1008 | const char *remote_macaddr_prop = "remote-mac-address"; |
891 | 1009 | ||
892 | static int __devinit vnet_port_probe(struct vio_dev *vdev, | 1010 | static int __devinit vnet_port_probe(struct vio_dev *vdev, |
@@ -899,14 +1017,17 @@ static int __devinit vnet_port_probe(struct vio_dev *vdev, | |||
899 | const u64 *rmac; | 1017 | const u64 *rmac; |
900 | int len, i, err, switch_port; | 1018 | int len, i, err, switch_port; |
901 | 1019 | ||
902 | vp = dev_get_drvdata(vdev->dev.parent); | 1020 | print_version(); |
903 | if (!vp) { | ||
904 | printk(KERN_ERR PFX "Cannot find port parent vnet.\n"); | ||
905 | return -ENODEV; | ||
906 | } | ||
907 | 1021 | ||
908 | hp = mdesc_grab(); | 1022 | hp = mdesc_grab(); |
909 | 1023 | ||
1024 | vp = vnet_find_parent(hp, vdev->mp); | ||
1025 | if (IS_ERR(vp)) { | ||
1026 | printk(KERN_ERR PFX "Cannot find port parent vnet.\n"); | ||
1027 | err = PTR_ERR(vp); | ||
1028 | goto err_out_put_mdesc; | ||
1029 | } | ||
1030 | |||
910 | rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len); | 1031 | rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len); |
911 | err = -ENODEV; | 1032 | err = -ENODEV; |
912 | if (!rmac) { | 1033 | if (!rmac) { |
@@ -1025,139 +1146,14 @@ static struct vio_driver vnet_port_driver = { | |||
1025 | } | 1146 | } |
1026 | }; | 1147 | }; |
1027 | 1148 | ||
1028 | const char *local_mac_prop = "local-mac-address"; | ||
1029 | |||
1030 | static int __devinit vnet_probe(struct vio_dev *vdev, | ||
1031 | const struct vio_device_id *id) | ||
1032 | { | ||
1033 | static int vnet_version_printed; | ||
1034 | struct mdesc_handle *hp; | ||
1035 | struct net_device *dev; | ||
1036 | struct vnet *vp; | ||
1037 | const u64 *mac; | ||
1038 | int err, i, len; | ||
1039 | |||
1040 | if (vnet_version_printed++ == 0) | ||
1041 | printk(KERN_INFO "%s", version); | ||
1042 | |||
1043 | hp = mdesc_grab(); | ||
1044 | |||
1045 | mac = mdesc_get_property(hp, vdev->mp, local_mac_prop, &len); | ||
1046 | if (!mac) { | ||
1047 | printk(KERN_ERR PFX "vnet lacks %s property.\n", | ||
1048 | local_mac_prop); | ||
1049 | err = -ENODEV; | ||
1050 | goto err_out; | ||
1051 | } | ||
1052 | |||
1053 | dev = alloc_etherdev(sizeof(*vp)); | ||
1054 | if (!dev) { | ||
1055 | printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); | ||
1056 | err = -ENOMEM; | ||
1057 | goto err_out; | ||
1058 | } | ||
1059 | |||
1060 | for (i = 0; i < ETH_ALEN; i++) | ||
1061 | dev->dev_addr[i] = (*mac >> (5 - i) * 8) & 0xff; | ||
1062 | |||
1063 | memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); | ||
1064 | |||
1065 | SET_NETDEV_DEV(dev, &vdev->dev); | ||
1066 | |||
1067 | vp = netdev_priv(dev); | ||
1068 | |||
1069 | spin_lock_init(&vp->lock); | ||
1070 | vp->dev = dev; | ||
1071 | vp->vdev = vdev; | ||
1072 | |||
1073 | INIT_LIST_HEAD(&vp->port_list); | ||
1074 | for (i = 0; i < VNET_PORT_HASH_SIZE; i++) | ||
1075 | INIT_HLIST_HEAD(&vp->port_hash[i]); | ||
1076 | |||
1077 | dev->open = vnet_open; | ||
1078 | dev->stop = vnet_close; | ||
1079 | dev->set_multicast_list = vnet_set_rx_mode; | ||
1080 | dev->set_mac_address = vnet_set_mac_addr; | ||
1081 | dev->tx_timeout = vnet_tx_timeout; | ||
1082 | dev->ethtool_ops = &vnet_ethtool_ops; | ||
1083 | dev->watchdog_timeo = VNET_TX_TIMEOUT; | ||
1084 | dev->change_mtu = vnet_change_mtu; | ||
1085 | dev->hard_start_xmit = vnet_start_xmit; | ||
1086 | |||
1087 | err = register_netdev(dev); | ||
1088 | if (err) { | ||
1089 | printk(KERN_ERR PFX "Cannot register net device, " | ||
1090 | "aborting.\n"); | ||
1091 | goto err_out_free_dev; | ||
1092 | } | ||
1093 | |||
1094 | printk(KERN_INFO "%s: Sun LDOM vnet ", dev->name); | ||
1095 | |||
1096 | for (i = 0; i < 6; i++) | ||
1097 | printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); | ||
1098 | |||
1099 | dev_set_drvdata(&vdev->dev, vp); | ||
1100 | |||
1101 | mdesc_release(hp); | ||
1102 | |||
1103 | return 0; | ||
1104 | |||
1105 | err_out_free_dev: | ||
1106 | free_netdev(dev); | ||
1107 | |||
1108 | err_out: | ||
1109 | mdesc_release(hp); | ||
1110 | return err; | ||
1111 | } | ||
1112 | |||
1113 | static int vnet_remove(struct vio_dev *vdev) | ||
1114 | { | ||
1115 | |||
1116 | struct vnet *vp = dev_get_drvdata(&vdev->dev); | ||
1117 | |||
1118 | if (vp) { | ||
1119 | /* XXX unregister port, or at least check XXX */ | ||
1120 | unregister_netdevice(vp->dev); | ||
1121 | dev_set_drvdata(&vdev->dev, NULL); | ||
1122 | } | ||
1123 | return 0; | ||
1124 | } | ||
1125 | |||
1126 | static struct vio_device_id vnet_match[] = { | ||
1127 | { | ||
1128 | .type = "network", | ||
1129 | }, | ||
1130 | {}, | ||
1131 | }; | ||
1132 | MODULE_DEVICE_TABLE(vio, vnet_match); | ||
1133 | |||
1134 | static struct vio_driver vnet_driver = { | ||
1135 | .id_table = vnet_match, | ||
1136 | .probe = vnet_probe, | ||
1137 | .remove = vnet_remove, | ||
1138 | .driver = { | ||
1139 | .name = "vnet", | ||
1140 | .owner = THIS_MODULE, | ||
1141 | } | ||
1142 | }; | ||
1143 | |||
1144 | static int __init vnet_init(void) | 1149 | static int __init vnet_init(void) |
1145 | { | 1150 | { |
1146 | int err = vio_register_driver(&vnet_driver); | 1151 | return vio_register_driver(&vnet_port_driver); |
1147 | |||
1148 | if (!err) { | ||
1149 | err = vio_register_driver(&vnet_port_driver); | ||
1150 | if (err) | ||
1151 | vio_unregister_driver(&vnet_driver); | ||
1152 | } | ||
1153 | |||
1154 | return err; | ||
1155 | } | 1152 | } |
1156 | 1153 | ||
1157 | static void __exit vnet_exit(void) | 1154 | static void __exit vnet_exit(void) |
1158 | { | 1155 | { |
1159 | vio_unregister_driver(&vnet_port_driver); | 1156 | vio_unregister_driver(&vnet_port_driver); |
1160 | vio_unregister_driver(&vnet_driver); | ||
1161 | } | 1157 | } |
1162 | 1158 | ||
1163 | module_init(vnet_init); | 1159 | module_init(vnet_init); |
diff --git a/drivers/net/sunvnet.h b/drivers/net/sunvnet.h index 1c887302d46d..7d3a0cac727b 100644 --- a/drivers/net/sunvnet.h +++ b/drivers/net/sunvnet.h | |||
@@ -60,11 +60,13 @@ struct vnet { | |||
60 | struct net_device *dev; | 60 | struct net_device *dev; |
61 | 61 | ||
62 | u32 msg_enable; | 62 | u32 msg_enable; |
63 | struct vio_dev *vdev; | ||
64 | 63 | ||
65 | struct list_head port_list; | 64 | struct list_head port_list; |
66 | 65 | ||
67 | struct hlist_head port_hash[VNET_PORT_HASH_SIZE]; | 66 | struct hlist_head port_hash[VNET_PORT_HASH_SIZE]; |
67 | |||
68 | struct list_head list; | ||
69 | u64 local_mac; | ||
68 | }; | 70 | }; |
69 | 71 | ||
70 | #endif /* _SUNVNET_H */ | 72 | #endif /* _SUNVNET_H */ |