diff options
Diffstat (limited to 'net/core/net-sysfs.c')
-rw-r--r-- | net/core/net-sysfs.c | 369 |
1 files changed, 364 insertions, 5 deletions
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 7abeb7ceaa4c..68dbbfdee274 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c | |||
@@ -772,18 +772,377 @@ net_rx_queue_update_kobjects(struct net_device *net, int old_num, int new_num) | |||
772 | return error; | 772 | return error; |
773 | } | 773 | } |
774 | 774 | ||
775 | static int rx_queue_register_kobjects(struct net_device *net) | 775 | /* |
776 | * netdev_queue sysfs structures and functions. | ||
777 | */ | ||
778 | struct netdev_queue_attribute { | ||
779 | struct attribute attr; | ||
780 | ssize_t (*show)(struct netdev_queue *queue, | ||
781 | struct netdev_queue_attribute *attr, char *buf); | ||
782 | ssize_t (*store)(struct netdev_queue *queue, | ||
783 | struct netdev_queue_attribute *attr, const char *buf, size_t len); | ||
784 | }; | ||
785 | #define to_netdev_queue_attr(_attr) container_of(_attr, \ | ||
786 | struct netdev_queue_attribute, attr) | ||
787 | |||
788 | #define to_netdev_queue(obj) container_of(obj, struct netdev_queue, kobj) | ||
789 | |||
790 | static ssize_t netdev_queue_attr_show(struct kobject *kobj, | ||
791 | struct attribute *attr, char *buf) | ||
792 | { | ||
793 | struct netdev_queue_attribute *attribute = to_netdev_queue_attr(attr); | ||
794 | struct netdev_queue *queue = to_netdev_queue(kobj); | ||
795 | |||
796 | if (!attribute->show) | ||
797 | return -EIO; | ||
798 | |||
799 | return attribute->show(queue, attribute, buf); | ||
800 | } | ||
801 | |||
802 | static ssize_t netdev_queue_attr_store(struct kobject *kobj, | ||
803 | struct attribute *attr, | ||
804 | const char *buf, size_t count) | ||
805 | { | ||
806 | struct netdev_queue_attribute *attribute = to_netdev_queue_attr(attr); | ||
807 | struct netdev_queue *queue = to_netdev_queue(kobj); | ||
808 | |||
809 | if (!attribute->store) | ||
810 | return -EIO; | ||
811 | |||
812 | return attribute->store(queue, attribute, buf, count); | ||
813 | } | ||
814 | |||
815 | static const struct sysfs_ops netdev_queue_sysfs_ops = { | ||
816 | .show = netdev_queue_attr_show, | ||
817 | .store = netdev_queue_attr_store, | ||
818 | }; | ||
819 | |||
820 | static inline unsigned int get_netdev_queue_index(struct netdev_queue *queue) | ||
776 | { | 821 | { |
822 | struct net_device *dev = queue->dev; | ||
823 | int i; | ||
824 | |||
825 | for (i = 0; i < dev->num_tx_queues; i++) | ||
826 | if (queue == &dev->_tx[i]) | ||
827 | break; | ||
828 | |||
829 | BUG_ON(i >= dev->num_tx_queues); | ||
830 | |||
831 | return i; | ||
832 | } | ||
833 | |||
834 | |||
835 | static ssize_t show_xps_map(struct netdev_queue *queue, | ||
836 | struct netdev_queue_attribute *attribute, char *buf) | ||
837 | { | ||
838 | struct net_device *dev = queue->dev; | ||
839 | struct xps_dev_maps *dev_maps; | ||
840 | cpumask_var_t mask; | ||
841 | unsigned long index; | ||
842 | size_t len = 0; | ||
843 | int i; | ||
844 | |||
845 | if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) | ||
846 | return -ENOMEM; | ||
847 | |||
848 | index = get_netdev_queue_index(queue); | ||
849 | |||
850 | rcu_read_lock(); | ||
851 | dev_maps = rcu_dereference(dev->xps_maps); | ||
852 | if (dev_maps) { | ||
853 | for_each_possible_cpu(i) { | ||
854 | struct xps_map *map = | ||
855 | rcu_dereference(dev_maps->cpu_map[i]); | ||
856 | if (map) { | ||
857 | int j; | ||
858 | for (j = 0; j < map->len; j++) { | ||
859 | if (map->queues[j] == index) { | ||
860 | cpumask_set_cpu(i, mask); | ||
861 | break; | ||
862 | } | ||
863 | } | ||
864 | } | ||
865 | } | ||
866 | } | ||
867 | rcu_read_unlock(); | ||
868 | |||
869 | len += cpumask_scnprintf(buf + len, PAGE_SIZE, mask); | ||
870 | if (PAGE_SIZE - len < 3) { | ||
871 | free_cpumask_var(mask); | ||
872 | return -EINVAL; | ||
873 | } | ||
874 | |||
875 | free_cpumask_var(mask); | ||
876 | len += sprintf(buf + len, "\n"); | ||
877 | return len; | ||
878 | } | ||
879 | |||
880 | static void xps_map_release(struct rcu_head *rcu) | ||
881 | { | ||
882 | struct xps_map *map = container_of(rcu, struct xps_map, rcu); | ||
883 | |||
884 | kfree(map); | ||
885 | } | ||
886 | |||
887 | static void xps_dev_maps_release(struct rcu_head *rcu) | ||
888 | { | ||
889 | struct xps_dev_maps *dev_maps = | ||
890 | container_of(rcu, struct xps_dev_maps, rcu); | ||
891 | |||
892 | kfree(dev_maps); | ||
893 | } | ||
894 | |||
895 | static DEFINE_MUTEX(xps_map_mutex); | ||
896 | |||
897 | static ssize_t store_xps_map(struct netdev_queue *queue, | ||
898 | struct netdev_queue_attribute *attribute, | ||
899 | const char *buf, size_t len) | ||
900 | { | ||
901 | struct net_device *dev = queue->dev; | ||
902 | cpumask_var_t mask; | ||
903 | int err, i, cpu, pos, map_len, alloc_len, need_set; | ||
904 | unsigned long index; | ||
905 | struct xps_map *map, *new_map; | ||
906 | struct xps_dev_maps *dev_maps, *new_dev_maps; | ||
907 | int nonempty = 0; | ||
908 | |||
909 | if (!capable(CAP_NET_ADMIN)) | ||
910 | return -EPERM; | ||
911 | |||
912 | if (!alloc_cpumask_var(&mask, GFP_KERNEL)) | ||
913 | return -ENOMEM; | ||
914 | |||
915 | index = get_netdev_queue_index(queue); | ||
916 | |||
917 | err = bitmap_parse(buf, len, cpumask_bits(mask), nr_cpumask_bits); | ||
918 | if (err) { | ||
919 | free_cpumask_var(mask); | ||
920 | return err; | ||
921 | } | ||
922 | |||
923 | new_dev_maps = kzalloc(max_t(unsigned, | ||
924 | XPS_DEV_MAPS_SIZE, L1_CACHE_BYTES), GFP_KERNEL); | ||
925 | if (!new_dev_maps) { | ||
926 | free_cpumask_var(mask); | ||
927 | return -ENOMEM; | ||
928 | } | ||
929 | |||
930 | mutex_lock(&xps_map_mutex); | ||
931 | |||
932 | dev_maps = dev->xps_maps; | ||
933 | |||
934 | for_each_possible_cpu(cpu) { | ||
935 | new_map = map = dev_maps ? dev_maps->cpu_map[cpu] : NULL; | ||
936 | |||
937 | if (map) { | ||
938 | for (pos = 0; pos < map->len; pos++) | ||
939 | if (map->queues[pos] == index) | ||
940 | break; | ||
941 | map_len = map->len; | ||
942 | alloc_len = map->alloc_len; | ||
943 | } else | ||
944 | pos = map_len = alloc_len = 0; | ||
945 | |||
946 | need_set = cpu_isset(cpu, *mask) && cpu_online(cpu); | ||
947 | |||
948 | if (need_set && pos >= map_len) { | ||
949 | /* Need to add queue to this CPU's map */ | ||
950 | if (map_len >= alloc_len) { | ||
951 | alloc_len = alloc_len ? | ||
952 | 2 * alloc_len : XPS_MIN_MAP_ALLOC; | ||
953 | new_map = kzalloc(XPS_MAP_SIZE(alloc_len), | ||
954 | GFP_KERNEL); | ||
955 | if (!new_map) | ||
956 | goto error; | ||
957 | new_map->alloc_len = alloc_len; | ||
958 | for (i = 0; i < map_len; i++) | ||
959 | new_map->queues[i] = map->queues[i]; | ||
960 | new_map->len = map_len; | ||
961 | } | ||
962 | new_map->queues[new_map->len++] = index; | ||
963 | } else if (!need_set && pos < map_len) { | ||
964 | /* Need to remove queue from this CPU's map */ | ||
965 | if (map_len > 1) | ||
966 | new_map->queues[pos] = | ||
967 | new_map->queues[--new_map->len]; | ||
968 | else | ||
969 | new_map = NULL; | ||
970 | } | ||
971 | new_dev_maps->cpu_map[cpu] = new_map; | ||
972 | } | ||
973 | |||
974 | /* Cleanup old maps */ | ||
975 | for_each_possible_cpu(cpu) { | ||
976 | map = dev_maps ? dev_maps->cpu_map[cpu] : NULL; | ||
977 | if (map && new_dev_maps->cpu_map[cpu] != map) | ||
978 | call_rcu(&map->rcu, xps_map_release); | ||
979 | if (new_dev_maps->cpu_map[cpu]) | ||
980 | nonempty = 1; | ||
981 | } | ||
982 | |||
983 | if (nonempty) | ||
984 | rcu_assign_pointer(dev->xps_maps, new_dev_maps); | ||
985 | else { | ||
986 | kfree(new_dev_maps); | ||
987 | rcu_assign_pointer(dev->xps_maps, NULL); | ||
988 | } | ||
989 | |||
990 | if (dev_maps) | ||
991 | call_rcu(&dev_maps->rcu, xps_dev_maps_release); | ||
992 | |||
993 | mutex_unlock(&xps_map_mutex); | ||
994 | |||
995 | free_cpumask_var(mask); | ||
996 | return len; | ||
997 | |||
998 | error: | ||
999 | mutex_unlock(&xps_map_mutex); | ||
1000 | |||
1001 | if (new_dev_maps) | ||
1002 | for_each_possible_cpu(i) | ||
1003 | kfree(new_dev_maps->cpu_map[i]); | ||
1004 | kfree(new_dev_maps); | ||
1005 | free_cpumask_var(mask); | ||
1006 | return -ENOMEM; | ||
1007 | } | ||
1008 | |||
1009 | static struct netdev_queue_attribute xps_cpus_attribute = | ||
1010 | __ATTR(xps_cpus, S_IRUGO | S_IWUSR, show_xps_map, store_xps_map); | ||
1011 | |||
1012 | static struct attribute *netdev_queue_default_attrs[] = { | ||
1013 | &xps_cpus_attribute.attr, | ||
1014 | NULL | ||
1015 | }; | ||
1016 | |||
1017 | static void netdev_queue_release(struct kobject *kobj) | ||
1018 | { | ||
1019 | struct netdev_queue *queue = to_netdev_queue(kobj); | ||
1020 | struct net_device *dev = queue->dev; | ||
1021 | struct xps_dev_maps *dev_maps; | ||
1022 | struct xps_map *map; | ||
1023 | unsigned long index; | ||
1024 | int i, pos, nonempty = 0; | ||
1025 | |||
1026 | index = get_netdev_queue_index(queue); | ||
1027 | |||
1028 | mutex_lock(&xps_map_mutex); | ||
1029 | dev_maps = dev->xps_maps; | ||
1030 | |||
1031 | if (dev_maps) { | ||
1032 | for_each_possible_cpu(i) { | ||
1033 | map = dev_maps->cpu_map[i]; | ||
1034 | if (!map) | ||
1035 | continue; | ||
1036 | |||
1037 | for (pos = 0; pos < map->len; pos++) | ||
1038 | if (map->queues[pos] == index) | ||
1039 | break; | ||
1040 | |||
1041 | if (pos < map->len) { | ||
1042 | if (map->len > 1) | ||
1043 | map->queues[pos] = | ||
1044 | map->queues[--map->len]; | ||
1045 | else { | ||
1046 | RCU_INIT_POINTER(dev_maps->cpu_map[i], | ||
1047 | NULL); | ||
1048 | call_rcu(&map->rcu, xps_map_release); | ||
1049 | map = NULL; | ||
1050 | } | ||
1051 | } | ||
1052 | if (map) | ||
1053 | nonempty = 1; | ||
1054 | } | ||
1055 | |||
1056 | if (!nonempty) { | ||
1057 | RCU_INIT_POINTER(dev->xps_maps, NULL); | ||
1058 | call_rcu(&dev_maps->rcu, xps_dev_maps_release); | ||
1059 | } | ||
1060 | } | ||
1061 | |||
1062 | mutex_unlock(&xps_map_mutex); | ||
1063 | |||
1064 | memset(kobj, 0, sizeof(*kobj)); | ||
1065 | dev_put(queue->dev); | ||
1066 | } | ||
1067 | |||
1068 | static struct kobj_type netdev_queue_ktype = { | ||
1069 | .sysfs_ops = &netdev_queue_sysfs_ops, | ||
1070 | .release = netdev_queue_release, | ||
1071 | .default_attrs = netdev_queue_default_attrs, | ||
1072 | }; | ||
1073 | |||
1074 | static int netdev_queue_add_kobject(struct net_device *net, int index) | ||
1075 | { | ||
1076 | struct netdev_queue *queue = net->_tx + index; | ||
1077 | struct kobject *kobj = &queue->kobj; | ||
1078 | int error = 0; | ||
1079 | |||
1080 | kobj->kset = net->queues_kset; | ||
1081 | error = kobject_init_and_add(kobj, &netdev_queue_ktype, NULL, | ||
1082 | "tx-%u", index); | ||
1083 | if (error) { | ||
1084 | kobject_put(kobj); | ||
1085 | return error; | ||
1086 | } | ||
1087 | |||
1088 | kobject_uevent(kobj, KOBJ_ADD); | ||
1089 | dev_hold(queue->dev); | ||
1090 | |||
1091 | return error; | ||
1092 | } | ||
1093 | |||
1094 | int | ||
1095 | netdev_queue_update_kobjects(struct net_device *net, int old_num, int new_num) | ||
1096 | { | ||
1097 | int i; | ||
1098 | int error = 0; | ||
1099 | |||
1100 | for (i = old_num; i < new_num; i++) { | ||
1101 | error = netdev_queue_add_kobject(net, i); | ||
1102 | if (error) { | ||
1103 | new_num = old_num; | ||
1104 | break; | ||
1105 | } | ||
1106 | } | ||
1107 | |||
1108 | while (--i >= new_num) | ||
1109 | kobject_put(&net->_tx[i].kobj); | ||
1110 | |||
1111 | return error; | ||
1112 | } | ||
1113 | |||
1114 | static int register_queue_kobjects(struct net_device *net) | ||
1115 | { | ||
1116 | int error = 0, txq = 0, rxq = 0; | ||
1117 | |||
777 | net->queues_kset = kset_create_and_add("queues", | 1118 | net->queues_kset = kset_create_and_add("queues", |
778 | NULL, &net->dev.kobj); | 1119 | NULL, &net->dev.kobj); |
779 | if (!net->queues_kset) | 1120 | if (!net->queues_kset) |
780 | return -ENOMEM; | 1121 | return -ENOMEM; |
781 | return net_rx_queue_update_kobjects(net, 0, net->real_num_rx_queues); | 1122 | |
1123 | error = net_rx_queue_update_kobjects(net, 0, net->real_num_rx_queues); | ||
1124 | if (error) | ||
1125 | goto error; | ||
1126 | rxq = net->real_num_rx_queues; | ||
1127 | |||
1128 | error = netdev_queue_update_kobjects(net, 0, | ||
1129 | net->real_num_tx_queues); | ||
1130 | if (error) | ||
1131 | goto error; | ||
1132 | txq = net->real_num_tx_queues; | ||
1133 | |||
1134 | return 0; | ||
1135 | |||
1136 | error: | ||
1137 | netdev_queue_update_kobjects(net, txq, 0); | ||
1138 | net_rx_queue_update_kobjects(net, rxq, 0); | ||
1139 | return error; | ||
782 | } | 1140 | } |
783 | 1141 | ||
784 | static void rx_queue_remove_kobjects(struct net_device *net) | 1142 | static void remove_queue_kobjects(struct net_device *net) |
785 | { | 1143 | { |
786 | net_rx_queue_update_kobjects(net, net->real_num_rx_queues, 0); | 1144 | net_rx_queue_update_kobjects(net, net->real_num_rx_queues, 0); |
1145 | netdev_queue_update_kobjects(net, net->real_num_tx_queues, 0); | ||
787 | kset_unregister(net->queues_kset); | 1146 | kset_unregister(net->queues_kset); |
788 | } | 1147 | } |
789 | #endif /* CONFIG_RPS */ | 1148 | #endif /* CONFIG_RPS */ |
@@ -886,7 +1245,7 @@ void netdev_unregister_kobject(struct net_device * net) | |||
886 | kobject_get(&dev->kobj); | 1245 | kobject_get(&dev->kobj); |
887 | 1246 | ||
888 | #ifdef CONFIG_RPS | 1247 | #ifdef CONFIG_RPS |
889 | rx_queue_remove_kobjects(net); | 1248 | remove_queue_kobjects(net); |
890 | #endif | 1249 | #endif |
891 | 1250 | ||
892 | device_del(dev); | 1251 | device_del(dev); |
@@ -927,7 +1286,7 @@ int netdev_register_kobject(struct net_device *net) | |||
927 | return error; | 1286 | return error; |
928 | 1287 | ||
929 | #ifdef CONFIG_RPS | 1288 | #ifdef CONFIG_RPS |
930 | error = rx_queue_register_kobjects(net); | 1289 | error = register_queue_kobjects(net); |
931 | if (error) { | 1290 | if (error) { |
932 | device_del(dev); | 1291 | device_del(dev); |
933 | return error; | 1292 | return error; |