diff options
author | Tom Herbert <therbert@google.com> | 2011-11-28 11:33:09 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-11-29 12:46:19 -0500 |
commit | 114cf5802165ee93e3ab461c9c505cd94a08b800 (patch) | |
tree | 88208dddd7c1e90f5e1a840ad0ad7afcea991050 | |
parent | 927fbec13e40648d3c87cbb1daaac5b1fb9c8775 (diff) |
bql: Byte queue limits
Networking stack support for byte queue limits, uses dynamic queue
limits library. Byte queue limits are maintained per transmit queue,
and a dql structure has been added to netdev_queue structure for this
purpose.
Configuration of bql is in the tx-<n> sysfs directory for the queue
under the byte_queue_limits directory. Configuration includes:
limit_min, bql minimum limit
limit_max, bql maximum limit
hold_time, bql slack hold time
Also under the directory are:
limit, current byte limit
inflight, current number of bytes on the queue
Signed-off-by: Tom Herbert <therbert@google.com>
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/netdevice.h | 32 | ||||
-rw-r--r-- | net/Kconfig | 6 | ||||
-rw-r--r-- | net/core/dev.c | 3 | ||||
-rw-r--r-- | net/core/net-sysfs.c | 140 |
4 files changed, 172 insertions, 9 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9b24cc7a54d..97edb3215a5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -43,6 +43,7 @@ | |||
43 | #include <linux/rculist.h> | 43 | #include <linux/rculist.h> |
44 | #include <linux/dmaengine.h> | 44 | #include <linux/dmaengine.h> |
45 | #include <linux/workqueue.h> | 45 | #include <linux/workqueue.h> |
46 | #include <linux/dynamic_queue_limits.h> | ||
46 | 47 | ||
47 | #include <linux/ethtool.h> | 48 | #include <linux/ethtool.h> |
48 | #include <net/net_namespace.h> | 49 | #include <net/net_namespace.h> |
@@ -541,7 +542,6 @@ struct netdev_queue { | |||
541 | */ | 542 | */ |
542 | struct net_device *dev; | 543 | struct net_device *dev; |
543 | struct Qdisc *qdisc; | 544 | struct Qdisc *qdisc; |
544 | unsigned long state; | ||
545 | struct Qdisc *qdisc_sleeping; | 545 | struct Qdisc *qdisc_sleeping; |
546 | #ifdef CONFIG_SYSFS | 546 | #ifdef CONFIG_SYSFS |
547 | struct kobject kobj; | 547 | struct kobject kobj; |
@@ -564,6 +564,12 @@ struct netdev_queue { | |||
564 | * (/sys/class/net/DEV/Q/trans_timeout) | 564 | * (/sys/class/net/DEV/Q/trans_timeout) |
565 | */ | 565 | */ |
566 | unsigned long trans_timeout; | 566 | unsigned long trans_timeout; |
567 | |||
568 | unsigned long state; | ||
569 | |||
570 | #ifdef CONFIG_BQL | ||
571 | struct dql dql; | ||
572 | #endif | ||
567 | } ____cacheline_aligned_in_smp; | 573 | } ____cacheline_aligned_in_smp; |
568 | 574 | ||
569 | static inline int netdev_queue_numa_node_read(const struct netdev_queue *q) | 575 | static inline int netdev_queue_numa_node_read(const struct netdev_queue *q) |
@@ -1862,6 +1868,15 @@ static inline int netif_xmit_frozen_or_stopped(const struct netdev_queue *dev_qu | |||
1862 | static inline void netdev_tx_sent_queue(struct netdev_queue *dev_queue, | 1868 | static inline void netdev_tx_sent_queue(struct netdev_queue *dev_queue, |
1863 | unsigned int bytes) | 1869 | unsigned int bytes) |
1864 | { | 1870 | { |
1871 | #ifdef CONFIG_BQL | ||
1872 | dql_queued(&dev_queue->dql, bytes); | ||
1873 | if (unlikely(dql_avail(&dev_queue->dql) < 0)) { | ||
1874 | set_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state); | ||
1875 | if (unlikely(dql_avail(&dev_queue->dql) >= 0)) | ||
1876 | clear_bit(__QUEUE_STATE_STACK_XOFF, | ||
1877 | &dev_queue->state); | ||
1878 | } | ||
1879 | #endif | ||
1865 | } | 1880 | } |
1866 | 1881 | ||
1867 | static inline void netdev_sent_queue(struct net_device *dev, unsigned int bytes) | 1882 | static inline void netdev_sent_queue(struct net_device *dev, unsigned int bytes) |
@@ -1872,6 +1887,18 @@ static inline void netdev_sent_queue(struct net_device *dev, unsigned int bytes) | |||
1872 | static inline void netdev_tx_completed_queue(struct netdev_queue *dev_queue, | 1887 | static inline void netdev_tx_completed_queue(struct netdev_queue *dev_queue, |
1873 | unsigned pkts, unsigned bytes) | 1888 | unsigned pkts, unsigned bytes) |
1874 | { | 1889 | { |
1890 | #ifdef CONFIG_BQL | ||
1891 | if (likely(bytes)) { | ||
1892 | dql_completed(&dev_queue->dql, bytes); | ||
1893 | if (unlikely(test_bit(__QUEUE_STATE_STACK_XOFF, | ||
1894 | &dev_queue->state) && | ||
1895 | dql_avail(&dev_queue->dql) >= 0)) { | ||
1896 | if (test_and_clear_bit(__QUEUE_STATE_STACK_XOFF, | ||
1897 | &dev_queue->state)) | ||
1898 | netif_schedule_queue(dev_queue); | ||
1899 | } | ||
1900 | } | ||
1901 | #endif | ||
1875 | } | 1902 | } |
1876 | 1903 | ||
1877 | static inline void netdev_completed_queue(struct net_device *dev, | 1904 | static inline void netdev_completed_queue(struct net_device *dev, |
@@ -1882,6 +1909,9 @@ static inline void netdev_completed_queue(struct net_device *dev, | |||
1882 | 1909 | ||
1883 | static inline void netdev_tx_reset_queue(struct netdev_queue *q) | 1910 | static inline void netdev_tx_reset_queue(struct netdev_queue *q) |
1884 | { | 1911 | { |
1912 | #ifdef CONFIG_BQL | ||
1913 | dql_reset(&q->dql); | ||
1914 | #endif | ||
1885 | } | 1915 | } |
1886 | 1916 | ||
1887 | static inline void netdev_reset_queue(struct net_device *dev_queue) | 1917 | static inline void netdev_reset_queue(struct net_device *dev_queue) |
diff --git a/net/Kconfig b/net/Kconfig index 63d2c5dc36f..2d998735c4d 100644 --- a/net/Kconfig +++ b/net/Kconfig | |||
@@ -239,6 +239,12 @@ config NETPRIO_CGROUP | |||
239 | Cgroup subsystem for use in assigning processes to network priorities on | 239 | Cgroup subsystem for use in assigning processes to network priorities on |
240 | a per-interface basis | 240 | a per-interface basis |
241 | 241 | ||
242 | config BQL | ||
243 | boolean | ||
244 | depends on SYSFS | ||
245 | select DQL | ||
246 | default y | ||
247 | |||
242 | config HAVE_BPF_JIT | 248 | config HAVE_BPF_JIT |
243 | bool | 249 | bool |
244 | 250 | ||
diff --git a/net/core/dev.c b/net/core/dev.c index cb8f753b423..91a59912207 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -5470,6 +5470,9 @@ static void netdev_init_one_queue(struct net_device *dev, | |||
5470 | queue->xmit_lock_owner = -1; | 5470 | queue->xmit_lock_owner = -1; |
5471 | netdev_queue_numa_node_write(queue, NUMA_NO_NODE); | 5471 | netdev_queue_numa_node_write(queue, NUMA_NO_NODE); |
5472 | queue->dev = dev; | 5472 | queue->dev = dev; |
5473 | #ifdef CONFIG_BQL | ||
5474 | dql_init(&queue->dql, HZ); | ||
5475 | #endif | ||
5473 | } | 5476 | } |
5474 | 5477 | ||
5475 | static int netif_alloc_netdev_queues(struct net_device *dev) | 5478 | static int netif_alloc_netdev_queues(struct net_device *dev) |
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index b17c14a0fce..3bf72b638d3 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/wireless.h> | 21 | #include <linux/wireless.h> |
22 | #include <linux/vmalloc.h> | 22 | #include <linux/vmalloc.h> |
23 | #include <linux/export.h> | 23 | #include <linux/export.h> |
24 | #include <linux/jiffies.h> | ||
24 | #include <net/wext.h> | 25 | #include <net/wext.h> |
25 | 26 | ||
26 | #include "net-sysfs.h" | 27 | #include "net-sysfs.h" |
@@ -845,6 +846,116 @@ static ssize_t show_trans_timeout(struct netdev_queue *queue, | |||
845 | static struct netdev_queue_attribute queue_trans_timeout = | 846 | static struct netdev_queue_attribute queue_trans_timeout = |
846 | __ATTR(tx_timeout, S_IRUGO, show_trans_timeout, NULL); | 847 | __ATTR(tx_timeout, S_IRUGO, show_trans_timeout, NULL); |
847 | 848 | ||
849 | #ifdef CONFIG_BQL | ||
850 | /* | ||
851 | * Byte queue limits sysfs structures and functions. | ||
852 | */ | ||
853 | static ssize_t bql_show(char *buf, unsigned int value) | ||
854 | { | ||
855 | return sprintf(buf, "%u\n", value); | ||
856 | } | ||
857 | |||
858 | static ssize_t bql_set(const char *buf, const size_t count, | ||
859 | unsigned int *pvalue) | ||
860 | { | ||
861 | unsigned int value; | ||
862 | int err; | ||
863 | |||
864 | if (!strcmp(buf, "max") || !strcmp(buf, "max\n")) | ||
865 | value = DQL_MAX_LIMIT; | ||
866 | else { | ||
867 | err = kstrtouint(buf, 10, &value); | ||
868 | if (err < 0) | ||
869 | return err; | ||
870 | if (value > DQL_MAX_LIMIT) | ||
871 | return -EINVAL; | ||
872 | } | ||
873 | |||
874 | *pvalue = value; | ||
875 | |||
876 | return count; | ||
877 | } | ||
878 | |||
879 | static ssize_t bql_show_hold_time(struct netdev_queue *queue, | ||
880 | struct netdev_queue_attribute *attr, | ||
881 | char *buf) | ||
882 | { | ||
883 | struct dql *dql = &queue->dql; | ||
884 | |||
885 | return sprintf(buf, "%u\n", jiffies_to_msecs(dql->slack_hold_time)); | ||
886 | } | ||
887 | |||
888 | static ssize_t bql_set_hold_time(struct netdev_queue *queue, | ||
889 | struct netdev_queue_attribute *attribute, | ||
890 | const char *buf, size_t len) | ||
891 | { | ||
892 | struct dql *dql = &queue->dql; | ||
893 | unsigned value; | ||
894 | int err; | ||
895 | |||
896 | err = kstrtouint(buf, 10, &value); | ||
897 | if (err < 0) | ||
898 | return err; | ||
899 | |||
900 | dql->slack_hold_time = msecs_to_jiffies(value); | ||
901 | |||
902 | return len; | ||
903 | } | ||
904 | |||
905 | static struct netdev_queue_attribute bql_hold_time_attribute = | ||
906 | __ATTR(hold_time, S_IRUGO | S_IWUSR, bql_show_hold_time, | ||
907 | bql_set_hold_time); | ||
908 | |||
909 | static ssize_t bql_show_inflight(struct netdev_queue *queue, | ||
910 | struct netdev_queue_attribute *attr, | ||
911 | char *buf) | ||
912 | { | ||
913 | struct dql *dql = &queue->dql; | ||
914 | |||
915 | return sprintf(buf, "%u\n", dql->num_queued - dql->num_completed); | ||
916 | } | ||
917 | |||
918 | static struct netdev_queue_attribute bql_inflight_attribute = | ||
919 | __ATTR(inflight, S_IRUGO | S_IWUSR, bql_show_inflight, NULL); | ||
920 | |||
921 | #define BQL_ATTR(NAME, FIELD) \ | ||
922 | static ssize_t bql_show_ ## NAME(struct netdev_queue *queue, \ | ||
923 | struct netdev_queue_attribute *attr, \ | ||
924 | char *buf) \ | ||
925 | { \ | ||
926 | return bql_show(buf, queue->dql.FIELD); \ | ||
927 | } \ | ||
928 | \ | ||
929 | static ssize_t bql_set_ ## NAME(struct netdev_queue *queue, \ | ||
930 | struct netdev_queue_attribute *attr, \ | ||
931 | const char *buf, size_t len) \ | ||
932 | { \ | ||
933 | return bql_set(buf, len, &queue->dql.FIELD); \ | ||
934 | } \ | ||
935 | \ | ||
936 | static struct netdev_queue_attribute bql_ ## NAME ## _attribute = \ | ||
937 | __ATTR(NAME, S_IRUGO | S_IWUSR, bql_show_ ## NAME, \ | ||
938 | bql_set_ ## NAME); | ||
939 | |||
940 | BQL_ATTR(limit, limit) | ||
941 | BQL_ATTR(limit_max, max_limit) | ||
942 | BQL_ATTR(limit_min, min_limit) | ||
943 | |||
944 | static struct attribute *dql_attrs[] = { | ||
945 | &bql_limit_attribute.attr, | ||
946 | &bql_limit_max_attribute.attr, | ||
947 | &bql_limit_min_attribute.attr, | ||
948 | &bql_hold_time_attribute.attr, | ||
949 | &bql_inflight_attribute.attr, | ||
950 | NULL | ||
951 | }; | ||
952 | |||
953 | static struct attribute_group dql_group = { | ||
954 | .name = "byte_queue_limits", | ||
955 | .attrs = dql_attrs, | ||
956 | }; | ||
957 | #endif /* CONFIG_BQL */ | ||
958 | |||
848 | #ifdef CONFIG_XPS | 959 | #ifdef CONFIG_XPS |
849 | static inline unsigned int get_netdev_queue_index(struct netdev_queue *queue) | 960 | static inline unsigned int get_netdev_queue_index(struct netdev_queue *queue) |
850 | { | 961 | { |
@@ -1096,17 +1207,17 @@ static struct attribute *netdev_queue_default_attrs[] = { | |||
1096 | NULL | 1207 | NULL |
1097 | }; | 1208 | }; |
1098 | 1209 | ||
1099 | #ifdef CONFIG_XPS | ||
1100 | static void netdev_queue_release(struct kobject *kobj) | 1210 | static void netdev_queue_release(struct kobject *kobj) |
1101 | { | 1211 | { |
1102 | struct netdev_queue *queue = to_netdev_queue(kobj); | 1212 | struct netdev_queue *queue = to_netdev_queue(kobj); |
1103 | 1213 | ||
1214 | #ifdef CONFIG_XPS | ||
1104 | xps_queue_release(queue); | 1215 | xps_queue_release(queue); |
1216 | #endif | ||
1105 | 1217 | ||
1106 | memset(kobj, 0, sizeof(*kobj)); | 1218 | memset(kobj, 0, sizeof(*kobj)); |
1107 | dev_put(queue->dev); | 1219 | dev_put(queue->dev); |
1108 | } | 1220 | } |
1109 | #endif /* CONFIG_XPS */ | ||
1110 | 1221 | ||
1111 | static struct kobj_type netdev_queue_ktype = { | 1222 | static struct kobj_type netdev_queue_ktype = { |
1112 | .sysfs_ops = &netdev_queue_sysfs_ops, | 1223 | .sysfs_ops = &netdev_queue_sysfs_ops, |
@@ -1125,14 +1236,21 @@ static int netdev_queue_add_kobject(struct net_device *net, int index) | |||
1125 | kobj->kset = net->queues_kset; | 1236 | kobj->kset = net->queues_kset; |
1126 | error = kobject_init_and_add(kobj, &netdev_queue_ktype, NULL, | 1237 | error = kobject_init_and_add(kobj, &netdev_queue_ktype, NULL, |
1127 | "tx-%u", index); | 1238 | "tx-%u", index); |
1128 | if (error) { | 1239 | if (error) |
1129 | kobject_put(kobj); | 1240 | goto exit; |
1130 | return error; | 1241 | |
1131 | } | 1242 | #ifdef CONFIG_BQL |
1243 | error = sysfs_create_group(kobj, &dql_group); | ||
1244 | if (error) | ||
1245 | goto exit; | ||
1246 | #endif | ||
1132 | 1247 | ||
1133 | kobject_uevent(kobj, KOBJ_ADD); | 1248 | kobject_uevent(kobj, KOBJ_ADD); |
1134 | dev_hold(queue->dev); | 1249 | dev_hold(queue->dev); |
1135 | 1250 | ||
1251 | return 0; | ||
1252 | exit: | ||
1253 | kobject_put(kobj); | ||
1136 | return error; | 1254 | return error; |
1137 | } | 1255 | } |
1138 | #endif /* CONFIG_SYSFS */ | 1256 | #endif /* CONFIG_SYSFS */ |
@@ -1152,8 +1270,14 @@ netdev_queue_update_kobjects(struct net_device *net, int old_num, int new_num) | |||
1152 | } | 1270 | } |
1153 | } | 1271 | } |
1154 | 1272 | ||
1155 | while (--i >= new_num) | 1273 | while (--i >= new_num) { |
1156 | kobject_put(&net->_tx[i].kobj); | 1274 | struct netdev_queue *queue = net->_tx + i; |
1275 | |||
1276 | #ifdef CONFIG_BQL | ||
1277 | sysfs_remove_group(&queue->kobj, &dql_group); | ||
1278 | #endif | ||
1279 | kobject_put(&queue->kobj); | ||
1280 | } | ||
1157 | 1281 | ||
1158 | return error; | 1282 | return error; |
1159 | #else | 1283 | #else |