From 8af60dae0911d6d087c9fc11f27b947f867589e2 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 24 Jun 2014 09:36:50 -0700 Subject: [PATCH 0001/1185] net: wireless: Increase scan entry expiration to fit new scan time Change-Id: I0e23ce45d78d7c17633670973f49943a5ed6032d Signed-off-by: Dmitry Shmidt --- net/wireless/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 41b0f96a933f..3ebf125bc99e 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -55,7 +55,7 @@ * also linked into the probe response struct. */ -#define IEEE80211_SCAN_RESULT_EXPIRE (3 * HZ) +#define IEEE80211_SCAN_RESULT_EXPIRE (7 * HZ) static void bss_free(struct cfg80211_internal_bss *bss) { From f6f56efe7de93cd091ee456921a6043da370c22a Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 24 Jun 2014 13:19:46 -0700 Subject: [PATCH 0002/1185] net: wireless: Add NL80211_FLAG_NEED_WIPHY flag to vendor command Change-Id: I52ee3bc8a422c2a4c57cccccccd6ba3e721b4c01 Signed-off-by: Dmitry Shmidt --- net/wireless/nl80211.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ddb993fb0d38..150a38fc346e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -9153,7 +9153,8 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_vendor_cmd, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_RTNL, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, }, }; From 364a09b74ecbcee9411c3677212f0392d3c876de Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Tue, 23 Jul 2013 17:38:41 -0400 Subject: [PATCH 0003/1185] SELinux: Enable setting security contexts on rootfs inodes. rootfs (ramfs) can support setting of security contexts by userspace due to the vfs fallback behavior of calling the security module to set the in-core inode state for security.* attributes when the filesystem does not provide an xattr handler. No xattr handler required as the inodes are pinned in memory and have no backing store. This is useful in allowing early userspace to label individual files within a rootfs while still providing a policy-defined default via genfs. Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore Signed-off-by: Eric Paris --- security/selinux/hooks.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 44087388010c..a8de30bd733a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -422,6 +422,13 @@ static int sb_finish_set_opts(struct super_block *sb) if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0) sbsec->flags |= SE_SBLABELSUPP; + /* + * Special handling for rootfs. Is genfs but supports + * setting SELinux context on in-core inodes. + */ + if (strncmp(sb->s_type->name, "rootfs", sizeof("rootfs")) == 0) + sbsec->flags |= SE_SBLABELSUPP; + /* Initialize the root inode. */ rc = inode_doinit_with_dentry(root_inode, root); From 99a6ea48b591877d1cd6a51732c40a1d5321d961 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 31 Mar 2014 16:23:51 +0900 Subject: [PATCH 0004/1185] net: core: Support UID-based routing. This contains the following commits: 1. cc2f522 net: core: Add a UID range to fib rules. 2. d7ed2bd net: core: Use the socket UID in routing lookups. 3. 2f9306a net: core: Add a RTA_UID attribute to routes. This is so that userspace can do per-UID route lookups. 4. 8e46efb net: ipv6: Use the UID in IPv6 PMTUD IPv4 PMTUD already does this because ipv4_sk_update_pmtu uses __build_flow_key, which includes the UID. Bug: 15413527 Change-Id: I81bd31dae655de9cce7d7a1f9a905dc1c2feba7c Signed-off-by: Lorenzo Colitti --- include/net/fib_rules.h | 6 +++- include/net/flow.h | 9 +++++- include/net/ip.h | 1 + include/net/ip6_route.h | 2 +- include/net/route.h | 5 +-- include/uapi/linux/fib_rules.h | 2 ++ include/uapi/linux/rtnetlink.h | 1 + net/core/fib_rules.c | 53 ++++++++++++++++++++++++++++++-- net/ipv4/fib_frontend.c | 1 + net/ipv4/inet_connection_sock.c | 6 ++-- net/ipv4/ip_output.c | 3 +- net/ipv4/ping.c | 3 +- net/ipv4/raw.c | 3 +- net/ipv4/route.c | 25 +++++++++++---- net/ipv4/syncookies.c | 3 +- net/ipv4/udp.c | 3 +- net/ipv6/af_inet6.c | 1 + net/ipv6/ah6.c | 2 +- net/ipv6/datagram.c | 1 + net/ipv6/esp6.c | 2 +- net/ipv6/icmp.c | 2 +- net/ipv6/inet6_connection_sock.c | 2 ++ net/ipv6/ipcomp6.c | 2 +- net/ipv6/ping.c | 1 + net/ipv6/raw.c | 1 + net/ipv6/route.c | 12 ++++++-- net/ipv6/syncookies.c | 1 + net/ipv6/tcp_ipv6.c | 1 + net/ipv6/udp.c | 1 + 29 files changed, 129 insertions(+), 26 deletions(-) diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index e361f4882426..4ac12e14c6d9 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -23,6 +23,8 @@ struct fib_rule { struct fib_rule __rcu *ctarget; char iifname[IFNAMSIZ]; char oifname[IFNAMSIZ]; + kuid_t uid_start; + kuid_t uid_end; struct rcu_head rcu; struct net * fr_net; }; @@ -80,7 +82,9 @@ struct fib_rules_ops { [FRA_FWMARK] = { .type = NLA_U32 }, \ [FRA_FWMASK] = { .type = NLA_U32 }, \ [FRA_TABLE] = { .type = NLA_U32 }, \ - [FRA_GOTO] = { .type = NLA_U32 } + [FRA_GOTO] = { .type = NLA_U32 }, \ + [FRA_UID_START] = { .type = NLA_U32 }, \ + [FRA_UID_END] = { .type = NLA_U32 } static inline void fib_rule_get(struct fib_rule *rule) { diff --git a/include/net/flow.h b/include/net/flow.h index 628e11b98c58..c91e2aae3fb1 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -10,6 +10,7 @@ #include #include #include +#include struct flowi_common { int flowic_oif; @@ -23,6 +24,7 @@ struct flowi_common { #define FLOWI_FLAG_CAN_SLEEP 0x02 #define FLOWI_FLAG_KNOWN_NH 0x04 __u32 flowic_secid; + kuid_t flowic_uid; }; union flowi_uli { @@ -59,6 +61,7 @@ struct flowi4 { #define flowi4_proto __fl_common.flowic_proto #define flowi4_flags __fl_common.flowic_flags #define flowi4_secid __fl_common.flowic_secid +#define flowi4_uid __fl_common.flowic_uid /* (saddr,daddr) must be grouped, same order as in IP header */ __be32 saddr; @@ -78,7 +81,8 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, __u32 mark, __u8 tos, __u8 scope, __u8 proto, __u8 flags, __be32 daddr, __be32 saddr, - __be16 dport, __be16 sport) + __be16 dport, __be16 sport, + kuid_t uid) { fl4->flowi4_oif = oif; fl4->flowi4_iif = 0; @@ -88,6 +92,7 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, fl4->flowi4_proto = proto; fl4->flowi4_flags = flags; fl4->flowi4_secid = 0; + fl4->flowi4_uid = uid; fl4->daddr = daddr; fl4->saddr = saddr; fl4->fl4_dport = dport; @@ -115,6 +120,7 @@ struct flowi6 { #define flowi6_proto __fl_common.flowic_proto #define flowi6_flags __fl_common.flowic_flags #define flowi6_secid __fl_common.flowic_secid +#define flowi6_uid __fl_common.flowic_uid struct in6_addr daddr; struct in6_addr saddr; __be32 flowlabel; @@ -158,6 +164,7 @@ struct flowi { #define flowi_proto u.__fl_common.flowic_proto #define flowi_flags u.__fl_common.flowic_flags #define flowi_secid u.__fl_common.flowic_secid +#define flowi_uid u.__fl_common.flowic_uid } __attribute__((__aligned__(BITS_PER_LONG/8))); static inline struct flowi *flowi4_to_flowi(struct flowi4 *fl4) diff --git a/include/net/ip.h b/include/net/ip.h index 509b88079270..02fc145ecc42 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -153,6 +153,7 @@ struct ip_reply_arg { /* -1 if not needed */ int bound_dev_if; u8 tos; + kuid_t uid; }; #define IP_REPLY_ARG_NOSRCCHECK 1 diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 260f83f16bcf..25b4500f28c9 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -131,7 +131,7 @@ extern int rt6_route_rcv(struct net_device *dev, const struct in6_addr *gwaddr); extern void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, - int oif, u32 mark); + int oif, u32 mark, kuid_t uid); extern void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu); extern void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark); diff --git a/include/net/route.h b/include/net/route.h index 2ea40c1b5e00..b5b44875543e 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -142,7 +142,7 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos, RT_SCOPE_UNIVERSE, proto, sk ? inet_sk_flowi_flags(sk) : 0, - daddr, saddr, dport, sport); + daddr, saddr, dport, sport, sock_i_uid(sk)); if (sk) security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); @@ -253,7 +253,8 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32 flow_flags |= FLOWI_FLAG_CAN_SLEEP; flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, - protocol, flow_flags, dst, src, dport, sport); + protocol, flow_flags, dst, src, dport, sport, + sock_i_uid(sk)); } static inline struct rtable *ip_route_connect(struct flowi4 *fl4, diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h index 51da65b68b85..9dcdb6251cb8 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h @@ -49,6 +49,8 @@ enum { FRA_TABLE, /* Extended table id */ FRA_FWMASK, /* mask for netfilter mark */ FRA_OIFNAME, + FRA_UID_START, /* UID range */ + FRA_UID_END, __FRA_MAX }; diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 7a2144e1afae..07c1146c1f51 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -297,6 +297,7 @@ enum rtattr_type_t { RTA_TABLE, RTA_MARK, RTA_MFC_STATS, + RTA_UID, __RTA_MAX }; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 0e9131195eb0..a40a876b8559 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -31,6 +31,8 @@ int fib_default_rule_add(struct fib_rules_ops *ops, r->pref = pref; r->table = table; r->flags = flags; + r->uid_start = INVALID_UID; + r->uid_end = INVALID_UID; r->fr_net = hold_net(ops->fro_net); /* The lock is not required here, the list in unreacheable @@ -179,6 +181,23 @@ void fib_rules_unregister(struct fib_rules_ops *ops) } EXPORT_SYMBOL_GPL(fib_rules_unregister); +static inline kuid_t fib_nl_uid(struct nlattr *nla) +{ + return make_kuid(current_user_ns(), nla_get_u32(nla)); +} + +static int nla_put_uid(struct sk_buff *skb, int idx, kuid_t uid) +{ + return nla_put_u32(skb, idx, from_kuid_munged(current_user_ns(), uid)); +} + +static int fib_uid_range_match(struct flowi *fl, struct fib_rule *rule) +{ + return (!uid_valid(rule->uid_start) && !uid_valid(rule->uid_end)) || + (uid_gte(fl->flowi_uid, rule->uid_start) && + uid_lte(fl->flowi_uid, rule->uid_end)); +} + static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, struct flowi *fl, int flags) { @@ -193,6 +212,9 @@ static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, if ((rule->mark ^ fl->flowi_mark) & rule->mark_mask) goto out; + if (!fib_uid_range_match(fl, rule)) + goto out; + ret = ops->match(rule, fl, flags); out: return (rule->flags & FIB_RULE_INVERT) ? !ret : ret; @@ -363,6 +385,19 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh) } else if (rule->action == FR_ACT_GOTO) goto errout_free; + /* UID start and end must either both be valid or both unspecified. */ + rule->uid_start = rule->uid_end = INVALID_UID; + if (tb[FRA_UID_START] || tb[FRA_UID_END]) { + if (tb[FRA_UID_START] && tb[FRA_UID_END]) { + rule->uid_start = fib_nl_uid(tb[FRA_UID_START]); + rule->uid_end = fib_nl_uid(tb[FRA_UID_END]); + } + if (!uid_valid(rule->uid_start) || + !uid_valid(rule->uid_end) || + !uid_lte(rule->uid_start, rule->uid_end)) + goto errout_free; + } + err = ops->configure(rule, skb, frh, tb); if (err < 0) goto errout_free; @@ -469,6 +504,14 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh) (rule->mark_mask != nla_get_u32(tb[FRA_FWMASK]))) continue; + if (tb[FRA_UID_START] && + !uid_eq(rule->uid_start, fib_nl_uid(tb[FRA_UID_START]))) + continue; + + if (tb[FRA_UID_END] && + !uid_eq(rule->uid_end, fib_nl_uid(tb[FRA_UID_END]))) + continue; + if (!ops->compare(rule, frh, tb)) continue; @@ -525,7 +568,9 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(4) /* FRA_PRIORITY */ + nla_total_size(4) /* FRA_TABLE */ + nla_total_size(4) /* FRA_FWMARK */ - + nla_total_size(4); /* FRA_FWMASK */ + + nla_total_size(4) /* FRA_FWMASK */ + + nla_total_size(4) /* FRA_UID_START */ + + nla_total_size(4); /* FRA_UID_END */ if (ops->nlmsg_payload) payload += ops->nlmsg_payload(rule); @@ -579,7 +624,11 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, ((rule->mark_mask || rule->mark) && nla_put_u32(skb, FRA_FWMASK, rule->mark_mask)) || (rule->target && - nla_put_u32(skb, FRA_GOTO, rule->target))) + nla_put_u32(skb, FRA_GOTO, rule->target)) || + (uid_valid(rule->uid_start) && + nla_put_uid(skb, FRA_UID_START, rule->uid_start)) || + (uid_valid(rule->uid_end) && + nla_put_uid(skb, FRA_UID_END, rule->uid_end))) goto nla_put_failure; if (ops->fill(rule, skb, frh) < 0) goto nla_put_failure; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index c7629a209f9d..ffffeb448ec4 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -531,6 +531,7 @@ const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = { [RTA_METRICS] = { .type = NLA_NESTED }, [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, [RTA_FLOW] = { .type = NLA_U32 }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 442087d371f6..6dfec2f18214 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -422,7 +422,8 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, sk->sk_protocol, flags, (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, - ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); + ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport, + sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) @@ -458,7 +459,8 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, - ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); + ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport, + sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index c2ee385cecfb..8e20e9405582 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1503,7 +1503,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr, RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol, ip_reply_arg_flowi_flags(arg), daddr, saddr, - tcp_hdr(skb)->source, tcp_hdr(skb)->dest); + tcp_hdr(skb)->source, tcp_hdr(skb)->dest, + arg->uid); security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(net, &fl4); if (IS_ERR(rt)) diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 111e5a409594..b83d82951cad 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -768,7 +768,8 @@ int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, - inet_sk_flowi_flags(sk), faddr, saddr, 0, 0); + inet_sk_flowi_flags(sk), faddr, saddr, 0, 0, + sock_i_uid(sk)); security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); rt = ip_route_output_flow(net, &fl4, sk); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index dd44e0ab600c..b8287330c579 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -572,7 +572,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, RT_SCOPE_UNIVERSE, inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP, - daddr, saddr, 0, 0); + daddr, saddr, 0, 0, + sock_i_uid(sk)); if (!inet->hdrincl) { err = raw_probe_proto_opt(&fl4, msg); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index c04359196ebc..ef2fe9d2e9e6 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -500,7 +500,7 @@ void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more) } EXPORT_SYMBOL(__ip_select_ident); -static void __build_flow_key(struct flowi4 *fl4, const struct sock *sk, +static void __build_flow_key(struct flowi4 *fl4, struct sock *sk, const struct iphdr *iph, int oif, u8 tos, u8 prot, u32 mark, int flow_flags) @@ -516,11 +516,12 @@ static void __build_flow_key(struct flowi4 *fl4, const struct sock *sk, flowi4_init_output(fl4, oif, mark, tos, RT_SCOPE_UNIVERSE, prot, flow_flags, - iph->daddr, iph->saddr, 0, 0); + iph->daddr, iph->saddr, 0, 0, + sock_i_uid(sk)); } static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, - const struct sock *sk) + struct sock *sk) { const struct iphdr *iph = ip_hdr(skb); int oif = skb->dev->ifindex; @@ -531,7 +532,7 @@ static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, __build_flow_key(fl4, sk, iph, oif, tos, prot, mark, 0); } -static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) +static void build_sk_flow_key(struct flowi4 *fl4, struct sock *sk) { const struct inet_sock *inet = inet_sk(sk); const struct ip_options_rcu *inet_opt; @@ -545,11 +546,12 @@ static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, inet_sk_flowi_flags(sk), - daddr, inet->inet_saddr, 0, 0); + daddr, inet->inet_saddr, 0, 0, + sock_i_uid(sk)); rcu_read_unlock(); } -static void ip_rt_build_flow_key(struct flowi4 *fl4, const struct sock *sk, +static void ip_rt_build_flow_key(struct flowi4 *fl4, struct sock *sk, const struct sk_buff *skb) { if (skb) @@ -2287,6 +2289,11 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, nla_put_u32(skb, RTA_MARK, fl4->flowi4_mark)) goto nla_put_failure; + if (!uid_eq(fl4->flowi4_uid, INVALID_UID) && + nla_put_u32(skb, RTA_UID, + from_kuid_munged(current_user_ns(), fl4->flowi4_uid))) + goto nla_put_failure; + error = rt->dst.error; if (rt_is_input_route(rt)) { @@ -2336,6 +2343,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) int err; int mark; struct sk_buff *skb; + kuid_t uid; err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy); if (err < 0) @@ -2363,6 +2371,10 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) dst = tb[RTA_DST] ? nla_get_be32(tb[RTA_DST]) : 0; iif = tb[RTA_IIF] ? nla_get_u32(tb[RTA_IIF]) : 0; mark = tb[RTA_MARK] ? nla_get_u32(tb[RTA_MARK]) : 0; + if (tb[RTA_UID]) + uid = make_kuid(current_user_ns(), nla_get_u32(tb[RTA_UID])); + else + uid = (iif ? INVALID_UID : current_uid()); memset(&fl4, 0, sizeof(fl4)); fl4.daddr = dst; @@ -2370,6 +2382,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) fl4.flowi4_tos = rtm->rtm_tos; fl4.flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0; fl4.flowi4_mark = mark; + fl4.flowi4_uid = uid; if (iif) { struct net_device *dev; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 5abb45e281be..c94032b95c60 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -353,7 +353,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP, inet_sk_flowi_flags(sk), (opt && opt->srr) ? opt->faddr : ireq->rmt_addr, - ireq->loc_addr, th->source, th->dest); + ireq->loc_addr, th->source, th->dest, + sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); if (IS_ERR(rt)) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 0bf5d399a03c..35ab330ed958 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -962,7 +962,8 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk)|FLOWI_FLAG_CAN_SLEEP, - faddr, saddr, dport, inet->inet_sport); + faddr, saddr, dport, inet->inet_sport, + sock_i_uid(sk)); security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index a4cfde67fcb7..d29ae19ae698 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -694,6 +694,7 @@ int inet6_sk_rebuild_header(struct sock *sk) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); final_p = fl6_update_dst(&fl6, np->opt, &final); diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index bb02e176cb70..b903e19463c9 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -630,7 +630,7 @@ static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (type == NDISC_REDIRECT) ip6_redirect(skb, net, 0, 0); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); xfrm_state_put(x); } diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 4b56cbbc7890..00b4a5f6eea4 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -162,6 +162,7 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); if (!fl6.flowi6_oif && (addr_type&IPV6_ADDR_MULTICAST)) fl6.flowi6_oif = np->mcast_oif; diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 40ffd72243a4..fdc81cb29e80 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -449,7 +449,7 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (type == NDISC_REDIRECT) ip6_redirect(skb, net, 0, 0); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); xfrm_state_put(x); } diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 28da4003e842..12b1a942dc99 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -90,7 +90,7 @@ static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, struct net *net = dev_net(skb->dev); if (type == ICMPV6_PKT_TOOBIG) - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); else if (type == NDISC_REDIRECT) ip6_redirect(skb, net, 0, 0); diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index f1493138d21e..65a46058c854 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -84,6 +84,7 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, fl6->flowi6_mark = inet_rsk(req)->ir_mark; fl6->fl6_dport = inet_rsk(req)->rmt_port; fl6->fl6_sport = inet_rsk(req)->loc_port; + fl6->flowi6_uid = sock_i_uid(sk); security_req_classify_flow(req, flowi6_to_flowi(fl6)); dst = ip6_dst_lookup_flow(sk, fl6, final_p, false); @@ -211,6 +212,7 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk, fl6->flowi6_mark = sk->sk_mark; fl6->fl6_sport = inet->inet_sport; fl6->fl6_dport = inet->inet_dport; + fl6->flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(fl6)); final_p = fl6_update_dst(fl6, np->opt, &final); diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 7af5aee75d98..a1beb59a841e 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -78,7 +78,7 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (type == NDISC_REDIRECT) ip6_redirect(skb, net, 0, 0); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); xfrm_state_put(x); } diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index d4edfceab36f..38ceca8a6358 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -159,6 +159,7 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, fl6.saddr = np->saddr; fl6.daddr = *daddr; fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); fl6.fl6_icmp_type = user_icmph.icmp6_type; fl6.fl6_icmp_code = user_icmph.icmp6_code; security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index eedff8ccded5..dfef31581f85 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -761,6 +761,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); if (sin6) { if (addr_len < SIN6_LEN_RFC2133) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 8ecf44af7c2e..bad36468dcd7 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1099,7 +1099,7 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, } void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, - int oif, u32 mark) + int oif, u32 mark, kuid_t uid) { const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; struct dst_entry *dst; @@ -1112,6 +1112,7 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; fl6.flowlabel = ip6_flowinfo(iph); + fl6.flowi6_uid = uid; dst = ip6_route_output(net, NULL, &fl6); if (!dst->error) @@ -1123,7 +1124,7 @@ EXPORT_SYMBOL_GPL(ip6_update_pmtu); void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) { ip6_update_pmtu(skb, sock_net(sk), mtu, - sk->sk_bound_dev_if, sk->sk_mark); + sk->sk_bound_dev_if, sk->sk_mark, sock_i_uid(sk)); } EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); @@ -2199,6 +2200,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { [RTA_PRIORITY] = { .type = NLA_U32 }, [RTA_METRICS] = { .type = NLA_NESTED }, [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -2585,6 +2587,12 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh) if (tb[RTA_OIF]) oif = nla_get_u32(tb[RTA_OIF]); + if (tb[RTA_UID]) + fl6.flowi6_uid = make_kuid(current_user_ns(), + nla_get_u32(tb[RTA_UID])); + else + fl6.flowi6_uid = iif ? INVALID_UID : current_uid(); + if (iif) { struct net_device *dev; int flags = 0; diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 1efbc6f44a6a..ba8622daffd7 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -243,6 +243,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) fl6.flowi6_mark = ireq->ir_mark; fl6.fl6_dport = inet_rsk(req)->rmt_port; fl6.fl6_sport = inet_sk(sk)->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); security_req_classify_flow(req, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 6e882dadb4f8..a4fc647deb00 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -252,6 +252,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = usin->sin6_port; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); final_p = fl6_update_dst(&fl6, np->opt, &final); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 42923b14dfa6..e6dd85da9062 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1147,6 +1147,7 @@ int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); if (msg->msg_controllen) { opt = &opt_space; From 7ae573c139c8a91bed58060818a2559526aee741 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 26 Jun 2014 09:26:21 -0700 Subject: [PATCH 0005/1185] net: wireless: Fix cfg80211_vendor_cmd_alloc_reply_skb Change-Id: Ia8da6cdacd5668d10f8955972d996177305b7228 Signed-off-by: Dmitry Shmidt --- include/net/cfg80211.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 304e41381a1f..2ebb168e5a5b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3696,8 +3696,8 @@ void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp); static inline struct sk_buff * cfg80211_vendor_cmd_alloc_reply_skb(struct wiphy *wiphy, int approxlen) { - return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_TESTMODE, - NL80211_ATTR_TESTDATA, approxlen); + return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_VENDOR, + NL80211_ATTR_VENDOR_DATA, approxlen); } /** From 3c9e49908b882631f3737022e707ffddf0b7c230 Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Wed, 25 Jun 2014 19:44:50 +0900 Subject: [PATCH 0006/1185] cpufreq: fix sleeping in atomic context when realloc freq_table for all_time_in_state Commit 40cf2f8 (cpufreq: Persist cpufreq time in state data across hotplug) causes the following call trace to be spit on boot: BUG: sleeping function called from invalid context at mm/slub.c:936 in_atomic(): 1, irqs_disabled(): 0, pid: 1, name: swapper/0 CPU: 6 PID: 1 Comm: swapper/0 Not tainted 3.10.9-20140624.172707-eng-gd6c0f69-dirty #50 Backtrace: [] (dump_backtrace+0x0/0x10c) from [] (show_stack+0x18/0x1c) r6:ffff1788 r5:c0c020c0 r4:e609c000 r3:00000000 [] (show_stack+0x0/0x1c) from [] (dump_stack+0x20/0x28) [] (dump_stack+0x0/0x28) from [] (__might_sleep+0x104/0x120) [] (__might_sleep+0x0/0x120) from [] (__kmalloc_track_caller+0x144/0x274) r6:00000000 r5:e609c000 r4:e6802140 [] (__kmalloc_track_caller+0x0/0x274) from [] (krealloc+0x58/0xb0) [] (krealloc+0x0/0xb0) from [] (cpufreq_allstats_create+0x120/0x204) r8:e4c4ff00 r7:c0d266b8 r6:0013d620 r5:e4c4e600 r4:00000001 r3:e535d6d0 [] (cpufreq_allstats_create+0x0/0x204) from [] (cpufreq_stat_notifier_policy+0xb8/0xd0) [] (cpufreq_stat_notifier_policy+0x0/0xd0) from [] (notifier_call_chain+0x4c/0x8c) r5:00000000 r4:fffffffe [] (notifier_call_chain+0x0/0x8c) from [] (__blocking_notifier_call_chain+0x50/0x68) r8:c0cd4d00 r7:00000002 r6:e609dd7c r5:ffffffff r4:c0d25a4c r3:ffffffff [] (__blocking_notifier_call_chain+0x0/0x68) from [] (blocking_notifier_call_chain+0x20/0x28) r7:c0e24f30 r6:00000000 r5:e53e1e00 r4:e609dd7c [] (blocking_notifier_call_chain+0x0/0x28) from [] (__cpufreq_set_policy+0xc0/0x1d0) [] (__cpufreq_set_policy+0x0/0x1d0) from [] (cpufreq_add_dev_interface+0x20c/0x270) r7:00000008 r6:00000000 r5:e53e1e00 r4:e53e1e58 [] (cpufreq_add_dev_interface+0x0/0x270) from [] (cpufreq_add_dev+0x33c/0x420) [] (cpufreq_add_dev+0x0/0x420) from [] (subsys_interface_register+0x80/0xbc) [] (subsys_interface_register+0x0/0xbc) from [] (cpufreq_register_driver+0x8c/0x194) Change-Id: If77a656d0ea60a8fc4083283d104509fa6c07f8f Signed-off-by: Minsung Kim --- drivers/cpufreq/cpufreq_stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 3f5e279ff9d8..7a2bcac3ad7f 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -424,7 +424,7 @@ static void add_all_freq_table(unsigned int freq) unsigned int size; size = sizeof(unsigned int) * (all_freq_table->table_size + 1); all_freq_table->freq_table = krealloc(all_freq_table->freq_table, - size, GFP_KERNEL); + size, GFP_ATOMIC); if (IS_ERR(all_freq_table->freq_table)) { pr_warn("Could not reallocate memory for freq_table\n"); all_freq_table->freq_table = NULL; From 8d0e99be244a647f8f0dd8741238446088e1c30f Mon Sep 17 00:00:00 2001 From: Yann Soubeyrand Date: Wed, 18 Jun 2014 14:57:29 +0200 Subject: [PATCH 0007/1185] net: wireless: fix misplaced #endif in net/wireless/nl80211.c The patch "nl80211: cumulative vendor command support patch" introduced compilation error in file net/wireless/nl80211.c. The nl80211_vendor_mcgrp variable is defined only if the CONFIG_NL80211_TESTMODE preprocessor constant is defined. However, this variable is later used wether CONFIG_NL80211_TESTMODE is defined or not. The cause is a misplaced #endif. Change-Id: I466488285578d57e6554a1f8ebe71d4f3385ecf2 Signed-off-by: Dmitry Shmidt --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 150a38fc346e..ec1356fab67e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10973,11 +10973,11 @@ int nl80211_init(void) err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp); if (err) goto err_out; -#endif err = genl_register_mc_group(&nl80211_fam, &nl80211_vendor_mcgrp); if (err) goto err_out; +#endif err = netlink_register_notifier(&nl80211_netlink_notifier); if (err) From dd979cc254c3aeec85927dbaad669d1ecd193c6f Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 1 Jul 2014 14:48:15 -0700 Subject: [PATCH 0008/1185] net: cfg80211: Fix wiphy_vendor_command 'doit' type Change-Id: I5b1732eed7ac4f6bc267b4baa2153f6de2e16dc8 Signed-off-by: Dmitry Shmidt --- include/net/cfg80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 2ebb168e5a5b..d9681a288ce6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2497,7 +2497,7 @@ struct wiphy_vendor_command { struct nl80211_vendor_cmd_info info; u32 flags; int (*doit)(struct wiphy *wiphy, struct wireless_dev *wdev, - void *data, int data_len); + const void *data, int data_len); }; /** From 70bcc368f145d0871379f5ffe0428e65e10345e1 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 27 Jun 2014 16:39:35 -0700 Subject: [PATCH 0009/1185] input: Made keyreset more robust Switched do_restart to run in a seperate workqueue to handle cases where kernel_restart hangs. Change-Id: I1ecd61f8d0859f1a86d37c692351d644b5db9c69 Signed-off-by: Daniel Rosenberg --- drivers/input/keyreset.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c index eaaccde82210..7fbf7247e65f 100644 --- a/drivers/input/keyreset.c +++ b/drivers/input/keyreset.c @@ -27,9 +27,10 @@ struct keyreset_state { int restart_requested; int (*reset_fn)(void); struct platform_device *pdev_child; + struct work_struct restart_work; }; -static void do_restart(void) +static void do_restart(struct work_struct *unused) { sys_sync(); kernel_restart(NULL); @@ -44,7 +45,7 @@ static void do_reset_fn(void *priv) state->restart_requested = state->reset_fn(); } else { pr_info("keyboard reset\n"); - do_restart(); + schedule_work(&state->restart_work); state->restart_requested = 1; } } @@ -69,6 +70,7 @@ static int keyreset_probe(struct platform_device *pdev) if (!state->pdev_child) return -ENOMEM; state->pdev_child->dev.parent = &pdev->dev; + INIT_WORK(&state->restart_work, do_restart); keyp = pdata->keys_down; while ((key = *keyp++)) { From 455b09d66a9ccfc572497ae88375ae343ff9ae66 Mon Sep 17 00:00:00 2001 From: Sreeram Ramachandran Date: Tue, 8 Jul 2014 11:57:14 -0700 Subject: [PATCH 0010/1185] Handle 'sk' being NULL in UID-based routing. Bug: 15413527 Change-Id: Iab1fae9da6053b284591628ef1de878761b137b1 Signed-off-by: Sreeram Ramachandran --- include/net/route.h | 2 +- net/ipv4/route.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net/route.h b/include/net/route.h index b5b44875543e..647bb2adbffd 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -142,7 +142,7 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos, RT_SCOPE_UNIVERSE, proto, sk ? inet_sk_flowi_flags(sk) : 0, - daddr, saddr, dport, sport, sock_i_uid(sk)); + daddr, saddr, dport, sport, sk ? sock_i_uid(sk) : 0); if (sk) security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index ef2fe9d2e9e6..42cd979d1633 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -517,7 +517,7 @@ static void __build_flow_key(struct flowi4 *fl4, struct sock *sk, RT_SCOPE_UNIVERSE, prot, flow_flags, iph->daddr, iph->saddr, 0, 0, - sock_i_uid(sk)); + sk ? sock_i_uid(sk) : 0); } static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, From f8fe2735daaf662876d1333075991997c04d5359 Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Mon, 23 Jun 2014 19:07:44 +0800 Subject: [PATCH 0011/1185] usb: gadget: f_accessory: Enabled Zero Length Packet (ZLP) for acc_write Accessory connected to Android Device requires Zero Length Packet (ZLP) to be written when data transferred out from the Android device are multiples of wMaxPacketSize (64bytes (Full-Speed) / 512bytes (High-Speed)) to end the transfer. Change-Id: Ib2c2c0ab98ef9afa10e74a720142deca5c0ed476 Signed-off-by: Anson Jacob --- drivers/usb/gadget/f_accessory.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 53e50b5e8612..a401acdceb4d 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -662,10 +662,17 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, break; } - if (count > BULK_BUFFER_SIZE) + if (count > BULK_BUFFER_SIZE) { xfer = BULK_BUFFER_SIZE; - else + /* ZLP, They will be more TX requests so not yet. */ + req->zero = 0; + } else { xfer = count; + /* If the data length is a multple of the + * maxpacket size then send a zero length packet(ZLP). + */ + req->zero = ((xfer % dev->ep_in->maxpacket) == 0); + } if (copy_from_user(req->buf, buf, xfer)) { r = -EFAULT; break; From 2ca27aef6f907c33d85915df5f37ddee32a92742 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:29:55 +0200 Subject: [PATCH 0012/1185] HID: validate HID report id size The "Report ID" field of a HID report is used to build indexes of reports. The kernel's index of these is limited to 256 entries, so any malicious device that sets a Report ID greater than 255 will trigger memory corruption on the host: [ 1347.156239] BUG: unable to handle kernel paging request at ffff88094958a878 [ 1347.156261] IP: [] hid_register_report+0x2a/0x8b CVE-2013-2888 Signed-off-by: Kees Cook Cc: stable@kernel.org Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 10 +++++++--- include/linux/hid.h | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 264f55099940..95a97a44fe74 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -63,6 +63,8 @@ struct hid_report *hid_register_report(struct hid_device *device, unsigned type, struct hid_report_enum *report_enum = device->report_enum + type; struct hid_report *report; + if (id >= HID_MAX_IDS) + return NULL; if (report_enum->report_id_hash[id]) return report_enum->report_id_hash[id]; @@ -404,8 +406,10 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_ID: parser->global.report_id = item_udata(item); - if (parser->global.report_id == 0) { - hid_err(parser->device, "report_id 0 is invalid\n"); + if (parser->global.report_id == 0 || + parser->global.report_id >= HID_MAX_IDS) { + hid_err(parser->device, "report_id %u is invalid\n", + parser->global.report_id); return -1; } return 0; @@ -575,7 +579,7 @@ static void hid_close_report(struct hid_device *device) for (i = 0; i < HID_REPORT_TYPES; i++) { struct hid_report_enum *report_enum = device->report_enum + i; - for (j = 0; j < 256; j++) { + for (j = 0; j < HID_MAX_IDS; j++) { struct hid_report *report = report_enum->report_id_hash[j]; if (report) hid_free_report(report); diff --git a/include/linux/hid.h b/include/linux/hid.h index 8136c6d99037..95b6ddf0fa45 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -393,10 +393,12 @@ struct hid_report { struct hid_device *device; /* associated device */ }; +#define HID_MAX_IDS 256 + struct hid_report_enum { unsigned numbered; struct list_head report_list; - struct hid_report *report_id_hash[256]; + struct hid_report *report_id_hash[HID_MAX_IDS]; }; #define HID_REPORT_TYPES 3 From b8b84374b4876b2a62187102392f72a8beaf217e Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 11 Sep 2013 21:56:50 +0200 Subject: [PATCH 0013/1185] HID: provide a helper for validating hid reports Many drivers need to validate the characteristics of their HID report during initialization to avoid misusing the reports. This adds a common helper to perform validation of the report exisitng, the field existing, and the expected number of values within the field. Signed-off-by: Kees Cook Cc: stable@vger.kernel.org Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 58 ++++++++++++++++++++++++++++++++++++++++++ include/linux/hid.h | 4 +++ 2 files changed, 62 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 95a97a44fe74..c6d42deb9163 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -759,6 +759,64 @@ int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size) } EXPORT_SYMBOL_GPL(hid_parse_report); +static const char * const hid_report_names[] = { + "HID_INPUT_REPORT", + "HID_OUTPUT_REPORT", + "HID_FEATURE_REPORT", +}; +/** + * hid_validate_values - validate existing device report's value indexes + * + * @device: hid device + * @type: which report type to examine + * @id: which report ID to examine (0 for first) + * @field_index: which report field to examine + * @report_counts: expected number of values + * + * Validate the number of values in a given field of a given report, after + * parsing. + */ +struct hid_report *hid_validate_values(struct hid_device *hid, + unsigned int type, unsigned int id, + unsigned int field_index, + unsigned int report_counts) +{ + struct hid_report *report; + + if (type > HID_FEATURE_REPORT) { + hid_err(hid, "invalid HID report type %u\n", type); + return NULL; + } + + if (id >= HID_MAX_IDS) { + hid_err(hid, "invalid HID report id %u\n", id); + return NULL; + } + + /* + * Explicitly not using hid_get_report() here since it depends on + * ->numbered being checked, which may not always be the case when + * drivers go to access report values. + */ + report = hid->report_enum[type].report_id_hash[id]; + if (!report) { + hid_err(hid, "missing %s %u\n", hid_report_names[type], id); + return NULL; + } + if (report->maxfield <= field_index) { + hid_err(hid, "not enough fields in %s %u\n", + hid_report_names[type], id); + return NULL; + } + if (report->field[field_index]->report_count < report_counts) { + hid_err(hid, "not enough values in %s %u field %u\n", + hid_report_names[type], id, field_index); + return NULL; + } + return report; +} +EXPORT_SYMBOL_GPL(hid_validate_values); + /** * hid_open_report - open a driver-specific device report * diff --git a/include/linux/hid.h b/include/linux/hid.h index 95b6ddf0fa45..1f3c5f7b3bc5 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -749,6 +749,10 @@ void hid_output_report(struct hid_report *report, __u8 *data); struct hid_device *hid_allocate_device(void); struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); +struct hid_report *hid_validate_values(struct hid_device *hid, + unsigned int type, unsigned int id, + unsigned int field_index, + unsigned int report_counts); int hid_open_report(struct hid_device *device); int hid_check_keys_pressed(struct hid_device *hid); int hid_connect(struct hid_device *hid, unsigned int connect_mask); From 728a564fa767d186ea1f95fbf81b43e9d865dc0b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 11 Sep 2013 21:56:51 +0200 Subject: [PATCH 0014/1185] HID: zeroplus: validate output report details The zeroplus HID driver was not checking the size of allocated values in fields it used. A HID device could send a malicious output report that would cause the driver to write beyond the output report allocation during initialization, causing a heap overflow: [ 1442.728680] usb 1-1: New USB device found, idVendor=0c12, idProduct=0005 ... [ 1466.243173] BUG kmalloc-192 (Tainted: G W ): Redzone overwritten CVE-2013-2889 Signed-off-by: Kees Cook Cc: stable@vger.kernel.org Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-zpff.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c index 6ec28a37c146..a29756c6ca02 100644 --- a/drivers/hid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -68,21 +68,13 @@ static int zpff_init(struct hid_device *hid) struct hid_report *report; struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = - &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; - int error; + int i, error; - if (list_empty(report_list)) { - hid_err(hid, "no output report found\n"); - return -ENODEV; - } - - report = list_entry(report_list->next, struct hid_report, list); - - if (report->maxfield < 4) { - hid_err(hid, "not enough fields in report\n"); - return -ENODEV; + for (i = 0; i < 4; i++) { + report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1); + if (!report) + return -ENODEV; } zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL); From 7e62de4584a6ad505e164ae92c9c1571da46c9bb Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 11 Sep 2013 21:56:53 +0200 Subject: [PATCH 0015/1185] HID: steelseries: validate output report details A HID device could send a malicious output report that would cause the steelseries HID driver to write beyond the output report allocation during initialization, causing a heap overflow: [ 167.981534] usb 1-1: New USB device found, idVendor=1038, idProduct=1410 ... [ 182.050547] BUG kmalloc-256 (Tainted: G W ): Redzone overwritten CVE-2013-2891 Signed-off-by: Kees Cook Cc: stable@vger.kernel.org Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-steelseries.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index d16491192112..29f328f411fb 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -249,6 +249,11 @@ static int steelseries_srws1_probe(struct hid_device *hdev, goto err_free; } + if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 16)) { + ret = -ENODEV; + goto err_free; + } + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); From e3c850750a74e04855fc61e9f5d2854eeca8087c Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:30:49 +0200 Subject: [PATCH 0016/1185] HID: pantherlord: validate output report details A HID device could send a malicious output report that would cause the pantherlord HID driver to write beyond the output report allocation during initialization, causing a heap overflow: [ 310.939483] usb 1-1: New USB device found, idVendor=0e8f, idProduct=0003 ... [ 315.980774] BUG kmalloc-192 (Tainted: G W ): Redzone overwritten CVE-2013-2892 Signed-off-by: Kees Cook Cc: stable@kernel.org Signed-off-by: Jiri Kosina --- drivers/hid/hid-pl.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index d29112fa5cd5..2dcd7d98dbd6 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -132,8 +132,14 @@ static int plff_init(struct hid_device *hid) strong = &report->field[0]->value[2]; weak = &report->field[0]->value[3]; debug("detected single-field device"); - } else if (report->maxfield >= 4 && report->field[0]->maxusage == 1 && - report->field[0]->usage[0].hid == (HID_UP_LED | 0x43)) { + } else if (report->field[0]->maxusage == 1 && + report->field[0]->usage[0].hid == + (HID_UP_LED | 0x43) && + report->maxfield >= 4 && + report->field[0]->report_count >= 1 && + report->field[1]->report_count >= 1 && + report->field[2]->report_count >= 1 && + report->field[3]->report_count >= 1) { report->field[0]->value[0] = 0x00; report->field[1]->value[0] = 0x00; strong = &report->field[2]->value[0]; From d87aff426add54f65131d450a06db618742a7dfe Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 11 Sep 2013 21:56:54 +0200 Subject: [PATCH 0017/1185] HID: LG: validate HID output report details A HID device could send a malicious output report that would cause the lg, lg3, and lg4 HID drivers to write beyond the output report allocation during an event, causing a heap overflow: [ 325.245240] usb 1-1: New USB device found, idVendor=046d, idProduct=c287 ... [ 414.518960] BUG kmalloc-4096 (Not tainted): Redzone overwritten Additionally, while lg2 did correctly validate the report details, it was cleaned up and shortened. CVE-2013-2893 Signed-off-by: Kees Cook Cc: stable@vger.kernel.org Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-lg2ff.c | 19 +++---------------- drivers/hid/hid-lg3ff.c | 29 ++++++----------------------- drivers/hid/hid-lg4ff.c | 20 +------------------- drivers/hid/hid-lgff.c | 17 ++--------------- 4 files changed, 12 insertions(+), 73 deletions(-) diff --git a/drivers/hid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c index b3cd1507dda2..1a42eaa6ca02 100644 --- a/drivers/hid/hid-lg2ff.c +++ b/drivers/hid/hid-lg2ff.c @@ -64,26 +64,13 @@ int lg2ff_init(struct hid_device *hid) struct hid_report *report; struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = - &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; int error; - if (list_empty(report_list)) { - hid_err(hid, "no output report found\n"); + /* Check that the report looks ok */ + report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7); + if (!report) return -ENODEV; - } - - report = list_entry(report_list->next, struct hid_report, list); - - if (report->maxfield < 1) { - hid_err(hid, "output report is empty\n"); - return -ENODEV; - } - if (report->field[0]->report_count < 7) { - hid_err(hid, "not enough values in the field\n"); - return -ENODEV; - } lg2ff = kmalloc(sizeof(struct lg2ff_device), GFP_KERNEL); if (!lg2ff) diff --git a/drivers/hid/hid-lg3ff.c b/drivers/hid/hid-lg3ff.c index e52f181f6aa1..8c2da183d3bc 100644 --- a/drivers/hid/hid-lg3ff.c +++ b/drivers/hid/hid-lg3ff.c @@ -66,10 +66,11 @@ static int hid_lg3ff_play(struct input_dev *dev, void *data, int x, y; /* - * Maxusage should always be 63 (maximum fields) - * likely a better way to ensure this data is clean + * Available values in the field should always be 63, but we only use up to + * 35. Instead, clear the entire area, however big it is. */ - memset(report->field[0]->value, 0, sizeof(__s32)*report->field[0]->maxusage); + memset(report->field[0]->value, 0, + sizeof(__s32) * report->field[0]->report_count); switch (effect->type) { case FF_CONSTANT: @@ -129,32 +130,14 @@ static const signed short ff3_joystick_ac[] = { int lg3ff_init(struct hid_device *hid) { struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; - struct hid_report *report; - struct hid_field *field; const signed short *ff_bits = ff3_joystick_ac; int error; int i; - /* Find the report to use */ - if (list_empty(report_list)) { - hid_err(hid, "No output report found\n"); - return -1; - } - /* Check that the report looks ok */ - report = list_entry(report_list->next, struct hid_report, list); - if (!report) { - hid_err(hid, "NULL output report\n"); - return -1; - } - - field = report->field[0]; - if (!field) { - hid_err(hid, "NULL field\n"); - return -1; - } + if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 35)) + return -ENODEV; /* Assume single fixed device G940 */ for (i = 0; ff_bits[i] >= 0; i++) diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 0ddae2a00d59..8782fe1aaa07 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -484,34 +484,16 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde int lg4ff_init(struct hid_device *hid) { struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; - struct hid_report *report; - struct hid_field *field; struct lg4ff_device_entry *entry; struct lg_drv_data *drv_data; struct usb_device_descriptor *udesc; int error, i, j; __u16 bcdDevice, rev_maj, rev_min; - /* Find the report to use */ - if (list_empty(report_list)) { - hid_err(hid, "No output report found\n"); - return -1; - } - /* Check that the report looks ok */ - report = list_entry(report_list->next, struct hid_report, list); - if (!report) { - hid_err(hid, "NULL output report\n"); + if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7)) return -1; - } - - field = report->field[0]; - if (!field) { - hid_err(hid, "NULL field\n"); - return -1; - } /* Check what wheel has been connected */ for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) { diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c index d7ea8c845b40..e1394af0ae7b 100644 --- a/drivers/hid/hid-lgff.c +++ b/drivers/hid/hid-lgff.c @@ -128,27 +128,14 @@ static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude) int lgff_init(struct hid_device* hid) { struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; - struct hid_report *report; - struct hid_field *field; const signed short *ff_bits = ff_joystick; int error; int i; - /* Find the report to use */ - if (list_empty(report_list)) { - hid_err(hid, "No output report found\n"); - return -1; - } - /* Check that the report looks ok */ - report = list_entry(report_list->next, struct hid_report, list); - field = report->field[0]; - if (!field) { - hid_err(hid, "NULL field\n"); - return -1; - } + if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7)) + return -ENODEV; for (i = 0; i < ARRAY_SIZE(devices); i++) { if (dev->id.vendor == devices[i].idVendor && From 255b0df27b1f2b590226f29a78169cc529e74cf0 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 11 Sep 2013 21:56:55 +0200 Subject: [PATCH 0018/1185] HID: lenovo-tpkbd: validate output report details A HID device could send a malicious output report that would cause the lenovo-tpkbd HID driver to write just beyond the output report allocation during initialization, causing a heap overflow: [ 76.109807] usb 1-1: New USB device found, idVendor=17ef, idProduct=6009 ... [ 80.462540] BUG kmalloc-192 (Tainted: G W ): Redzone overwritten CVE-2013-2894 Signed-off-by: Kees Cook Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-lenovo-tpkbd.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c index 07837f5a4eb8..762d988548a2 100644 --- a/drivers/hid/hid-lenovo-tpkbd.c +++ b/drivers/hid/hid-lenovo-tpkbd.c @@ -339,7 +339,15 @@ static int tpkbd_probe_tp(struct hid_device *hdev) struct tpkbd_data_pointer *data_pointer; size_t name_sz = strlen(dev_name(dev)) + 16; char *name_mute, *name_micmute; - int ret; + int i, ret; + + /* Validate required reports. */ + for (i = 0; i < 4; i++) { + if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1)) + return -ENODEV; + } + if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2)) + return -ENODEV; if (sysfs_create_group(&hdev->dev.kobj, &tpkbd_attr_group_pointer)) { From 142ae08aba82c8226779d6cc8cddc95a83923d77 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 11 Sep 2013 21:56:56 +0200 Subject: [PATCH 0019/1185] HID: logitech-dj: validate output report details A HID device could send a malicious output report that would cause the logitech-dj HID driver to leak kernel memory contents to the device, or trigger a NULL dereference during initialization: [ 304.424553] usb 1-1: New USB device found, idVendor=046d, idProduct=c52b ... [ 304.780467] BUG: unable to handle kernel NULL pointer dereference at 0000000000000028 [ 304.781409] IP: [] logi_dj_recv_send_report.isra.11+0x1a/0x90 CVE-2013-2895 Signed-off-by: Kees Cook Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 5207591a598c..0522b80eab5a 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -421,7 +421,7 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev, struct hid_report *report; struct hid_report_enum *output_report_enum; u8 *data = (u8 *)(&dj_report->device_index); - int i; + unsigned int i; output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; @@ -431,7 +431,7 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev, return -ENODEV; } - for (i = 0; i < report->field[0]->report_count; i++) + for (i = 0; i < DJREPORT_SHORT_LENGTH - 1; i++) report->field[0]->value[i] = data[i]; hid_hw_request(hdev, report, HID_REQ_SET_REPORT); @@ -738,6 +738,12 @@ static int logi_dj_probe(struct hid_device *hdev, goto hid_parse_fail; } + if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, REPORT_ID_DJ_SHORT, + 0, DJREPORT_SHORT_LENGTH - 1)) { + retval = -ENODEV; + goto hid_parse_fail; + } + /* Starts the usb device and connects to upper interfaces hiddev and * hidraw */ retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); From 03507a663cef349083e8d1abc4b9e9b1970a16e8 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:31:28 +0200 Subject: [PATCH 0020/1185] HID: ntrig: validate feature report details A HID device could send a malicious feature report that would cause the ntrig HID driver to trigger a NULL dereference during initialization: [57383.031190] usb 3-1: New USB device found, idVendor=1b96, idProduct=0001 ... [57383.315193] BUG: unable to handle kernel NULL pointer dereference at 0000000000000030 [57383.315308] IP: [] ntrig_probe+0x25e/0x420 [hid_ntrig] CVE-2013-2896 Signed-off-by: Kees Cook Cc: stable@kernel.org Signed-off-by: Rafi Rubin Signed-off-by: Jiri Kosina --- drivers/hid/hid-ntrig.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index ef95102515e4..5482156ab4de 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -115,7 +115,8 @@ static inline int ntrig_get_mode(struct hid_device *hdev) struct hid_report *report = hdev->report_enum[HID_FEATURE_REPORT]. report_id_hash[0x0d]; - if (!report) + if (!report || report->maxfield < 1 || + report->field[0]->report_count < 1) return -EINVAL; hid_hw_request(hdev, report, HID_REQ_GET_REPORT); From 9b24c9c51653d4400d9a4aea2dc28124dce2893c Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 11 Sep 2013 21:56:57 +0200 Subject: [PATCH 0021/1185] HID: validate feature and input report details When dealing with usage_index, be sure to properly use unsigned instead of int to avoid overflows. When working on report fields, always validate that their report_counts are in bounds. Without this, a HID device could report a malicious feature report that could trick the driver into a heap overflow: [ 634.885003] usb 1-1: New USB device found, idVendor=0596, idProduct=0500 ... [ 676.469629] BUG kmalloc-192 (Tainted: G W ): Redzone overwritten CVE-2013-2897 Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Acked-by: Kees Cook Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 16 +++++++--------- drivers/hid/hid-input.c | 11 ++++++++++- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index c6d42deb9163..eadcc85e5a62 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -94,7 +94,6 @@ EXPORT_SYMBOL_GPL(hid_register_report); static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values) { struct hid_field *field; - int i; if (report->maxfield == HID_MAX_FIELDS) { hid_err(report->device, "too many fields in report\n"); @@ -113,9 +112,6 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned field->value = (s32 *)(field->usage + usages); field->report = report; - for (i = 0; i < usages; i++) - field->usage[i].usage_index = i; - return field; } @@ -226,9 +222,9 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign { struct hid_report *report; struct hid_field *field; - int usages; + unsigned usages; unsigned offset; - int i; + unsigned i; report = hid_register_report(parser->device, report_type, parser->global.report_id); if (!report) { @@ -255,7 +251,8 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign if (!parser->local.usage_index) /* Ignore padding fields */ return 0; - usages = max_t(int, parser->local.usage_index, parser->global.report_count); + usages = max_t(unsigned, parser->local.usage_index, + parser->global.report_count); field = hid_register_field(report, usages, parser->global.report_count); if (!field) @@ -266,13 +263,14 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); for (i = 0; i < usages; i++) { - int j = i; + unsigned j = i; /* Duplicate the last usage we parsed if we have excess values */ if (i >= parser->local.usage_index) j = parser->local.usage_index - 1; field->usage[i].hid = parser->local.usage[j]; field->usage[i].collection_index = parser->local.collection_index[j]; + field->usage[i].usage_index = i; } field->maxusage = usages; @@ -1290,7 +1288,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, goto out; } - if (hid->claimed != HID_CLAIMED_HIDRAW) { + if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) { for (a = 0; a < report->maxfield; a++) hid_input_field(hid, report->field[a], cdata, interrupt); hdrv = hid->driver; diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 2df9cdcba0de..762b2cff72b1 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -477,6 +477,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel if (field->flags & HID_MAIN_ITEM_CONSTANT) goto ignore; + /* Ignore if report count is out of bounds. */ + if (field->report_count < 1) + goto ignore; + /* only LED usages are supported in output fields */ if (field->report_type == HID_OUTPUT_REPORT && (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { @@ -1172,7 +1176,11 @@ static void report_features(struct hid_device *hid) rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; list_for_each_entry(rep, &rep_enum->report_list, list) - for (i = 0; i < rep->maxfield; i++) + for (i = 0; i < rep->maxfield; i++) { + /* Ignore if report count is out of bounds. */ + if (rep->field[i]->report_count < 1) + continue; + for (j = 0; j < rep->field[i]->maxusage; j++) { /* Verify if Battery Strength feature is available */ hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]); @@ -1181,6 +1189,7 @@ static void report_features(struct hid_device *hid) drv->feature_mapping(hid, rep->field[i], rep->field[i]->usage + j); } + } } static struct hid_input *hidinput_allocate(struct hid_device *hid) From 8d6fa24bc8f6f4441c4bb29f6b7b44393a5c9319 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 11 Sep 2013 21:56:58 +0200 Subject: [PATCH 0022/1185] HID: multitouch: validate indexes details When working on report indexes, always validate that they are in bounds. Without this, a HID device could report a malicious feature report that could trick the driver into a heap overflow: [ 634.885003] usb 1-1: New USB device found, idVendor=0596, idProduct=0500 ... [ 676.469629] BUG kmalloc-192 (Tainted: G W ): Redzone overwritten Note that we need to change the indexes from s8 to s16 as they can be between -1 and 255. CVE-2013-2897 Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Acked-by: Kees Cook Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index f4b77f4d5fb8..bb6fe3ee0030 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -101,9 +101,9 @@ struct mt_device { unsigned last_slot_field; /* the last field of a slot */ unsigned mt_report_id; /* the report ID of the multitouch device */ unsigned pen_report_id; /* the report ID of the pen device */ - __s8 inputmode; /* InputMode HID feature, -1 if non-existent */ - __s8 inputmode_index; /* InputMode HID feature index in the report */ - __s8 maxcontact_report_id; /* Maximum Contact Number HID feature, + __s16 inputmode; /* InputMode HID feature, -1 if non-existent */ + __s16 inputmode_index; /* InputMode HID feature index in the report */ + __s16 maxcontact_report_id; /* Maximum Contact Number HID feature, -1 if non-existent */ __u8 num_received; /* how many contacts we received */ __u8 num_expected; /* expected last contact index */ @@ -317,20 +317,18 @@ static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { struct mt_device *td = hid_get_drvdata(hdev); - int i; switch (usage->hid) { case HID_DG_INPUTMODE: - td->inputmode = field->report->id; - td->inputmode_index = 0; /* has to be updated below */ - - for (i=0; i < field->maxusage; i++) { - if (field->usage[i].hid == usage->hid) { - td->inputmode_index = i; - break; - } + /* Ignore if value index is out of bounds. */ + if (usage->usage_index >= field->report_count) { + dev_err(&hdev->dev, "HID_DG_INPUTMODE out of range\n"); + break; } + td->inputmode = field->report->id; + td->inputmode_index = usage->usage_index; + break; case HID_DG_CONTACTMAX: td->maxcontact_report_id = field->report->id; @@ -546,6 +544,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, mt_store_field(usage, td, hi); return 1; case HID_DG_CONTACTCOUNT: + /* Ignore if indexes are out of bounds. */ + if (field->index >= field->report->maxfield || + usage->usage_index >= field->report_count) + return 1; td->cc_index = field->index; td->cc_value_index = usage->usage_index; return 1; From b73eb9e6927df1072e7420dedec4fb917d505fa5 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:31:44 +0200 Subject: [PATCH 0023/1185] HID: sensor-hub: validate feature report details A HID device could send a malicious feature report that would cause the sensor-hub HID driver to read past the end of heap allocation, leaking kernel memory contents to the caller. CVE-2013-2898 Signed-off-by: Kees Cook Cc: stable@kernel.org Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-sensor-hub.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index ca7498107327..aa34755ca205 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -221,7 +221,8 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, mutex_lock(&data->mutex); report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); - if (!report || (field_index >= report->maxfield)) { + if (!report || (field_index >= report->maxfield) || + report->field[field_index]->report_count < 1) { ret = -EINVAL; goto done_proc; } From 5e1624f591cc2acbc02d88ab6f1008a57cfb129f Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:31:52 +0200 Subject: [PATCH 0024/1185] HID: picolcd_core: validate output report details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A HID device could send a malicious output report that would cause the picolcd HID driver to trigger a NULL dereference during attr file writing. [jkosina@suse.cz: changed report->maxfield < 1 to report->maxfield != 1 as suggested by Bruno]. CVE-2013-2899 Signed-off-by: Kees Cook Cc: stable@kernel.org Reviewed-by: Bruno Prémont Acked-by: Bruno Prémont Signed-off-by: Jiri Kosina --- drivers/hid/hid-picolcd_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index b48092d0e139..acbb021065ec 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -290,7 +290,7 @@ static ssize_t picolcd_operation_mode_store(struct device *dev, buf += 10; cnt -= 10; } - if (!report) + if (!report || report->maxfield != 1) return -EINVAL; while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r')) From 58c62d4474fbeda42851553a546e5cb860a036ea Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:32:01 +0200 Subject: [PATCH 0025/1185] HID: check for NULL field when setting values Defensively check that the field to be worked on is not NULL. Signed-off-by: Kees Cook Cc: stable@kernel.org Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index eadcc85e5a62..5b3e4cf7ca6b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1212,7 +1212,12 @@ EXPORT_SYMBOL_GPL(hid_output_report); int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) { - unsigned size = field->report_size; + unsigned size; + + if (!field) + return -1; + + size = field->report_size; hid_dump_input(field->report->device, field->usage + offset, value); From e947bc2827cf608bd42ec9480e759766e5dfe09c Mon Sep 17 00:00:00 2001 From: Jonathan Hamilton Date: Thu, 17 Jul 2014 15:54:44 -0700 Subject: [PATCH 0026/1185] video: adf: Cleanup sw_sync timeline at adf_device_destroy If a sw_sync timeline was created by ADF (for drivers that do not implement ops->complete_fence) we should clean it up when the ADF device is destroyed. Change-Id: Idd90180fcae56a87111f7d12bdd80190756a6b80 Signed-off-by: Jonathan Hamilton --- drivers/video/adf/adf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/video/adf/adf.c b/drivers/video/adf/adf.c index 231881c2b355..42c30c05826a 100644 --- a/drivers/video/adf/adf.c +++ b/drivers/video/adf/adf.c @@ -613,6 +613,10 @@ void adf_device_destroy(struct adf_device *dev) } mutex_destroy(&dev->post_lock); mutex_destroy(&dev->client_lock); + + if (dev->timeline) + sync_timeline_destroy(&dev->timeline->obj); + adf_obj_destroy(&dev->base, &adf_devices); } EXPORT_SYMBOL(adf_device_destroy); From 30dc17b47c2f5b12279f1db8935ae2e6d35af8ea Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 4 Feb 2014 02:15:32 +0000 Subject: [PATCH 0027/1185] security: select correct default LSM_MMAP_MIN_ADDR on arm on arm64 Binaries compiled for arm may run on arm64 if CONFIG_COMPAT is selected. Set LSM_MMAP_MIN_ADDR to 32768 if ARM64 && COMPAT to prevent selinux failures launching 32-bit static executables that are mapped at 0x8000. Signed-off-by: Colin Cross Acked-by: Will Deacon Acked-by: Eric Paris Acked-by: James Morris Signed-off-by: Catalin Marinas (cherry picked from upstream 3.14 commit 530b099dfe8499d639e7fbcad28c4199e2a720c7) Change-Id: I05d092d3539380e08e7daf0b9d2faae76147b72b Signed-off-by: Dan Willemsen Reviewed-on: http://git-master/r/367837 Signed-off-by: John Stultz --- security/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/Kconfig b/security/Kconfig index e9c6ac724fef..beb86b500adf 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -103,7 +103,7 @@ config INTEL_TXT config LSM_MMAP_MIN_ADDR int "Low address space for LSM to protect from user allocation" depends on SECURITY && SECURITY_SELINUX - default 32768 if ARM + default 32768 if ARM || (ARM64 && COMPAT) default 65536 help This is the portion of low virtual memory which should be protected From 5f4a7e5e43293f27e0b5ff935b32ba303c89de59 Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Tue, 1 Jul 2014 18:17:20 +0800 Subject: [PATCH 0028/1185] usb: gadget: f_audio_source: change max ISO packet size Re-applying from https://gitorious.org/shr/linux/commit/eb4c9d2db894c3492c0a848581bd4f6790f93d5f Most USB-AUDIO devices are limited to 256 byte for max iso buffer size. If a IN_EP_MAX_PACKET_SIZE is bigger than a USB-AUDIO device's max iso buffer size, it will cause noise. This patch will prevent this case as possibe by reducing packet size. When using 44.1khz, 2ch, 16bit audio data, if max packet size is bigger than 176 bytes, it's no problem. Credits to: Iliyan Malchev Change-Id: Ic2a1c19ea65d5fb42bf12926b51b255b465d7215 Signed-off-by: Anson Jacob --- drivers/usb/gadget/f_audio_source.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 56dcf217cfe5..65760c42d422 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -24,7 +24,7 @@ #define SAMPLE_RATE 44100 #define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) -#define IN_EP_MAX_PACKET_SIZE 384 +#define IN_EP_MAX_PACKET_SIZE 256 /* Number of requests to allocate */ #define IN_EP_REQ_COUNT 4 From dc0cf1216b3db4617610ecee67185de0dade9552 Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Mon, 23 Jun 2014 19:14:01 +0800 Subject: [PATCH 0029/1185] usb: gadget: f_audio_source: Fixed USB Audio Class Interface Descriptor Fixed Android Issue #56549. When both Vendor Class and Audio Class are activated for AOA 2.0, the baInterfaceNr of the AudioControl Interface Descriptor points to wrong interface numbers. They should be pointing to Audio Control Device and Audio Streaming interfaces. Replaced baInterfaceNr with the correct value. Change-Id: Iaa083f3d97c1f0fc9481bf87852b2b51278a6351 Signed-off-by: Anson Jacob --- drivers/usb/gadget/f_audio_source.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 65760c42d422..21ced13c83d8 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -580,12 +580,18 @@ audio_bind(struct usb_configuration *c, struct usb_function *f) goto fail; ac_interface_desc.bInterfaceNumber = status; + /* AUDIO_AC_INTERFACE */ + ac_header_desc.baInterfaceNr[0] = status; + status = usb_interface_id(c, f); if (status < 0) goto fail; as_interface_alt_0_desc.bInterfaceNumber = status; as_interface_alt_1_desc.bInterfaceNumber = status; + /* AUDIO_AS_INTERFACE */ + ac_header_desc.baInterfaceNr[1] = status; + status = -ENODEV; /* allocate our endpoint */ From 42cfa82f8201f91cdb7539a6e15c6ee15eee7f73 Mon Sep 17 00:00:00 2001 From: Gavin Guo Date: Fri, 18 Jul 2014 01:12:13 +0800 Subject: [PATCH 0030/1185] usb: Check if port status is equal to RxDetect commit bb86cf569bbd7ad4dce581a37c7fbd748057e9dc upstream. When using USB 3.0 pen drive with the [AMD] FCH USB XHCI Controller [1022:7814], the second hotplugging will experience the USB 3.0 pen drive is recognized as high-speed device. After bisecting the kernel, I found the commit number 41e7e056cdc662f704fa9262e5c6e213b4ab45dd (USB: Allow USB 3.0 ports to be disabled.) causes the bug. After doing some experiments, the bug can be fixed by avoiding executing the function hub_usb3_port_disable(). Because the port status with [AMD] FCH USB XHCI Controlleris [1022:7814] is already in RxDetect (I tried printing out the port status before setting to Disabled state), it's reasonable to check the port status before really executing hub_usb3_port_disable(). Fixes: 41e7e056cdc6 (USB: Allow USB 3.0 ports to be disabled.) Signed-off-by: Gavin Guo Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 46efdca96952..63c217053668 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -887,6 +887,25 @@ static int hub_usb3_port_disable(struct usb_hub *hub, int port1) if (!hub_is_superspeed(hub->hdev)) return -EINVAL; + ret = hub_port_status(hub, port1, &portstatus, &portchange); + if (ret < 0) + return ret; + + /* + * USB controller Advanced Micro Devices, Inc. [AMD] FCH USB XHCI + * Controller [1022:7814] will have spurious result making the following + * usb 3.0 device hotplugging route to the 2.0 root hub and recognized + * as high-speed device if we set the usb 3.0 port link state to + * Disabled. Since it's already in USB_SS_PORT_LS_RX_DETECT state, we + * check the state here to avoid the bug. + */ + if ((portstatus & USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_RX_DETECT) { + dev_dbg(&hub->ports[port1 - 1]->dev, + "Not disabling port; link state is RxDetect\n"); + return ret; + } + ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED); if (ret) return ret; From 87f7b77e5fe55f27fd705b858eae9858ebdb2327 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Jul 2014 06:20:44 -0300 Subject: [PATCH 0031/1185] media: gspca_pac7302: Add new usb-id for Genius i-Look 317 commit 242841d3d71191348f98310e2d2001e1001d8630 upstream. Tested-and-reported-by: yullaw Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/gspca/pac7302.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c index 6008c8d546a3..20d9c15a305d 100644 --- a/drivers/media/usb/gspca/pac7302.c +++ b/drivers/media/usb/gspca/pac7302.c @@ -945,6 +945,7 @@ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2620)}, {USB_DEVICE(0x093a, 0x2621)}, {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP}, + {USB_DEVICE(0x093a, 0x2623), .driver_info = FL_VFLIP}, {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP}, {USB_DEVICE(0x093a, 0x2625)}, {USB_DEVICE(0x093a, 0x2626)}, From 9ac5d53ca3d2c5b849a57227cc5b0c958692cfdb Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Mon, 7 Jul 2014 16:34:25 -0700 Subject: [PATCH 0032/1185] Drivers: hv: util: Fix a bug in the KVP code commit 9bd2d0dfe4714dd5d7c09a93a5c9ea9e14ceb3fc upstream. Add code to poll the channel since we process only one message at a time and the host may not interrupt us. Also increase the receive buffer size since some KVP messages are close to 8K bytes in size. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 14 ++++++++++++-- drivers/hv/hv_util.c | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index ed50e9e83c61..0e8c1ea4dd53 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -111,6 +111,15 @@ kvp_work_func(struct work_struct *dummy) kvp_respond_to_host(NULL, HV_E_FAIL); } +static void poll_channel(struct vmbus_channel *channel) +{ + unsigned long flags; + + spin_lock_irqsave(&channel->inbound_lock, flags); + hv_kvp_onchannelcallback(channel); + spin_unlock_irqrestore(&channel->inbound_lock, flags); +} + static int kvp_handle_handshake(struct hv_kvp_msg *msg) { int ret = 1; @@ -139,7 +148,7 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg) kvp_register(dm_reg_value); kvp_transaction.active = false; if (kvp_transaction.kvp_context) - hv_kvp_onchannelcallback(kvp_transaction.kvp_context); + poll_channel(kvp_transaction.kvp_context); } return ret; } @@ -552,6 +561,7 @@ kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error) vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, VM_PKT_DATA_INBAND, 0); + poll_channel(channel); } @@ -585,7 +595,7 @@ void hv_kvp_onchannelcallback(void *context) return; } - vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen, + vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 4, &recvlen, &requestid); if (recvlen > 0) { diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 2f561c5dfe24..64c778f7756f 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -279,7 +279,7 @@ static int util_probe(struct hv_device *dev, (struct hv_util_service *)dev_id->driver_data; int ret; - srv->recv_buffer = kmalloc(PAGE_SIZE * 2, GFP_KERNEL); + srv->recv_buffer = kmalloc(PAGE_SIZE * 4, GFP_KERNEL); if (!srv->recv_buffer) return -ENOMEM; if (srv->util_init) { From 18d8867933b9df6b51afa3b5694d82dd88bb46e2 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Mon, 23 Jun 2014 17:42:44 +0200 Subject: [PATCH 0033/1185] Bluetooth: Ignore H5 non-link packets in non-active state commit 48439d501e3d9e8634bdc0c418e066870039599d upstream. When detecting a non-link packet, h5_reset_rx() frees the Rx skb. Not returning after that will cause the upcoming h5_rx_payload() call to dereference a now NULL Rx skb and trigger a kernel oops. Signed-off-by: Loic Poulain Signed-off-by: Marcel Holtmann Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/hci_h5.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index b6154d5a07a5..db0be2fb05fe 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -406,6 +406,7 @@ static int h5_rx_3wire_hdr(struct hci_uart *hu, unsigned char c) H5_HDR_PKT_TYPE(hdr) != HCI_3WIRE_LINK_PKT) { BT_ERR("Non-link packet received in non-active state"); h5_reset_rx(h5); + return 0; } h5->rx_func = h5_rx_payload; From d609df085cf9333cfda02a398d74694e7f2a644c Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 7 Jul 2014 15:28:51 +0200 Subject: [PATCH 0034/1185] fuse: handle large user and group ID commit 233a01fa9c4c7c41238537e8db8434667ff28a2f upstream. If the number in "user_id=N" or "group_id=N" mount options was larger than INT_MAX then fuse returned EINVAL. Fix this to handle all valid uid/gid values. Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/inode.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index b5718516825b..39a986e1da9e 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -461,6 +461,17 @@ static const match_table_t tokens = { {OPT_ERR, NULL} }; +static int fuse_match_uint(substring_t *s, unsigned int *res) +{ + int err = -ENOMEM; + char *buf = match_strdup(s); + if (buf) { + err = kstrtouint(buf, 10, res); + kfree(buf); + } + return err; +} + static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev) { char *p; @@ -471,6 +482,7 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev) while ((p = strsep(&opt, ",")) != NULL) { int token; int value; + unsigned uv; substring_t args[MAX_OPT_ARGS]; if (!*p) continue; @@ -494,18 +506,18 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev) break; case OPT_USER_ID: - if (match_int(&args[0], &value)) + if (fuse_match_uint(&args[0], &uv)) return 0; - d->user_id = make_kuid(current_user_ns(), value); + d->user_id = make_kuid(current_user_ns(), uv); if (!uid_valid(d->user_id)) return 0; d->user_id_present = 1; break; case OPT_GROUP_ID: - if (match_int(&args[0], &value)) + if (fuse_match_uint(&args[0], &uv)) return 0; - d->group_id = make_kgid(current_user_ns(), value); + d->group_id = make_kgid(current_user_ns(), uv); if (!gid_valid(d->group_id)) return 0; d->group_id_present = 1; From 9b87c4e58f2143ba9bc05ffff22d86d172e4f4ac Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 15 Jul 2014 11:05:12 -0400 Subject: [PATCH 0035/1185] tracing: Fix graph tracer with stack tracer on other archs commit 5f8bf2d263a20b986225ae1ed7d6759dc4b93af9 upstream. Running my ftrace tests on PowerPC, it failed the test that checks if function_graph tracer is affected by the stack tracer. It was. Looking into this, I found that the update_function_graph_func() must be called even if the trampoline function is not changed. This is because archs like PowerPC do not support ftrace_ops being passed by assembly and instead uses a helper function (what the trampoline function points to). Since this function is not changed even when multiple ftrace_ops are added to the code, the test that falls out before calling update_function_graph_func() will miss that the update must still be done. Call update_function_graph_function() for all calls to update_ftrace_function() Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/ftrace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 797d3b91a30b..401d9bd1fe42 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -331,12 +331,12 @@ static void update_ftrace_function(void) func = ftrace_ops_list_func; } + update_function_graph_func(); + /* If there's no change, then do nothing more here */ if (ftrace_trace_function == func) return; - update_function_graph_func(); - /* * If we are using the list function, it doesn't care * about the function_trace_ops. From e250100beddf49178bd36886eea77b376a1e39bd Mon Sep 17 00:00:00 2001 From: "zhangwei(Jovi)" Date: Thu, 18 Jul 2013 16:31:05 +0800 Subject: [PATCH 0036/1185] tracing: Add ftrace_trace_stack into __trace_puts/__trace_bputs commit 8abfb8727f4a724d31f9ccfd8013fbd16d539445 upstream. Currently trace option stacktrace is not applicable for trace_printk with constant string argument, the reason is in __trace_puts/__trace_bputs ftrace_trace_stack is missing. In contrast, when using trace_printk with non constant string argument(will call into __trace_printk/__trace_bprintk), then trace option stacktrace is workable, this inconstant result will confuses users a lot. Link: http://lkml.kernel.org/p/51E7A7C9.9040401@huawei.com Signed-off-by: zhangwei(Jovi) Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 8fe92ce43f39..98a830d079b9 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -423,6 +423,9 @@ int __trace_puts(unsigned long ip, const char *str, int size) struct print_entry *entry; unsigned long irq_flags; int alloc; + int pc; + + pc = preempt_count(); if (unlikely(tracing_selftest_running || tracing_disabled)) return 0; @@ -432,7 +435,7 @@ int __trace_puts(unsigned long ip, const char *str, int size) local_save_flags(irq_flags); buffer = global_trace.trace_buffer.buffer; event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, alloc, - irq_flags, preempt_count()); + irq_flags, pc); if (!event) return 0; @@ -449,6 +452,7 @@ int __trace_puts(unsigned long ip, const char *str, int size) entry->buf[size] = '\0'; __buffer_unlock_commit(buffer, event); + ftrace_trace_stack(buffer, irq_flags, 4, pc); return size; } @@ -466,6 +470,9 @@ int __trace_bputs(unsigned long ip, const char *str) struct bputs_entry *entry; unsigned long irq_flags; int size = sizeof(struct bputs_entry); + int pc; + + pc = preempt_count(); if (unlikely(tracing_selftest_running || tracing_disabled)) return 0; @@ -473,7 +480,7 @@ int __trace_bputs(unsigned long ip, const char *str) local_save_flags(irq_flags); buffer = global_trace.trace_buffer.buffer; event = trace_buffer_lock_reserve(buffer, TRACE_BPUTS, size, - irq_flags, preempt_count()); + irq_flags, pc); if (!event) return 0; @@ -482,6 +489,7 @@ int __trace_bputs(unsigned long ip, const char *str) entry->str = str; __buffer_unlock_commit(buffer, event); + ftrace_trace_stack(buffer, irq_flags, 4, pc); return 1; } From 48050b8a692fdf5eb71e4c6e445ca506ec6f1359 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 9 Jul 2014 09:22:54 +0800 Subject: [PATCH 0037/1185] hwmon: (da9055) Don't use dash in the name attribute commit 6b00f440dd678d786389a7100a2e03fe44478431 upstream. Dashes are not allowed in hwmon name attributes. Use "da9055" instead of "da9055-hwmon". Signed-off-by: Axel Lin Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/da9055-hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/da9055-hwmon.c b/drivers/hwmon/da9055-hwmon.c index 029ecabc4380..1b275a2881d6 100644 --- a/drivers/hwmon/da9055-hwmon.c +++ b/drivers/hwmon/da9055-hwmon.c @@ -204,7 +204,7 @@ static ssize_t da9055_hwmon_show_name(struct device *dev, struct device_attribute *devattr, char *buf) { - return sprintf(buf, "da9055-hwmon\n"); + return sprintf(buf, "da9055\n"); } static ssize_t show_label(struct device *dev, From 4c6d5fb84ff7ad51e7bf91dbe60ed6fe670f252d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 9 Jul 2014 09:18:59 +0800 Subject: [PATCH 0038/1185] hwmon: (da9052) Don't use dash in the name attribute commit ee14b644daaa58afe1e91bb9ebd9cf1b18d1f5fa upstream. Dashes are not allowed in hwmon name attributes. Use "da9052" instead of "da9052-hwmon". Signed-off-by: Axel Lin Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/da9052-hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c index 960fac3fb166..48044b044b7a 100644 --- a/drivers/hwmon/da9052-hwmon.c +++ b/drivers/hwmon/da9052-hwmon.c @@ -194,7 +194,7 @@ static ssize_t da9052_hwmon_show_name(struct device *dev, struct device_attribute *devattr, char *buf) { - return sprintf(buf, "da9052-hwmon\n"); + return sprintf(buf, "da9052\n"); } static ssize_t show_label(struct device *dev, From 0979b7169679cc91edcadd98ffbe389400bbc088 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 16 Jul 2014 17:40:31 -0700 Subject: [PATCH 0039/1185] hwmon: (adt7470) Fix writes to temperature limit registers commit de12d6f4b10b21854441f5242dcb29ea96181e58 upstream. Temperature limit registers are signed. Limits therefore need to be clamped to (-128, 127) degrees C and not to (0, 255) degrees C. Without this fix, writing a limit of 128 degrees C sets the actual limit to -128 degrees C. Signed-off-by: Guenter Roeck Reviewed-by: Axel Lin Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/adt7470.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 58637355c1f6..79610bdf1d35 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -515,7 +515,7 @@ static ssize_t set_temp_min(struct device *dev, return -EINVAL; temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = clamp_val(temp, 0, 255); + temp = clamp_val(temp, -128, 127); mutex_lock(&data->lock); data->temp_min[attr->index] = temp; @@ -549,7 +549,7 @@ static ssize_t set_temp_max(struct device *dev, return -EINVAL; temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = clamp_val(temp, 0, 255); + temp = clamp_val(temp, -128, 127); mutex_lock(&data->lock); data->temp_max[attr->index] = temp; @@ -826,7 +826,7 @@ static ssize_t set_pwm_tmin(struct device *dev, return -EINVAL; temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = clamp_val(temp, 0, 255); + temp = clamp_val(temp, -128, 127); mutex_lock(&data->lock); data->pwm_tmin[attr->index] = temp; From 125a0039d6eaee7b9e65be765f704d9278e7ccff Mon Sep 17 00:00:00 2001 From: Stefan Assmann Date: Thu, 10 Jul 2014 03:29:39 -0700 Subject: [PATCH 0040/1185] igb: do a reset on SR-IOV re-init if device is down commit 76252723e88681628a3dbb9c09c963e095476f73 upstream. To properly re-initialize SR-IOV it is necessary to reset the device even if it is already down. Not doing this may result in Tx unit hangs. Signed-off-by: Stefan Assmann Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/igb/igb_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 64cbe0dfe043..4d3c8122e2aa 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -7229,6 +7229,8 @@ static int igb_sriov_reinit(struct pci_dev *dev) if (netif_running(netdev)) igb_close(netdev); + else + igb_reset(adapter); igb_clear_interrupt_scheme(adapter); From 44a5342ff8de29043144129e27abce1f201fc774 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 25 Jun 2014 09:12:30 +0300 Subject: [PATCH 0041/1185] iwlwifi: dvm: don't enable CTS to self commit 43d826ca5979927131685cc2092c7ce862cb91cd upstream. We should always prefer to use full RTS protection. Using CTS to self gives a meaningless improvement, but this flow is much harder for the firmware which is likely to have issues with it. Signed-off-by: Emmanuel Grumbach Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/dvm/rxon.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/iwlwifi/dvm/rxon.c index cd1ad0019185..ca17e4c9eca2 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rxon.c +++ b/drivers/net/wireless/iwlwifi/dvm/rxon.c @@ -1072,13 +1072,6 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) /* recalculate basic rates */ iwl_calc_basic_rates(priv, ctx); - /* - * force CTS-to-self frames protection if RTS-CTS is not preferred - * one aggregation protection method - */ - if (!priv->hw_params.use_rts_for_aggregation) - ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; - if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) || !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK)) ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; @@ -1484,11 +1477,6 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw, else ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; - if (bss_conf->use_cts_prot) - ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; - else - ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN; - memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); if (vif->type == NL80211_IFTYPE_AP || From 1ccc3ffad12489d90994243be03017ff6e78ef51 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Mon, 23 Jun 2014 13:22:06 -0700 Subject: [PATCH 0042/1185] shmem: fix faulting into a hole while it's punched commit f00cdc6df7d7cfcabb5b740911e6788cb0802bdb upstream. Trinity finds that mmap access to a hole while it's punched from shmem can prevent the madvise(MADV_REMOVE) or fallocate(FALLOC_FL_PUNCH_HOLE) from completing, until the reader chooses to stop; with the puncher's hold on i_mutex locking out all other writers until it can complete. It appears that the tmpfs fault path is too light in comparison with its hole-punching path, lacking an i_data_sem to obstruct it; but we don't want to slow down the common case. Extend shmem_fallocate()'s existing range notification mechanism, so shmem_fault() can refrain from faulting pages into the hole while it's punched, waiting instead on i_mutex (when safe to sleep; or repeatedly faulting when not). [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Hugh Dickins Reported-by: Sasha Levin Tested-by: Sasha Levin Cc: Dave Jones Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/shmem.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 509b393eceeb..61cf45c343e6 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -80,11 +80,12 @@ static struct vfsmount *shm_mnt; #define SHORT_SYMLINK_LEN 128 /* - * shmem_fallocate and shmem_writepage communicate via inode->i_private - * (with i_mutex making sure that it has only one user at a time): - * we would prefer not to enlarge the shmem inode just for that. + * shmem_fallocate communicates with shmem_fault or shmem_writepage via + * inode->i_private (with i_mutex making sure that it has only one user at + * a time): we would prefer not to enlarge the shmem inode just for that. */ struct shmem_falloc { + int mode; /* FALLOC_FL mode currently operating */ pgoff_t start; /* start of range currently being fallocated */ pgoff_t next; /* the next page offset to be fallocated */ pgoff_t nr_falloced; /* how many new pages have been fallocated */ @@ -826,6 +827,7 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) spin_lock(&inode->i_lock); shmem_falloc = inode->i_private; if (shmem_falloc && + !shmem_falloc->mode && index >= shmem_falloc->start && index < shmem_falloc->next) shmem_falloc->nr_unswapped++; @@ -1300,6 +1302,44 @@ static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) int error; int ret = VM_FAULT_LOCKED; + /* + * Trinity finds that probing a hole which tmpfs is punching can + * prevent the hole-punch from ever completing: which in turn + * locks writers out with its hold on i_mutex. So refrain from + * faulting pages into the hole while it's being punched, and + * wait on i_mutex to be released if vmf->flags permits. + */ + if (unlikely(inode->i_private)) { + struct shmem_falloc *shmem_falloc; + + spin_lock(&inode->i_lock); + shmem_falloc = inode->i_private; + if (!shmem_falloc || + shmem_falloc->mode != FALLOC_FL_PUNCH_HOLE || + vmf->pgoff < shmem_falloc->start || + vmf->pgoff >= shmem_falloc->next) + shmem_falloc = NULL; + spin_unlock(&inode->i_lock); + /* + * i_lock has protected us from taking shmem_falloc seriously + * once return from shmem_fallocate() went back up that stack. + * i_lock does not serialize with i_mutex at all, but it does + * not matter if sometimes we wait unnecessarily, or sometimes + * miss out on waiting: we just need to make those cases rare. + */ + if (shmem_falloc) { + if ((vmf->flags & FAULT_FLAG_ALLOW_RETRY) && + !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) { + up_read(&vma->vm_mm->mmap_sem); + mutex_lock(&inode->i_mutex); + mutex_unlock(&inode->i_mutex); + return VM_FAULT_RETRY; + } + /* cond_resched? Leave that to GUP or return to user */ + return VM_FAULT_NOPAGE; + } + } + error = shmem_getpage(inode, vmf->pgoff, &vmf->page, SGP_CACHE, &ret); if (error) return ((error == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS); @@ -1817,18 +1857,26 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset, mutex_lock(&inode->i_mutex); + shmem_falloc.mode = mode & ~FALLOC_FL_KEEP_SIZE; + if (mode & FALLOC_FL_PUNCH_HOLE) { struct address_space *mapping = file->f_mapping; loff_t unmap_start = round_up(offset, PAGE_SIZE); loff_t unmap_end = round_down(offset + len, PAGE_SIZE) - 1; + shmem_falloc.start = unmap_start >> PAGE_SHIFT; + shmem_falloc.next = (unmap_end + 1) >> PAGE_SHIFT; + spin_lock(&inode->i_lock); + inode->i_private = &shmem_falloc; + spin_unlock(&inode->i_lock); + if ((u64)unmap_end > (u64)unmap_start) unmap_mapping_range(mapping, unmap_start, 1 + unmap_end - unmap_start, 0); shmem_truncate_range(inode, offset, offset + len - 1); /* No need to unmap again: hole-punching leaves COWed pages */ error = 0; - goto out; + goto undone; } /* We need to check rlimit even when FALLOC_FL_KEEP_SIZE */ From 887675c981bcefc567bd1f18352238d7ce1cf47a Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 23 Jul 2014 14:00:10 -0700 Subject: [PATCH 0043/1185] shmem: fix faulting into a hole, not taking i_mutex commit 8e205f779d1443a94b5ae81aa359cb535dd3021e upstream. Commit f00cdc6df7d7 ("shmem: fix faulting into a hole while it's punched") was buggy: Sasha sent a lockdep report to remind us that grabbing i_mutex in the fault path is a no-no (write syscall may already hold i_mutex while faulting user buffer). We tried a completely different approach (see following patch) but that proved inadequate: good enough for a rational workload, but not good enough against trinity - which forks off so many mappings of the object that contention on i_mmap_mutex while hole-puncher holds i_mutex builds into serious starvation when concurrent faults force the puncher to fall back to single-page unmap_mapping_range() searches of the i_mmap tree. So return to the original umbrella approach, but keep away from i_mutex this time. We really don't want to bloat every shmem inode with a new mutex or completion, just to protect this unlikely case from trinity. So extend the original with wait_queue_head on stack at the hole-punch end, and wait_queue item on the stack at the fault end. This involves further use of i_lock to guard against the races: lockdep has been happy so far, and I see fs/inode.c:unlock_new_inode() holds i_lock around wake_up_bit(), which is comparable to what we do here. i_lock is more convenient, but we could switch to shmem's info->lock. This issue has been tagged with CVE-2014-4171, which will require commit f00cdc6df7d7 and this and the following patch to be backported: we suggest to 3.1+, though in fact the trinity forkbomb effect might go back as far as 2.6.16, when madvise(,,MADV_REMOVE) came in - or might not, since much has changed, with i_mmap_mutex a spinlock before 3.0. Anyone running trinity on 3.0 and earlier? I don't think we need care. Signed-off-by: Hugh Dickins Reported-by: Sasha Levin Tested-by: Sasha Levin Cc: Vlastimil Babka Cc: Konstantin Khlebnikov Cc: Johannes Weiner Cc: Lukas Czerner Cc: Dave Jones Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/shmem.c | 78 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 61cf45c343e6..3d26fedbd20e 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -85,7 +85,7 @@ static struct vfsmount *shm_mnt; * a time): we would prefer not to enlarge the shmem inode just for that. */ struct shmem_falloc { - int mode; /* FALLOC_FL mode currently operating */ + wait_queue_head_t *waitq; /* faults into hole wait for punch to end */ pgoff_t start; /* start of range currently being fallocated */ pgoff_t next; /* the next page offset to be fallocated */ pgoff_t nr_falloced; /* how many new pages have been fallocated */ @@ -827,7 +827,7 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) spin_lock(&inode->i_lock); shmem_falloc = inode->i_private; if (shmem_falloc && - !shmem_falloc->mode && + !shmem_falloc->waitq && index >= shmem_falloc->start && index < shmem_falloc->next) shmem_falloc->nr_unswapped++; @@ -1306,38 +1306,58 @@ static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) * Trinity finds that probing a hole which tmpfs is punching can * prevent the hole-punch from ever completing: which in turn * locks writers out with its hold on i_mutex. So refrain from - * faulting pages into the hole while it's being punched, and - * wait on i_mutex to be released if vmf->flags permits. + * faulting pages into the hole while it's being punched. Although + * shmem_undo_range() does remove the additions, it may be unable to + * keep up, as each new page needs its own unmap_mapping_range() call, + * and the i_mmap tree grows ever slower to scan if new vmas are added. + * + * It does not matter if we sometimes reach this check just before the + * hole-punch begins, so that one fault then races with the punch: + * we just need to make racing faults a rare case. + * + * The implementation below would be much simpler if we just used a + * standard mutex or completion: but we cannot take i_mutex in fault, + * and bloating every shmem inode for this unlikely case would be sad. */ if (unlikely(inode->i_private)) { struct shmem_falloc *shmem_falloc; spin_lock(&inode->i_lock); shmem_falloc = inode->i_private; - if (!shmem_falloc || - shmem_falloc->mode != FALLOC_FL_PUNCH_HOLE || - vmf->pgoff < shmem_falloc->start || - vmf->pgoff >= shmem_falloc->next) - shmem_falloc = NULL; - spin_unlock(&inode->i_lock); - /* - * i_lock has protected us from taking shmem_falloc seriously - * once return from shmem_fallocate() went back up that stack. - * i_lock does not serialize with i_mutex at all, but it does - * not matter if sometimes we wait unnecessarily, or sometimes - * miss out on waiting: we just need to make those cases rare. - */ - if (shmem_falloc) { + if (shmem_falloc && + shmem_falloc->waitq && + vmf->pgoff >= shmem_falloc->start && + vmf->pgoff < shmem_falloc->next) { + wait_queue_head_t *shmem_falloc_waitq; + DEFINE_WAIT(shmem_fault_wait); + + ret = VM_FAULT_NOPAGE; if ((vmf->flags & FAULT_FLAG_ALLOW_RETRY) && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) { + /* It's polite to up mmap_sem if we can */ up_read(&vma->vm_mm->mmap_sem); - mutex_lock(&inode->i_mutex); - mutex_unlock(&inode->i_mutex); - return VM_FAULT_RETRY; + ret = VM_FAULT_RETRY; } - /* cond_resched? Leave that to GUP or return to user */ - return VM_FAULT_NOPAGE; + + shmem_falloc_waitq = shmem_falloc->waitq; + prepare_to_wait(shmem_falloc_waitq, &shmem_fault_wait, + TASK_UNINTERRUPTIBLE); + spin_unlock(&inode->i_lock); + schedule(); + + /* + * shmem_falloc_waitq points into the shmem_fallocate() + * stack of the hole-punching task: shmem_falloc_waitq + * is usually invalid by the time we reach here, but + * finish_wait() does not dereference it in that case; + * though i_lock needed lest racing with wake_up_all(). + */ + spin_lock(&inode->i_lock); + finish_wait(shmem_falloc_waitq, &shmem_fault_wait); + spin_unlock(&inode->i_lock); + return ret; } + spin_unlock(&inode->i_lock); } error = shmem_getpage(inode, vmf->pgoff, &vmf->page, SGP_CACHE, &ret); @@ -1857,13 +1877,13 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset, mutex_lock(&inode->i_mutex); - shmem_falloc.mode = mode & ~FALLOC_FL_KEEP_SIZE; - if (mode & FALLOC_FL_PUNCH_HOLE) { struct address_space *mapping = file->f_mapping; loff_t unmap_start = round_up(offset, PAGE_SIZE); loff_t unmap_end = round_down(offset + len, PAGE_SIZE) - 1; + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(shmem_falloc_waitq); + shmem_falloc.waitq = &shmem_falloc_waitq; shmem_falloc.start = unmap_start >> PAGE_SHIFT; shmem_falloc.next = (unmap_end + 1) >> PAGE_SHIFT; spin_lock(&inode->i_lock); @@ -1875,8 +1895,13 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset, 1 + unmap_end - unmap_start, 0); shmem_truncate_range(inode, offset, offset + len - 1); /* No need to unmap again: hole-punching leaves COWed pages */ + + spin_lock(&inode->i_lock); + inode->i_private = NULL; + wake_up_all(&shmem_falloc_waitq); + spin_unlock(&inode->i_lock); error = 0; - goto undone; + goto out; } /* We need to check rlimit even when FALLOC_FL_KEEP_SIZE */ @@ -1892,6 +1917,7 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset, goto out; } + shmem_falloc.waitq = NULL; shmem_falloc.start = start; shmem_falloc.next = start; shmem_falloc.nr_falloced = 0; From 7dc7fb432bc92a988afb49e948218de575b7eb3f Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 23 Jul 2014 14:00:13 -0700 Subject: [PATCH 0044/1185] shmem: fix splicing from a hole while it's punched commit b1a366500bd537b50c3aad26dc7df083ec03a448 upstream. shmem_fault() is the actual culprit in trinity's hole-punch starvation, and the most significant cause of such problems: since a page faulted is one that then appears page_mapped(), needing unmap_mapping_range() and i_mmap_mutex to be unmapped again. But it is not the only way in which a page can be brought into a hole in the radix_tree while that hole is being punched; and Vlastimil's testing implies that if enough other processors are busy filling in the hole, then shmem_undo_range() can be kept from completing indefinitely. shmem_file_splice_read() is the main other user of SGP_CACHE, which can instantiate shmem pagecache pages in the read-only case (without holding i_mutex, so perhaps concurrently with a hole-punch). Probably it's silly not to use SGP_READ already (using the ZERO_PAGE for holes): which ought to be safe, but might bring surprises - not a change to be rushed. shmem_read_mapping_page_gfp() is an internal interface used by drivers/gpu/drm GEM (and next by uprobes): it should be okay. And shmem_file_read_iter() uses the SGP_DIRTY variant of SGP_CACHE, when called internally by the kernel (perhaps for a stacking filesystem, which might rely on holes to be reserved): it's unclear whether it could be provoked to keep hole-punch busy or not. We could apply the same umbrella as now used in shmem_fault() to shmem_file_splice_read() and the others; but it looks ugly, and use over a range raises questions - should it actually be per page? can these get starved themselves? The origin of this part of the problem is my v3.1 commit d0823576bf4b ("mm: pincer in truncate_inode_pages_range"), once it was duplicated into shmem.c. It seemed like a nice idea at the time, to ensure (barring RCU lookup fuzziness) that there's an instant when the entire hole is empty; but the indefinitely repeated scans to ensure that make it vulnerable. Revert that "enhancement" to hole-punch from shmem_undo_range(), but retain the unproblematic rescanning when it's truncating; add a couple of comments there. Remove the "indices[0] >= end" test: that is now handled satisfactorily by the inner loop, and mem_cgroup_uncharge_start()/end() are too light to be worth avoiding here. But if we do not always loop indefinitely, we do need to handle the case of swap swizzled back to page before shmem_free_swap() gets it: add a retry for that case, as suggested by Konstantin Khlebnikov; and for the case of page swizzled back to swap, as suggested by Johannes Weiner. Signed-off-by: Hugh Dickins Reported-by: Sasha Levin Suggested-by: Vlastimil Babka Cc: Konstantin Khlebnikov Cc: Johannes Weiner Cc: Lukas Czerner Cc: Dave Jones Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/shmem.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 3d26fedbd20e..16cc1d77f70a 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -534,22 +534,19 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend, return; index = start; - for ( ; ; ) { + while (index < end) { cond_resched(); pvec.nr = shmem_find_get_pages_and_swap(mapping, index, min(end - index, (pgoff_t)PAGEVEC_SIZE), pvec.pages, indices); if (!pvec.nr) { - if (index == start || unfalloc) + /* If all gone or hole-punch or unfalloc, we're done */ + if (index == start || end != -1) break; + /* But if truncating, restart to make sure all gone */ index = start; continue; } - if ((index == start || unfalloc) && indices[0] >= end) { - shmem_deswap_pagevec(&pvec); - pagevec_release(&pvec); - break; - } mem_cgroup_uncharge_start(); for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; @@ -561,8 +558,12 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend, if (radix_tree_exceptional_entry(page)) { if (unfalloc) continue; - nr_swaps_freed += !shmem_free_swap(mapping, - index, page); + if (shmem_free_swap(mapping, index, page)) { + /* Swap was replaced by page: retry */ + index--; + break; + } + nr_swaps_freed++; continue; } @@ -571,6 +572,11 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend, if (page->mapping == mapping) { VM_BUG_ON(PageWriteback(page)); truncate_inode_page(mapping, page); + } else { + /* Page was replaced by swap: retry */ + unlock_page(page); + index--; + break; } } unlock_page(page); From 296692cab2e13d7bae70dd9ebfaf32eb36ec2793 Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Sat, 5 Jul 2014 02:26:37 +0400 Subject: [PATCH 0045/1185] ip_tunnel: fix ip_tunnel_lookup [ Upstream commit e0056593b61253f1a8a9941dacda22e73b963cdc ] This patch fixes 3 similar bugs where incoming packets might be routed into wrong non-wildcard tunnels: 1) Consider the following setup: ip address add 1.1.1.1/24 dev eth0 ip address add 1.1.1.2/24 dev eth0 ip tunnel add ipip1 remote 2.2.2.2 local 1.1.1.1 mode ipip dev eth0 ip link set ipip1 up Incoming ipip packets from 2.2.2.2 were routed into ipip1 even if it has dst = 1.1.1.2. Moreover even if there was wildcard tunnel like ip tunnel add ipip0 remote 2.2.2.2 local any mode ipip dev eth0 but it was created before explicit one (with local 1.1.1.1), incoming ipip packets with src = 2.2.2.2 and dst = 1.1.1.2 were still routed into ipip1. Same issue existed with all tunnels that use ip_tunnel_lookup (gre, vti) 2) ip address add 1.1.1.1/24 dev eth0 ip tunnel add ipip1 remote 2.2.146.85 local 1.1.1.1 mode ipip dev eth0 ip link set ipip1 up Incoming ipip packets with dst = 1.1.1.1 were routed into ipip1, no matter what src address is. Any remote ip address which has ip_tunnel_hash = 0 raised this issue, 2.2.146.85 is just an example, there are more than 4 million of them. And again, wildcard tunnel like ip tunnel add ipip0 remote any local 1.1.1.1 mode ipip dev eth0 wouldn't be ever matched if it was created before explicit tunnel like above. Gre & vti tunnels had the same issue. 3) ip address add 1.1.1.1/24 dev eth0 ip tunnel add gre1 remote 2.2.146.84 local 1.1.1.1 key 1 mode gre dev eth0 ip link set gre1 up Any incoming gre packet with key = 1 were routed into gre1, no matter what src/dst addresses are. Any remote ip address which has ip_tunnel_hash = 0 raised the issue, 2.2.146.84 is just an example, there are more than 4 million of them. Wildcard tunnel like ip tunnel add gre2 remote any local any key 1 mode gre dev eth0 wouldn't be ever matched if it was created before explicit tunnel like above. All this stuff happened because while looking for a wildcard tunnel we didn't check that matched tunnel is a wildcard one. Fixed. Signed-off-by: Dmitry Popov Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ip_tunnel.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index fa6573264c8a..5642374cb751 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -166,6 +166,7 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn, hlist_for_each_entry_rcu(t, head, hash_node) { if (remote != t->parms.iph.daddr || + t->parms.iph.saddr != 0 || !(t->dev->flags & IFF_UP)) continue; @@ -182,10 +183,11 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn, head = &itn->tunnels[hash]; hlist_for_each_entry_rcu(t, head, hash_node) { - if ((local != t->parms.iph.saddr && - (local != t->parms.iph.daddr || - !ipv4_is_multicast(local))) || - !(t->dev->flags & IFF_UP)) + if ((local != t->parms.iph.saddr || t->parms.iph.daddr != 0) && + (local != t->parms.iph.daddr || !ipv4_is_multicast(local))) + continue; + + if (!(t->dev->flags & IFF_UP)) continue; if (!ip_tunnel_key_match(&t->parms, flags, key)) @@ -202,6 +204,8 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn, hlist_for_each_entry_rcu(t, head, hash_node) { if (t->parms.i_key != key || + t->parms.iph.saddr != 0 || + t->parms.iph.daddr != 0 || !(t->dev->flags & IFF_UP)) continue; From 856443cb555a75b9700d3fabf5965b46337de199 Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Wed, 18 Jun 2014 21:15:03 -0400 Subject: [PATCH 0046/1185] tcp: fix tcp_match_skb_to_sack() for unaligned SACK at end of an skb [ Upstream commit 2cd0d743b05e87445c54ca124a9916f22f16742e ] If there is an MSS change (or misbehaving receiver) that causes a SACK to arrive that covers the end of an skb but is less than one MSS, then tcp_match_skb_to_sack() was rounding up pkt_len to the full length of the skb ("Round if necessary..."), then chopping all bytes off the skb and creating a zero-byte skb in the write queue. This was visible now because the recently simplified TLP logic in bef1909ee3ed1c ("tcp: fixing TLP's FIN recovery") could find that 0-byte skb at the end of the write queue, and now that we do not check that skb's length we could send it as a TLP probe. Consider the following example scenario: mss: 1000 skb: seq: 0 end_seq: 4000 len: 4000 SACK: start_seq: 3999 end_seq: 4000 The tcp_match_skb_to_sack() code will compute: in_sack = false pkt_len = start_seq - TCP_SKB_CB(skb)->seq = 3999 - 0 = 3999 new_len = (pkt_len / mss) * mss = (3999/1000)*1000 = 3000 new_len += mss = 4000 Previously we would find the new_len > skb->len check failing, so we would fall through and set pkt_len = new_len = 4000 and chop off pkt_len of 4000 from the 4000-byte skb, leaving a 0-byte segment afterward in the write queue. With this new commit, we notice that the new new_len >= skb->len check succeeds, so that we return without trying to fragment. Fixes: adb92db857ee ("tcp: Make SACK code to split only at mss boundaries") Reported-by: Eric Dumazet Signed-off-by: Neal Cardwell Cc: Eric Dumazet Cc: Yuchung Cheng Cc: Ilpo Jarvinen Acked-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index ba7d2b7ad9f9..19104e321029 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1130,7 +1130,7 @@ static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb, unsigned int new_len = (pkt_len / mss) * mss; if (!in_sack && new_len < pkt_len) { new_len += mss; - if (new_len > skb->len) + if (new_len >= skb->len) return 0; } pkt_len = new_len; From e9013d0f0faef78f90f7bb30e722965fe992dc1e Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 18 Jun 2014 23:46:31 +0200 Subject: [PATCH 0047/1185] net: sctp: check proc_dointvec result in proc_sctp_do_auth [ Upstream commit 24599e61b7552673dd85971cf5a35369cd8c119e ] When writing to the sysctl field net.sctp.auth_enable, it can well be that the user buffer we handed over to proc_dointvec() via proc_sctp_do_auth() handler contains something other than integers. In that case, we would set an uninitialized 4-byte value from the stack to net->sctp.auth_enable that can be leaked back when reading the sysctl variable, and it can unintentionally turn auth_enable on/off based on the stack content since auth_enable is interpreted as a boolean. Fix it up by making sure proc_dointvec() returned sucessfully. Fixes: b14878ccb7fa ("net: sctp: cache auth_enable per endpoint") Reported-by: Florian Westphal Signed-off-by: Daniel Borkmann Acked-by: Neil Horman Acked-by: Vlad Yasevich Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/sysctl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index fe0ba7488bdf..29299dcabfbb 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -368,8 +368,7 @@ static int proc_sctp_do_auth(struct ctl_table *ctl, int write, tbl.data = &net->sctp.auth_enable; ret = proc_dointvec(&tbl, write, buffer, lenp, ppos); - - if (write) { + if (write && ret == 0) { struct sock *sk = net->sctp.ctl_sock; net->sctp.auth_enable = new_value; From 1b56220b0df8f0963bacbf35637545b550484a64 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Wed, 18 Jun 2014 13:46:02 +0800 Subject: [PATCH 0048/1185] 8021q: fix a potential memory leak [ Upstream commit 916c1689a09bc1ca81f2d7a34876f8d35aadd11b ] skb_cow called in vlan_reorder_header does not free the skb when it failed, and vlan_reorder_header returns NULL to reset original skb when it is called in vlan_untag, lead to a memory leak. Signed-off-by: Li RongQing Acked-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/8021q/vlan_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 4a78c4de9f20..42ef36a85e69 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -103,8 +103,11 @@ EXPORT_SYMBOL(vlan_dev_vlan_id); static struct sk_buff *vlan_reorder_header(struct sk_buff *skb) { - if (skb_cow(skb, skb_headroom(skb)) < 0) + if (skb_cow(skb, skb_headroom(skb)) < 0) { + kfree_skb(skb); return NULL; + } + memmove(skb->data - ETH_HLEN, skb->data - VLAN_ETH_HLEN, 2 * ETH_ALEN); skb->mac_header += VLAN_HLEN; return skb; From 86e48c03d774e01ccd71ecba4fc4b5c2bc0b5b41 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 24 Jun 2014 10:05:11 -0700 Subject: [PATCH 0049/1185] ipv4: fix dst race in sk_dst_get() [ Upstream commit f88649721268999bdff09777847080a52004f691 ] When IP route cache had been removed in linux-3.6, we broke assumption that dst entries were all freed after rcu grace period. DST_NOCACHE dst were supposed to be freed from dst_release(). But it appears we want to keep such dst around, either in UDP sockets or tunnels. In sk_dst_get() we need to make sure dst refcount is not 0 before incrementing it, or else we might end up freeing a dst twice. DST_NOCACHE set on a dst does not mean this dst can not be attached to a socket or a tunnel. Then, before actual freeing, we need to observe a rcu grace period to make sure all other cpus can catch the fact the dst is no longer usable. Signed-off-by: Eric Dumazet Reported-by: Dormando Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/net/sock.h | 4 ++-- net/core/dst.c | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index 72f710d2f75a..ff57aff205cd 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1727,8 +1727,8 @@ sk_dst_get(struct sock *sk) rcu_read_lock(); dst = rcu_dereference(sk->sk_dst_cache); - if (dst) - dst_hold(dst); + if (dst && !atomic_inc_not_zero(&dst->__refcnt)) + dst = NULL; rcu_read_unlock(); return dst; } diff --git a/net/core/dst.c b/net/core/dst.c index df9cc810ec8e..c0e021871df8 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -267,6 +267,15 @@ struct dst_entry *dst_destroy(struct dst_entry * dst) } EXPORT_SYMBOL(dst_destroy); +static void dst_destroy_rcu(struct rcu_head *head) +{ + struct dst_entry *dst = container_of(head, struct dst_entry, rcu_head); + + dst = dst_destroy(dst); + if (dst) + __dst_free(dst); +} + void dst_release(struct dst_entry *dst) { if (dst) { @@ -274,11 +283,8 @@ void dst_release(struct dst_entry *dst) newrefcnt = atomic_dec_return(&dst->__refcnt); WARN_ON(newrefcnt < 0); - if (unlikely(dst->flags & DST_NOCACHE) && !newrefcnt) { - dst = dst_destroy(dst); - if (dst) - __dst_free(dst); - } + if (unlikely(dst->flags & DST_NOCACHE) && !newrefcnt) + call_rcu(&dst->rcu_head, dst_destroy_rcu); } } EXPORT_SYMBOL(dst_release); From f1e1b06f19e1ddcebcee56ba33845ded7bf719ac Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 30 Jun 2014 01:26:23 -0700 Subject: [PATCH 0050/1185] ipv4: irq safe sk_dst_[re]set() and ipv4_sk_update_pmtu() fix [ Upstream commit 7f502361531e9eecb396cf99bdc9e9a59f7ebd7f ] We have two different ways to handle changes to sk->sk_dst First way (used by TCP) assumes socket lock is owned by caller, and use no extra lock : __sk_dst_set() & __sk_dst_reset() Another way (used by UDP) uses sk_dst_lock because socket lock is not always taken. Note that sk_dst_lock is not softirq safe. These ways are not inter changeable for a given socket type. ipv4_sk_update_pmtu(), added in linux-3.8, added a race, as it used the socket lock as synchronization, but users might be UDP sockets. Instead of converting sk_dst_lock to a softirq safe version, use xchg() as we did for sk_rx_dst in commit e47eb5dfb296b ("udp: ipv4: do not use sk_dst_lock from softirq context") In a follow up patch, we probably can remove sk_dst_lock, as it is only used in IPv6. Signed-off-by: Eric Dumazet Cc: Steffen Klassert Fixes: 9cb3a50c5f63e ("ipv4: Invalidate the socket cached route on pmtu events if possible") Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/net/sock.h | 12 ++++++------ net/ipv4/route.c | 15 ++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index ff57aff205cd..4d2358113da2 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1767,9 +1767,11 @@ __sk_dst_set(struct sock *sk, struct dst_entry *dst) static inline void sk_dst_set(struct sock *sk, struct dst_entry *dst) { - spin_lock(&sk->sk_dst_lock); - __sk_dst_set(sk, dst); - spin_unlock(&sk->sk_dst_lock); + struct dst_entry *old_dst; + + sk_tx_queue_clear(sk); + old_dst = xchg(&sk->sk_dst_cache, dst); + dst_release(old_dst); } static inline void @@ -1781,9 +1783,7 @@ __sk_dst_reset(struct sock *sk) static inline void sk_dst_reset(struct sock *sk) { - spin_lock(&sk->sk_dst_lock); - __sk_dst_reset(sk); - spin_unlock(&sk->sk_dst_lock); + sk_dst_set(sk, NULL); } extern struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 7256eef088b2..2b9887becb5c 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -985,20 +985,21 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) const struct iphdr *iph = (const struct iphdr *) skb->data; struct flowi4 fl4; struct rtable *rt; - struct dst_entry *dst; + struct dst_entry *odst = NULL; bool new = false; bh_lock_sock(sk); - rt = (struct rtable *) __sk_dst_get(sk); + odst = sk_dst_get(sk); - if (sock_owned_by_user(sk) || !rt) { + if (sock_owned_by_user(sk) || !odst) { __ipv4_sk_update_pmtu(skb, sk, mtu); goto out; } __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0); - if (!__sk_dst_check(sk, 0)) { + rt = (struct rtable *)odst; + if (odst->obsolete && odst->ops->check(odst, 0) == NULL) { rt = ip_route_output_flow(sock_net(sk), &fl4, sk); if (IS_ERR(rt)) goto out; @@ -1008,8 +1009,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) __ip_rt_update_pmtu((struct rtable *) rt->dst.path, &fl4, mtu); - dst = dst_check(&rt->dst, 0); - if (!dst) { + if (!dst_check(&rt->dst, 0)) { if (new) dst_release(&rt->dst); @@ -1021,10 +1021,11 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) } if (new) - __sk_dst_set(sk, &rt->dst); + sk_dst_set(sk, &rt->dst); out: bh_unlock_sock(sk); + dst_release(odst); } EXPORT_SYMBOL_GPL(ipv4_sk_update_pmtu); From 4d8eb541f3bed4daf65dc188e7aa0824b1ac0d75 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 2 Jul 2014 02:39:38 -0700 Subject: [PATCH 0051/1185] net: fix sparse warning in sk_dst_set() [ Upstream commit 5925a0555bdaf0b396a84318cbc21ba085f6c0d3 ] sk_dst_cache has __rcu annotation, so we need a cast to avoid following sparse error : include/net/sock.h:1774:19: warning: incorrect type in initializer (different address spaces) include/net/sock.h:1774:19: expected struct dst_entry [noderef] *__ret include/net/sock.h:1774:19: got struct dst_entry *dst Signed-off-by: Eric Dumazet Reported-by: kbuild test robot Fixes: 7f502361531e ("ipv4: irq safe sk_dst_[re]set() and ipv4_sk_update_pmtu() fix") Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/net/sock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/sock.h b/include/net/sock.h index 4d2358113da2..26b15c0780be 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1770,7 +1770,7 @@ sk_dst_set(struct sock *sk, struct dst_entry *dst) struct dst_entry *old_dst; sk_tx_queue_clear(sk); - old_dst = xchg(&sk->sk_dst_cache, dst); + old_dst = xchg((__force struct dst_entry **)&sk->sk_dst_cache, dst); dst_release(old_dst); } From eb7e73eafa8fc5168bc734431f831c9f6aef134a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Jun 2014 00:44:02 -0700 Subject: [PATCH 0052/1185] bnx2x: fix possible panic under memory stress [ Upstream commit 07b0f00964def8af9321cfd6c4a7e84f6362f728 ] While it is legal to kfree(NULL), it is not wise to use : put_page(virt_to_head_page(NULL)) BUG: unable to handle kernel paging request at ffffeba400000000 IP: [] virt_to_head_page+0x36/0x44 [bnx2x] Reported-by: Michel Lespinasse Signed-off-by: Eric Dumazet Cc: Ariel Elior Fixes: d46d132cc021 ("bnx2x: use netdev_alloc_frag()") Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 70be100feeb4..b04f7f128f49 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -745,7 +745,8 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp, return; } - bnx2x_frag_free(fp, new_data); + if (new_data) + bnx2x_frag_free(fp, new_data); drop: /* drop the packet and keep the buffer in the bin */ DP(NETIF_MSG_RX_STATUS, From 00be00119aa3e62aa234a9ccc03010b2504c1096 Mon Sep 17 00:00:00 2001 From: Christoph Paasch Date: Sat, 28 Jun 2014 18:26:37 +0200 Subject: [PATCH 0053/1185] tcp: Fix divide by zero when pushing during tcp-repair [ Upstream commit 5924f17a8a30c2ae18d034a86ee7581b34accef6 ] When in repair-mode and TCP_RECV_QUEUE is set, we end up calling tcp_push with mss_now being 0. If data is in the send-queue and tcp_set_skb_tso_segs gets called, we crash because it will divide by mss_now: [ 347.151939] divide error: 0000 [#1] SMP [ 347.152907] Modules linked in: [ 347.152907] CPU: 1 PID: 1123 Comm: packetdrill Not tainted 3.16.0-rc2 #4 [ 347.152907] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2007 [ 347.152907] task: f5b88540 ti: f3c82000 task.ti: f3c82000 [ 347.152907] EIP: 0060:[] EFLAGS: 00210246 CPU: 1 [ 347.152907] EIP is at tcp_set_skb_tso_segs+0x49/0xa0 [ 347.152907] EAX: 00000b67 EBX: f5acd080 ECX: 00000000 EDX: 00000000 [ 347.152907] ESI: f5a28f40 EDI: f3c88f00 EBP: f3c83d10 ESP: f3c83d00 [ 347.152907] DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 [ 347.152907] CR0: 80050033 CR2: 083158b0 CR3: 35146000 CR4: 000006b0 [ 347.152907] Stack: [ 347.152907] c167f9d9 f5acd080 000005b4 00000002 f3c83d20 c16013e6 f3c88f00 f5acd080 [ 347.152907] f3c83da0 c1603b5a f3c83d38 c10a0188 00000000 00000000 f3c83d84 c10acc85 [ 347.152907] c1ad5ec0 00000000 00000000 c1ad679c 010003e0 00000000 00000000 f3c88fc8 [ 347.152907] Call Trace: [ 347.152907] [] ? apic_timer_interrupt+0x2d/0x34 [ 347.152907] [] tcp_init_tso_segs+0x36/0x50 [ 347.152907] [] tcp_write_xmit+0x7a/0xbf0 [ 347.152907] [] ? up+0x28/0x40 [ 347.152907] [] ? console_unlock+0x295/0x480 [ 347.152907] [] ? vprintk_emit+0x1ef/0x4b0 [ 347.152907] [] __tcp_push_pending_frames+0x36/0xd0 [ 347.152907] [] tcp_push+0xf0/0x120 [ 347.152907] [] tcp_sendmsg+0xf1/0xbf0 [ 347.152907] [] ? kmem_cache_free+0xf0/0x120 [ 347.152907] [] ? __sigqueue_free+0x32/0x40 [ 347.152907] [] ? __sigqueue_free+0x32/0x40 [ 347.152907] [] ? do_wp_page+0x3e0/0x850 [ 347.152907] [] inet_sendmsg+0x4a/0xb0 [ 347.152907] [] ? handle_mm_fault+0x709/0xfb0 [ 347.152907] [] sock_aio_write+0xbb/0xd0 [ 347.152907] [] do_sync_write+0x69/0xa0 [ 347.152907] [] vfs_write+0x123/0x160 [ 347.152907] [] SyS_write+0x55/0xb0 [ 347.152907] [] sysenter_do_call+0x12/0x28 This can easily be reproduced with the following packetdrill-script (the "magic" with netem, sk_pacing and limit_output_bytes is done to prevent the kernel from pushing all segments, because hitting the limit without doing this is not so easy with packetdrill): 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 +0 bind(3, ..., ...) = 0 +0 listen(3, 1) = 0 +0 < S 0:0(0) win 32792 +0 > S. 0:0(0) ack 1 +0.1 < . 1:1(0) ack 1 win 65000 +0 accept(3, ..., ...) = 4 // This forces that not all segments of the snd-queue will be pushed +0 `tc qdisc add dev tun0 root netem delay 10ms` +0 `sysctl -w net.ipv4.tcp_limit_output_bytes=2` +0 setsockopt(4, SOL_SOCKET, 47, [2], 4) = 0 +0 write(4,...,10000) = 10000 +0 write(4,...,10000) = 10000 // Set tcp-repair stuff, particularly TCP_RECV_QUEUE +0 setsockopt(4, SOL_TCP, 19, [1], 4) = 0 +0 setsockopt(4, SOL_TCP, 20, [1], 4) = 0 // This now will make the write push the remaining segments +0 setsockopt(4, SOL_SOCKET, 47, [20000], 4) = 0 +0 `sysctl -w net.ipv4.tcp_limit_output_bytes=130000` // Now we will crash +0 write(4,...,1000) = 1000 This happens since ec3423257508 (tcp: fix retransmission in repair mode). Prior to that, the call to tcp_push was prevented by a check for tp->repair. The patch fixes it, by adding the new goto-label out_nopush. When exiting tcp_sendmsg and a push is not required, which is the case for tp->repair, we go to this label. When repairing and calling send() with TCP_RECV_QUEUE, the data is actually put in the receive-queue. So, no push is required because no data has been added to the send-queue. Cc: Andrew Vagin Cc: Pavel Emelyanov Fixes: ec3423257508 (tcp: fix retransmission in repair mode) Signed-off-by: Christoph Paasch Acked-by: Andrew Vagin Acked-by: Pavel Emelyanov Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 39bdb14b3214..5d4bd6ca3ab1 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1065,7 +1065,7 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, if (unlikely(tp->repair)) { if (tp->repair_queue == TCP_RECV_QUEUE) { copied = tcp_send_rcvq(sk, msg, size); - goto out; + goto out_nopush; } err = -EINVAL; @@ -1238,6 +1238,7 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, out: if (copied) tcp_push(sk, flags, mss_now, tp->nonagle); +out_nopush: release_sock(sk); return copied + copied_syn; From 08d9137a5e01f7c977b81feefc480da6ce9d7a4b Mon Sep 17 00:00:00 2001 From: Edward Allcutt Date: Mon, 30 Jun 2014 16:16:02 +0100 Subject: [PATCH 0054/1185] ipv4: icmp: Fix pMTU handling for rare case [ Upstream commit 68b7107b62983f2cff0948292429d5f5999df096 ] Some older router implementations still send Fragmentation Needed errors with the Next-Hop MTU field set to zero. This is explicitly described as an eventuality that hosts must deal with by the standard (RFC 1191) since older standards specified that those bits must be zero. Linux had a generic (for all of IPv4) implementation of the algorithm described in the RFC for searching a list of MTU plateaus for a good value. Commit 46517008e116 ("ipv4: Kill ip_rt_frag_needed().") removed this as part of the changes to remove the routing cache. Subsequently any Fragmentation Needed packet with a zero Next-Hop MTU has been discarded without being passed to the per-protocol handlers or notifying userspace for raw sockets. When there is a router which does not implement RFC 1191 on an MTU limited path then this results in stalled connections since large packets are discarded and the local protocols are not notified so they never attempt to lower the pMTU. One example I have seen is an OpenBSD router terminating IPSec tunnels. It's worth pointing out that this case is distinct from the BSD 4.2 bug which incorrectly calculated the Next-Hop MTU since the commit in question dismissed that as a valid concern. All of the per-protocols handlers implement the simple approach from RFC 1191 of immediately falling back to the minimum value. Although this is sub-optimal it is vastly preferable to connections hanging indefinitely. Remove the Next-Hop MTU != 0 check and allow such packets to follow the normal path. Fixes: 46517008e116 ("ipv4: Kill ip_rt_frag_needed().") Signed-off-by: Edward Allcutt Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/icmp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 76e10b47e053..ea78ef5ac352 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -697,8 +697,6 @@ static void icmp_unreach(struct sk_buff *skb) &iph->daddr); } else { info = ntohs(icmph->un.frag.mtu); - if (!info) - goto out; } break; case ICMP_SR_FAILED: From c86572ab06fe9e97e3867e0d0b1af4f1aa763adb Mon Sep 17 00:00:00 2001 From: Bernd Wachter Date: Tue, 1 Jul 2014 22:01:09 +0300 Subject: [PATCH 0055/1185] net: qmi_wwan: Add ID for Telewell TW-LTE 4G v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 8dcb4b1526747d8431f9895e153dd478c9d16186 ] There's a new version of the Telewell 4G modem working with, but not recognized by this driver. Signed-off-by: Bernd Wachter Acked-by: Bjørn Mork Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/qmi_wwan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 6fb0082b3308..bf2e5c19b9be 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -721,6 +721,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x19d2, 0x1424, 2)}, {QMI_FIXED_INTF(0x19d2, 0x1425, 2)}, {QMI_FIXED_INTF(0x19d2, 0x1426, 2)}, /* ZTE MF91 */ + {QMI_FIXED_INTF(0x19d2, 0x1428, 2)}, /* Telewell TW-LTE 4G v2 */ {QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */ {QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */ {QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */ From 2de8b0c1e08a88325380538d9dfdf9c42b281ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Thu, 17 Jul 2014 13:33:51 +0200 Subject: [PATCH 0056/1185] net: qmi_wwan: add two Sierra Wireless/Netgear devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 5343330010a892b76a97fd93ad3c455a4a32a7fb ] Add two device IDs found in an out-of-tree driver downloadable from Netgear. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/qmi_wwan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index bf2e5c19b9be..6c584f8a2268 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -647,6 +647,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x05c6, 0x9084, 4)}, {QMI_FIXED_INTF(0x05c6, 0x920d, 0)}, {QMI_FIXED_INTF(0x05c6, 0x920d, 5)}, + {QMI_FIXED_INTF(0x0846, 0x68a2, 8)}, {QMI_FIXED_INTF(0x12d1, 0x140c, 1)}, /* Huawei E173 */ {QMI_FIXED_INTF(0x12d1, 0x14ac, 1)}, /* Huawei E1820 */ {QMI_FIXED_INTF(0x16d8, 0x6003, 0)}, /* CMOTech 6003 */ @@ -734,6 +735,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1199, 0x901f, 8)}, /* Sierra Wireless EM7355 */ {QMI_FIXED_INTF(0x1199, 0x9041, 8)}, /* Sierra Wireless MC7305/MC7355 */ {QMI_FIXED_INTF(0x1199, 0x9051, 8)}, /* Netgear AirCard 340U */ + {QMI_FIXED_INTF(0x1199, 0x9057, 8)}, {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ {QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */ {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ From 48b60bb7b53285622a808193334a0d558ebbea87 Mon Sep 17 00:00:00 2001 From: dingtianhong Date: Wed, 2 Jul 2014 13:50:48 +0800 Subject: [PATCH 0057/1185] igmp: fix the problem when mc leave group [ Upstream commit 52ad353a5344f1f700c5b777175bdfa41d3cd65a ] The problem was triggered by these steps: 1) create socket, bind and then setsockopt for add mc group. mreq.imr_multiaddr.s_addr = inet_addr("255.0.0.37"); mreq.imr_interface.s_addr = inet_addr("192.168.1.2"); setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); 2) drop the mc group for this socket. mreq.imr_multiaddr.s_addr = inet_addr("255.0.0.37"); mreq.imr_interface.s_addr = inet_addr("0.0.0.0"); setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); 3) and then drop the socket, I found the mc group was still used by the dev: netstat -g Interface RefCnt Group --------------- ------ --------------------- eth2 1 255.0.0.37 Normally even though the IP_DROP_MEMBERSHIP return error, the mc group still need to be released for the netdev when drop the socket, but this process was broken when route default is NULL, the reason is that: The ip_mc_leave_group() will choose the in_dev by the imr_interface.s_addr, if input addr is NULL, the default route dev will be chosen, then the ifindex is got from the dev, then polling the inet->mc_list and return -ENODEV, but if the default route dev is NULL, the in_dev and ifIndex is both NULL, when polling the inet->mc_list, the mc group will be released from the mc_list, but the dev didn't dec the refcnt for this mc group, so when dropping the socket, the mc_list is NULL and the dev still keep this group. v1->v2: According Hideaki's suggestion, we should align with IPv6 (RFC3493) and BSDs, so I add the checking for the in_dev before polling the mc_list, make sure when we remove the mc group, dec the refcnt to the real dev which was using the mc address. The problem would never happened again. Signed-off-by: Ding Tianhong Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/igmp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 089b4af4fecc..38d63ca8a6b5 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1874,6 +1874,10 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) rtnl_lock(); in_dev = ip_mc_find_dev(net, imr); + if (!in_dev) { + ret = -ENODEV; + goto out; + } ifindex = imr->imr_ifindex; for (imlp = &inet->mc_list; (iml = rtnl_dereference(*imlp)) != NULL; @@ -1891,16 +1895,14 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) *imlp = iml->next_rcu; - if (in_dev) - ip_mc_dec_group(in_dev, group); + ip_mc_dec_group(in_dev, group); rtnl_unlock(); /* decrease mem now to avoid the memleak warning */ atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); kfree_rcu(iml, rcu); return 0; } - if (!in_dev) - ret = -ENODEV; +out: rtnl_unlock(); return ret; } From 76fd3c89bb35027abbd483929d92267479f7346a Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Wed, 2 Jul 2014 12:07:16 -0700 Subject: [PATCH 0058/1185] tcp: fix false undo corner cases [ Upstream commit 6e08d5e3c8236e7484229e46fdf92006e1dd4c49 ] The undo code assumes that, upon entering loss recovery, TCP 1) always retransmit something 2) the retransmission never fails locally (e.g., qdisc drop) so undo_marker is set in tcp_enter_recovery() and undo_retrans is incremented only when tcp_retransmit_skb() is successful. When the assumption is broken because TCP's cwnd is too small to retransmit or the retransmit fails locally. The next (DUP)ACK would incorrectly revert the cwnd and the congestion state in tcp_try_undo_dsack() or tcp_may_undo(). Subsequent (DUP)ACKs may enter the recovery state. The sender repeatedly enter and (incorrectly) exit recovery states if the retransmits continue to fail locally while receiving (DUP)ACKs. The fix is to initialize undo_retrans to -1 and start counting on the first retransmission. Always increment undo_retrans even if the retransmissions fail locally because they couldn't cause DSACKs to undo the cwnd reduction. Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_input.c | 8 ++++---- net/ipv4/tcp_output.c | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 19104e321029..ea7f52f3062d 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1075,7 +1075,7 @@ static bool tcp_check_dsack(struct sock *sk, const struct sk_buff *ack_skb, } /* D-SACK for already forgotten data... Do dumb counting. */ - if (dup_sack && tp->undo_marker && tp->undo_retrans && + if (dup_sack && tp->undo_marker && tp->undo_retrans > 0 && !after(end_seq_0, prior_snd_una) && after(end_seq_0, tp->undo_marker)) tp->undo_retrans--; @@ -1154,7 +1154,7 @@ static u8 tcp_sacktag_one(struct sock *sk, /* Account D-SACK for retransmitted packet. */ if (dup_sack && (sacked & TCPCB_RETRANS)) { - if (tp->undo_marker && tp->undo_retrans && + if (tp->undo_marker && tp->undo_retrans > 0 && after(end_seq, tp->undo_marker)) tp->undo_retrans--; if (sacked & TCPCB_SACKED_ACKED) @@ -1850,7 +1850,7 @@ static void tcp_clear_retrans_partial(struct tcp_sock *tp) tp->lost_out = 0; tp->undo_marker = 0; - tp->undo_retrans = 0; + tp->undo_retrans = -1; } void tcp_clear_retrans(struct tcp_sock *tp) @@ -2700,7 +2700,7 @@ static void tcp_enter_recovery(struct sock *sk, bool ece_ack) tp->prior_ssthresh = 0; tp->undo_marker = tp->snd_una; - tp->undo_retrans = tp->retrans_out; + tp->undo_retrans = tp->retrans_out ? : -1; if (inet_csk(sk)->icsk_ca_state < TCP_CA_CWR) { if (!ece_ack) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 4a4e8746d1b2..56e29f0e230e 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2428,13 +2428,15 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) if (!tp->retrans_stamp) tp->retrans_stamp = TCP_SKB_CB(skb)->when; - tp->undo_retrans += tcp_skb_pcount(skb); - /* snd_nxt is stored to detect loss of retransmitted segment, * see tcp_input.c tcp_sacktag_write_queue(). */ TCP_SKB_CB(skb)->ack_seq = tp->snd_nxt; } + + if (tp->undo_retrans < 0) + tp->undo_retrans = 0; + tp->undo_retrans += tcp_skb_pcount(skb); return err; } From d5f758a35b50da3c1629c18b149d23ef99c91f70 Mon Sep 17 00:00:00 2001 From: Andrey Utkin Date: Mon, 7 Jul 2014 23:22:50 +0300 Subject: [PATCH 0059/1185] appletalk: Fix socket referencing in skb [ Upstream commit 36beddc272c111689f3042bf3d10a64d8a805f93 ] Setting just skb->sk without taking its reference and setting a destructor is invalid. However, in the places where this was done, skb is used in a way not requiring skb->sk setting. So dropping the setting of skb->sk. Thanks to Eric Dumazet for correct solution. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=79441 Reported-by: Ed Martin Signed-off-by: Andrey Utkin Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/appletalk/ddp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 0018daccdea9..8799e171addf 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1489,8 +1489,6 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, goto drop; /* Queue packet (standard) */ - skb->sk = sock; - if (sock_queue_rcv_skb(sock, skb) < 0) goto drop; @@ -1644,7 +1642,6 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr if (!skb) goto out; - skb->sk = sk; skb_reserve(skb, ddp_dl->header_length); skb_reserve(skb, dev->hard_header_len); skb->dev = dev; From 8a8d269dd25aa12c24c76c99efea9f63edb88a90 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 8 Jul 2014 10:49:43 +0200 Subject: [PATCH 0060/1185] net: mvneta: fix operation in 10 Mbit/s mode [ Upstream commit 4d12bc63ab5e48c1d78fa13883cf6fefcea3afb1 ] As reported by Maggie Mae Roxas, the mvneta driver doesn't behave properly in 10 Mbit/s mode. This is due to a misconfiguration of the MVNETA_GMAC_AUTONEG_CONFIG register: bit MVNETA_GMAC_CONFIG_MII_SPEED must be set for a 100 Mbit/s speed, but cleared for a 10 Mbit/s speed, which the driver was not properly doing. This commit adjusts that by setting the MVNETA_GMAC_CONFIG_MII_SPEED bit only in 100 Mbit/s mode, and relying on the fact that all the speed related bits of this register are cleared at the beginning of the mvneta_adjust_link() function. This problem exists since c5aff18204da0 ("net: mvneta: driver for Marvell Armada 370/XP network unit") which is the commit that introduced the mvneta driver in the kernel. Cc: # v3.8+ Fixes: c5aff18204da0 ("net: mvneta: driver for Marvell Armada 370/XP network unit") Reported-by: Maggie Mae Roxas Cc: Maggie Mae Roxas Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/marvell/mvneta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index a602aeeb3acb..dd33a112f474 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2306,7 +2306,7 @@ static void mvneta_adjust_link(struct net_device *ndev) if (phydev->speed == SPEED_1000) val |= MVNETA_GMAC_CONFIG_GMII_SPEED; - else + else if (phydev->speed == SPEED_100) val |= MVNETA_GMAC_CONFIG_MII_SPEED; mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); From ba502e1e236f4043c849cd11c159ca783643a4de Mon Sep 17 00:00:00 2001 From: Thomas Fitzsimmons Date: Tue, 8 Jul 2014 19:44:07 -0400 Subject: [PATCH 0061/1185] net: mvneta: Fix big endian issue in mvneta_txq_desc_csum() [ Upstream commit 0a1985879437d14bda8c90d0dae3455c467d7642 ] This commit fixes the command value generated for CSUM calculation when running in big endian mode. The Ethernet protocol ID for IP was being unconditionally byte-swapped in the layer 3 protocol check (with swab16), which caused the mvneta driver to not function correctly in big endian mode. This patch byte-swaps the ID conditionally with htons. Cc: # v3.13+ Signed-off-by: Thomas Fitzsimmons Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/marvell/mvneta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index dd33a112f474..658613021919 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -1145,7 +1145,7 @@ static u32 mvneta_txq_desc_csum(int l3_offs, int l3_proto, command = l3_offs << MVNETA_TX_L3_OFF_SHIFT; command |= ip_hdr_len << MVNETA_TX_IP_HLEN_SHIFT; - if (l3_proto == swab16(ETH_P_IP)) + if (l3_proto == htons(ETH_P_IP)) command |= MVNETA_TXD_IP_CSUM; else command |= MVNETA_TX_L3_IP6; From c29503e9c04e142bdc14f25df4569486c9e67bd1 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 9 Jul 2014 10:31:22 -0700 Subject: [PATCH 0062/1185] netlink: Fix handling of error from netlink_dump(). [ Upstream commit ac30ef832e6af0505b6f0251a6659adcfa74975e ] netlink_dump() returns a negative errno value on error. Until now, netlink_recvmsg() directly recorded that negative value in sk->sk_err, but that's wrong since sk_err takes positive errno values. (This manifests as userspace receiving a positive return value from the recv() system call, falsely indicating success.) This bug was introduced in the commit that started checking the netlink_dump() return value, commit b44d211 (netlink: handle errors from netlink_dump()). Multithreaded Netlink dumps are one way to trigger this behavior in practice, as described in the commit message for the userspace workaround posted here: http://openvswitch.org/pipermail/dev/2014-June/042339.html This commit also fixes the same bug in netlink_poll(), introduced in commit cd1df525d (netlink: add flow control for memory mapped I/O). Signed-off-by: Ben Pfaff Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/netlink/af_netlink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index be34adde692f..5ed562dfe743 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -500,7 +500,7 @@ static unsigned int netlink_poll(struct file *file, struct socket *sock, while (nlk->cb != NULL && netlink_dump_space(nlk)) { err = netlink_dump(sk); if (err < 0) { - sk->sk_err = err; + sk->sk_err = -err; sk->sk_error_report(sk); break; } @@ -2272,7 +2272,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) { ret = netlink_dump(sk); if (ret) { - sk->sk_err = ret; + sk->sk_err = -ret; sk->sk_error_report(sk); } } From c771cc33f93bac30415cdb6d4f9619261fbd1a9c Mon Sep 17 00:00:00 2001 From: Suresh Reddy Date: Fri, 11 Jul 2014 14:03:01 +0530 Subject: [PATCH 0063/1185] be2net: set EQ DB clear-intr bit in be_open() [ Upstream commit 4cad9f3b61c7268fa89ab8096e23202300399b5d ] On BE3, if the clear-interrupt bit of the EQ doorbell is not set the first time it is armed, ocassionally we have observed that the EQ doesn't raise anymore interrupts even if it is in armed state. This patch fixes this by setting the clear-interrupt bit when EQs are armed for the first time in be_open(). Signed-off-by: Suresh Reddy Signed-off-by: Sathya Perla Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/emulex/benet/be_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 7371626c56a1..d81a7dbfeef6 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2663,7 +2663,7 @@ static int be_open(struct net_device *netdev) for_all_evt_queues(adapter, eqo, i) { napi_enable(&eqo->napi); - be_eq_notify(adapter, eqo->q.id, true, false, 0); + be_eq_notify(adapter, eqo->q.id, true, true, 0); } adapter->flags |= BE_FLAGS_NAPI_ENABLED; From 60008435941d4ad1a55763a9cb6d4e9c0e20f374 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Fri, 11 Jul 2014 08:45:27 -0400 Subject: [PATCH 0064/1185] tipc: clear 'next'-pointer of message fragments before reassembly [ Upstream commit 999417549c16dd0e3a382aa9f6ae61688db03181 ] If the 'next' pointer of the last fragment buffer in a message is not zeroed before reassembly, we risk ending up with a corrupt message, since the reassembly function itself isn't doing this. Currently, when a buffer is retrieved from the deferred queue of the broadcast link, the next pointer is not cleared, with the result as described above. This commit corrects this, and thereby fixes a bug that may occur when long broadcast messages are transmitted across dual interfaces. The bug has been present since 40ba3cdf542a469aaa9083fa041656e59b109b90 ("tipc: message reassembly using fragment chain") This commit should be applied to both net and net-next. Signed-off-by: Jon Maloy Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/tipc/bcast.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index e5f3da507823..bf2755419ec6 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -531,6 +531,7 @@ void tipc_bclink_recv_pkt(struct sk_buff *buf) buf = node->bclink.deferred_head; node->bclink.deferred_head = buf->next; + buf->next = NULL; node->bclink.deferred_size--; goto receive; } From b3b3ba5714ee9f77748223a0dbcf40b90e8e0773 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 12 Jul 2014 20:30:35 +0200 Subject: [PATCH 0065/1185] net: sctp: fix information leaks in ulpevent layer [ Upstream commit 8f2e5ae40ec193bc0a0ed99e95315c3eebca84ea ] While working on some other SCTP code, I noticed that some structures shared with user space are leaking uninitialized stack or heap buffer. In particular, struct sctp_sndrcvinfo has a 2 bytes hole between .sinfo_flags and .sinfo_ppid that remains unfilled by us in sctp_ulpevent_read_sndrcvinfo() when putting this into cmsg. But also struct sctp_remote_error contains a 2 bytes hole that we don't fill but place into a skb through skb_copy_expand() via sctp_ulpevent_make_remote_error(). Both structures are defined by the IETF in RFC6458: * Section 5.3.2. SCTP Header Information Structure: The sctp_sndrcvinfo structure is defined below: struct sctp_sndrcvinfo { uint16_t sinfo_stream; uint16_t sinfo_ssn; uint16_t sinfo_flags; <-- 2 bytes hole --> uint32_t sinfo_ppid; uint32_t sinfo_context; uint32_t sinfo_timetolive; uint32_t sinfo_tsn; uint32_t sinfo_cumtsn; sctp_assoc_t sinfo_assoc_id; }; * 6.1.3. SCTP_REMOTE_ERROR: A remote peer may send an Operation Error message to its peer. This message indicates a variety of error conditions on an association. The entire ERROR chunk as it appears on the wire is included in an SCTP_REMOTE_ERROR event. Please refer to the SCTP specification [RFC4960] and any extensions for a list of possible error formats. An SCTP error notification has the following format: struct sctp_remote_error { uint16_t sre_type; uint16_t sre_flags; uint32_t sre_length; uint16_t sre_error; <-- 2 bytes hole --> sctp_assoc_t sre_assoc_id; uint8_t sre_data[]; }; Fix this by setting both to 0 before filling them out. We also have other structures shared between user and kernel space in SCTP that contains holes (e.g. struct sctp_paddrthlds), but we copy that buffer over from user space first and thus don't need to care about it in that cases. While at it, we can also remove lengthy comments copied from the draft, instead, we update the comment with the correct RFC number where one can look it up. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/ulpevent.c | 122 ++++++-------------------------------------- 1 file changed, 15 insertions(+), 107 deletions(-) diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 10c018a5b9fe..ca907f2f5e5a 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -373,9 +373,10 @@ struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change( * specification [SCTP] and any extensions for a list of possible * error formats. */ -struct sctp_ulpevent *sctp_ulpevent_make_remote_error( - const struct sctp_association *asoc, struct sctp_chunk *chunk, - __u16 flags, gfp_t gfp) +struct sctp_ulpevent * +sctp_ulpevent_make_remote_error(const struct sctp_association *asoc, + struct sctp_chunk *chunk, __u16 flags, + gfp_t gfp) { struct sctp_ulpevent *event; struct sctp_remote_error *sre; @@ -394,8 +395,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_remote_error( /* Copy the skb to a new skb with room for us to prepend * notification with. */ - skb = skb_copy_expand(chunk->skb, sizeof(struct sctp_remote_error), - 0, gfp); + skb = skb_copy_expand(chunk->skb, sizeof(*sre), 0, gfp); /* Pull off the rest of the cause TLV from the chunk. */ skb_pull(chunk->skb, elen); @@ -406,62 +406,21 @@ struct sctp_ulpevent *sctp_ulpevent_make_remote_error( event = sctp_skb2event(skb); sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize); - sre = (struct sctp_remote_error *) - skb_push(skb, sizeof(struct sctp_remote_error)); + sre = (struct sctp_remote_error *) skb_push(skb, sizeof(*sre)); /* Trim the buffer to the right length. */ - skb_trim(skb, sizeof(struct sctp_remote_error) + elen); + skb_trim(skb, sizeof(*sre) + elen); - /* Socket Extensions for SCTP - * 5.3.1.3 SCTP_REMOTE_ERROR - * - * sre_type: - * It should be SCTP_REMOTE_ERROR. - */ + /* RFC6458, Section 6.1.3. SCTP_REMOTE_ERROR */ + memset(sre, 0, sizeof(*sre)); sre->sre_type = SCTP_REMOTE_ERROR; - - /* - * Socket Extensions for SCTP - * 5.3.1.3 SCTP_REMOTE_ERROR - * - * sre_flags: 16 bits (unsigned integer) - * Currently unused. - */ sre->sre_flags = 0; - - /* Socket Extensions for SCTP - * 5.3.1.3 SCTP_REMOTE_ERROR - * - * sre_length: sizeof (__u32) - * - * This field is the total length of the notification data, - * including the notification header. - */ sre->sre_length = skb->len; - - /* Socket Extensions for SCTP - * 5.3.1.3 SCTP_REMOTE_ERROR - * - * sre_error: 16 bits (unsigned integer) - * This value represents one of the Operational Error causes defined in - * the SCTP specification, in network byte order. - */ sre->sre_error = cause; - - /* Socket Extensions for SCTP - * 5.3.1.3 SCTP_REMOTE_ERROR - * - * sre_assoc_id: sizeof (sctp_assoc_t) - * - * The association id field, holds the identifier for the association. - * All notifications for a given association have the same association - * identifier. For TCP style socket, this field is ignored. - */ sctp_ulpevent_set_owner(event, asoc); sre->sre_assoc_id = sctp_assoc2id(asoc); return event; - fail: return NULL; } @@ -906,7 +865,9 @@ __u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event) return notification->sn_header.sn_type; } -/* Copy out the sndrcvinfo into a msghdr. */ +/* RFC6458, Section 5.3.2. SCTP Header Information Structure + * (SCTP_SNDRCV, DEPRECATED) + */ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event, struct msghdr *msghdr) { @@ -915,74 +876,21 @@ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event, if (sctp_ulpevent_is_notification(event)) return; - /* Sockets API Extensions for SCTP - * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV) - * - * sinfo_stream: 16 bits (unsigned integer) - * - * For recvmsg() the SCTP stack places the message's stream number in - * this value. - */ + memset(&sinfo, 0, sizeof(sinfo)); sinfo.sinfo_stream = event->stream; - /* sinfo_ssn: 16 bits (unsigned integer) - * - * For recvmsg() this value contains the stream sequence number that - * the remote endpoint placed in the DATA chunk. For fragmented - * messages this is the same number for all deliveries of the message - * (if more than one recvmsg() is needed to read the message). - */ sinfo.sinfo_ssn = event->ssn; - /* sinfo_ppid: 32 bits (unsigned integer) - * - * In recvmsg() this value is - * the same information that was passed by the upper layer in the peer - * application. Please note that byte order issues are NOT accounted - * for and this information is passed opaquely by the SCTP stack from - * one end to the other. - */ sinfo.sinfo_ppid = event->ppid; - /* sinfo_flags: 16 bits (unsigned integer) - * - * This field may contain any of the following flags and is composed of - * a bitwise OR of these values. - * - * recvmsg() flags: - * - * SCTP_UNORDERED - This flag is present when the message was sent - * non-ordered. - */ sinfo.sinfo_flags = event->flags; - /* sinfo_tsn: 32 bit (unsigned integer) - * - * For the receiving side, this field holds a TSN that was - * assigned to one of the SCTP Data Chunks. - */ sinfo.sinfo_tsn = event->tsn; - /* sinfo_cumtsn: 32 bit (unsigned integer) - * - * This field will hold the current cumulative TSN as - * known by the underlying SCTP layer. Note this field is - * ignored when sending and only valid for a receive - * operation when sinfo_flags are set to SCTP_UNORDERED. - */ sinfo.sinfo_cumtsn = event->cumtsn; - /* sinfo_assoc_id: sizeof (sctp_assoc_t) - * - * The association handle field, sinfo_assoc_id, holds the identifier - * for the association announced in the COMMUNICATION_UP notification. - * All notifications for a given association have the same identifier. - * Ignored for one-to-one style sockets. - */ sinfo.sinfo_assoc_id = sctp_assoc2id(event->asoc); - - /* context value that is set via SCTP_CONTEXT socket option. */ + /* Context value that is set via SCTP_CONTEXT socket option. */ sinfo.sinfo_context = event->asoc->default_rcv_context; - /* These fields are not used while receiving. */ sinfo.sinfo_timetolive = 0; put_cmsg(msghdr, IPPROTO_SCTP, SCTP_SNDRCV, - sizeof(struct sctp_sndrcvinfo), (void *)&sinfo); + sizeof(sinfo), &sinfo); } /* Do accounting for bytes received and hold a reference to the association From dc1a6f415ed2405f4589b238d9b08a7d6613c8af Mon Sep 17 00:00:00 2001 From: Christoph Schulz Date: Sun, 13 Jul 2014 00:53:15 +0200 Subject: [PATCH 0066/1185] net: pppoe: use correct channel MTU when using Multilink PPP [ Upstream commit a8a3e41c67d24eb12f9ab9680cbb85e24fcd9711 ] The PPP channel MTU is used with Multilink PPP when ppp_mp_explode() (see ppp_generic module) tries to determine how big a fragment might be. According to RFC 1661, the MTU excludes the 2-byte PPP protocol field, see the corresponding comment and code in ppp_mp_explode(): /* * hdrlen includes the 2-byte PPP protocol field, but the * MTU counts only the payload excluding the protocol field. * (RFC1661 Section 2) */ mtu = pch->chan->mtu - (hdrlen - 2); However, the pppoe module *does* include the PPP protocol field in the channel MTU, which is wrong as it causes the PPP payload to be 1-2 bytes too big under certain circumstances (one byte if PPP protocol compression is used, two otherwise), causing the generated Ethernet packets to be dropped. So the pppoe module has to subtract two bytes from the channel MTU. This error only manifests itself when using Multilink PPP, as otherwise the channel MTU is not used anywhere. In the following, I will describe how to reproduce this bug. We configure two pppd instances for multilink PPP over two PPPoE links, say eth2 and eth3, with a MTU of 1492 bytes for each link and a MRRU of 2976 bytes. (This MRRU is computed by adding the two link MTUs and subtracting the MP header twice, which is 4 bytes long.) The necessary pppd statements on both sides are "multilink mtu 1492 mru 1492 mrru 2976". On the client side, we additionally need "plugin rp-pppoe.so eth2" and "plugin rp-pppoe.so eth3", respectively; on the server side, we additionally need to start two pppoe-server instances to be able to establish two PPPoE sessions, one over eth2 and one over eth3. We set the MTU of the PPP network interface to the MRRU (2976) on both sides of the connection in order to make use of the higher bandwidth. (If we didn't do that, IP fragmentation would kick in, which we want to avoid.) Now we send a ICMPv4 echo request with a payload of 2948 bytes from client to server over the PPP link. This results in the following network packet: 2948 (echo payload) + 8 (ICMPv4 header) + 20 (IPv4 header) --------------------- 2976 (PPP payload) These 2976 bytes do not exceed the MTU of the PPP network interface, so the IP packet is not fragmented. Now the multilink PPP code in ppp_mp_explode() prepends one protocol byte (0x21 for IPv4), making the packet one byte bigger than the negotiated MRRU. So this packet would have to be divided in three fragments. But this does not happen as each link MTU is assumed to be two bytes larger. So this packet is diveded into two fragments only, one of size 1489 and one of size 1488. Now we have for that bigger fragment: 1489 (PPP payload) + 4 (MP header) + 2 (PPP protocol field for the MP payload (0x3d)) + 6 (PPPoE header) -------------------------- 1501 (Ethernet payload) This packet exceeds the link MTU and is discarded. If one configures the link MTU on the client side to 1501, one can see the discarded Ethernet frames with tcpdump running on the client. A ping -s 2948 -c 1 192.168.15.254 leads to the smaller fragment that is correctly received on the server side: (tcpdump -vvvne -i eth3 pppoes and ppp proto 0x3d) 52:54:00:ad:87:fd > 52:54:00:79:5c:d0, ethertype PPPoE S (0x8864), length 1514: PPPoE [ses 0x3] MLPPP (0x003d), length 1494: seq 0x000, Flags [end], length 1492 and to the bigger fragment that is not received on the server side: (tcpdump -vvvne -i eth2 pppoes and ppp proto 0x3d) 52:54:00:70:9e:89 > 52:54:00:5d:6f:b0, ethertype PPPoE S (0x8864), length 1515: PPPoE [ses 0x5] MLPPP (0x003d), length 1495: seq 0x000, Flags [begin], length 1493 With the patch below, we correctly obtain three fragments: 52:54:00:ad:87:fd > 52:54:00:79:5c:d0, ethertype PPPoE S (0x8864), length 1514: PPPoE [ses 0x1] MLPPP (0x003d), length 1494: seq 0x000, Flags [begin], length 1492 52:54:00:70:9e:89 > 52:54:00:5d:6f:b0, ethertype PPPoE S (0x8864), length 1514: PPPoE [ses 0x1] MLPPP (0x003d), length 1494: seq 0x000, Flags [none], length 1492 52:54:00:ad:87:fd > 52:54:00:79:5c:d0, ethertype PPPoE S (0x8864), length 27: PPPoE [ses 0x1] MLPPP (0x003d), length 7: seq 0x000, Flags [end], length 5 And the ICMPv4 echo request is successfully received at the server side: IP (tos 0x0, ttl 64, id 21925, offset 0, flags [DF], proto ICMP (1), length 2976) 192.168.222.2 > 192.168.15.254: ICMP echo request, id 30530, seq 0, length 2956 The bug was introduced in commit c9aa6895371b2a257401f59d3393c9f7ac5a8698 ("[PPPOE]: Advertise PPPoE MTU") from the very beginning. This patch applies to 3.10 upwards but the fix can be applied (with minor modifications) to kernels as old as 2.6.32. Signed-off-by: Christoph Schulz Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ppp/pppoe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 6839fb07a4c9..becfa3ef7fdc 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -675,7 +675,7 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, po->chan.hdrlen = (sizeof(struct pppoe_hdr) + dev->hard_header_len); - po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr); + po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr) - 2; po->chan.private = sk; po->chan.ops = &pppoe_chan_ops; From 1c81dac91e065e39413f8ff5d22b444087b0ed11 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Wed, 16 Jul 2014 10:02:26 -0400 Subject: [PATCH 0067/1185] sunvnet: clean up objects created in vnet_new() on vnet_exit() [ Upstream commit a4b70a07ed12a71131cab7adce2ce91c71b37060 ] Nothing cleans up the objects created by vnet_new(), they are completely leaked. vnet_exit(), after doing the vio_unregister_driver() to clean up ports, should call a helper function that iterates over vnet_list and cleans up those objects. This includes unregister_netdevice() as well as free_netdev(). Signed-off-by: Sowmini Varadhan Acked-by: Dave Kleikamp Reviewed-by: Karl Volz Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/sun/sunvnet.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index 3df56840a3b9..398faff8be7a 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -1083,6 +1083,24 @@ static struct vnet *vnet_find_or_create(const u64 *local_mac) return vp; } +static void vnet_cleanup(void) +{ + struct vnet *vp; + struct net_device *dev; + + mutex_lock(&vnet_list_mutex); + while (!list_empty(&vnet_list)) { + vp = list_first_entry(&vnet_list, struct vnet, list); + list_del(&vp->list); + dev = vp->dev; + /* vio_unregister_driver() should have cleaned up port_list */ + BUG_ON(!list_empty(&vp->port_list)); + unregister_netdev(dev); + free_netdev(dev); + } + mutex_unlock(&vnet_list_mutex); +} + static const char *local_mac_prop = "local-mac-address"; static struct vnet *vnet_find_parent(struct mdesc_handle *hp, @@ -1240,7 +1258,6 @@ static int vnet_port_remove(struct vio_dev *vdev) kfree(port); - unregister_netdev(vp->dev); } return 0; } @@ -1268,6 +1285,7 @@ static int __init vnet_init(void) static void __exit vnet_exit(void) { vio_unregister_driver(&vnet_port_driver); + vnet_cleanup(); } module_init(vnet_init); From 443ba0f457a6c5f2eeec64fe3f80efc7cbb10133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Sch=C3=B6lling?= Date: Sat, 7 Jun 2014 23:57:25 +0200 Subject: [PATCH 0068/1185] dns_resolver: assure that dns_query() result is null-terminated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 84a7c0b1db1c17d5ded8d3800228a608e1070b40 ] dns_query() credulously assumes that keys are null-terminated and returns a copy of a memory block that is off by one. Signed-off-by: Manuel Schölling Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/dns_resolver/dns_query.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c index c32be292c7e3..ede0e2d7412e 100644 --- a/net/dns_resolver/dns_query.c +++ b/net/dns_resolver/dns_query.c @@ -150,7 +150,9 @@ int dns_query(const char *type, const char *name, size_t namelen, if (!*_result) goto put; - memcpy(*_result, upayload->data, len + 1); + memcpy(*_result, upayload->data, len); + *_result[len] = '\0'; + if (_expiry) *_expiry = rkey->expiry; From 72a0a659b5b2aa6a8d3ade5a7fbd0578bd1dd749 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 21 Jul 2014 00:06:48 +0100 Subject: [PATCH 0069/1185] dns_resolver: Null-terminate the right string [ Upstream commit 640d7efe4c08f06c4ae5d31b79bd8740e7f6790a ] *_result[len] is parsed as *(_result[len]) which is not at all what we want to touch here. Signed-off-by: Ben Hutchings Fixes: 84a7c0b1db1c ("dns_resolver: assure that dns_query() result is null-terminated") Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/dns_resolver/dns_query.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c index ede0e2d7412e..2022b46ab38f 100644 --- a/net/dns_resolver/dns_query.c +++ b/net/dns_resolver/dns_query.c @@ -151,7 +151,7 @@ int dns_query(const char *type, const char *name, size_t namelen, goto put; memcpy(*_result, upayload->data, len); - *_result[len] = '\0'; + (*_result)[len] = '\0'; if (_expiry) *_expiry = rkey->expiry; From 36b526620dcc8e01330964ef88c1ec5217027781 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 21 Jul 2014 07:17:42 +0200 Subject: [PATCH 0070/1185] ipv4: fix buffer overflow in ip_options_compile() [ Upstream commit 10ec9472f05b45c94db3c854d22581a20b97db41 ] There is a benign buffer overflow in ip_options_compile spotted by AddressSanitizer[1] : Its benign because we always can access one extra byte in skb->head (because header is followed by struct skb_shared_info), and in this case this byte is not even used. [28504.910798] ================================================================== [28504.912046] AddressSanitizer: heap-buffer-overflow in ip_options_compile [28504.913170] Read of size 1 by thread T15843: [28504.914026] [] ip_options_compile+0x121/0x9c0 [28504.915394] [] ip_options_get_from_user+0xad/0x120 [28504.916843] [] do_ip_setsockopt.isra.15+0x8df/0x1630 [28504.918175] [] ip_setsockopt+0x30/0xa0 [28504.919490] [] tcp_setsockopt+0x5b/0x90 [28504.920835] [] sock_common_setsockopt+0x5f/0x70 [28504.922208] [] SyS_setsockopt+0xa2/0x140 [28504.923459] [] system_call_fastpath+0x16/0x1b [28504.924722] [28504.925106] Allocated by thread T15843: [28504.925815] [] ip_options_get_from_user+0x35/0x120 [28504.926884] [] do_ip_setsockopt.isra.15+0x8df/0x1630 [28504.927975] [] ip_setsockopt+0x30/0xa0 [28504.929175] [] tcp_setsockopt+0x5b/0x90 [28504.930400] [] sock_common_setsockopt+0x5f/0x70 [28504.931677] [] SyS_setsockopt+0xa2/0x140 [28504.932851] [] system_call_fastpath+0x16/0x1b [28504.934018] [28504.934377] The buggy address ffff880026382828 is located 0 bytes to the right [28504.934377] of 40-byte region [ffff880026382800, ffff880026382828) [28504.937144] [28504.937474] Memory state around the buggy address: [28504.938430] ffff880026382300: ........ rrrrrrrr rrrrrrrr rrrrrrrr [28504.939884] ffff880026382400: ffffffff rrrrrrrr rrrrrrrr rrrrrrrr [28504.941294] ffff880026382500: .....rrr rrrrrrrr rrrrrrrr rrrrrrrr [28504.942504] ffff880026382600: ffffffff rrrrrrrr rrrrrrrr rrrrrrrr [28504.943483] ffff880026382700: ffffffff rrrrrrrr rrrrrrrr rrrrrrrr [28504.944511] >ffff880026382800: .....rrr rrrrrrrr rrrrrrrr rrrrrrrr [28504.945573] ^ [28504.946277] ffff880026382900: ffffffff rrrrrrrr rrrrrrrr rrrrrrrr [28505.094949] ffff880026382a00: ffffffff rrrrrrrr rrrrrrrr rrrrrrrr [28505.096114] ffff880026382b00: ffffffff rrrrrrrr rrrrrrrr rrrrrrrr [28505.097116] ffff880026382c00: ffffffff rrrrrrrr rrrrrrrr rrrrrrrr [28505.098472] ffff880026382d00: ffffffff rrrrrrrr rrrrrrrr rrrrrrrr [28505.099804] Legend: [28505.100269] f - 8 freed bytes [28505.100884] r - 8 redzone bytes [28505.101649] . - 8 allocated bytes [28505.102406] x=1..7 - x allocated bytes + (8-x) redzone bytes [28505.103637] ================================================================== [1] https://code.google.com/p/address-sanitizer/wiki/AddressSanitizerForKernel Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ip_options.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index ec7264514a82..089ed81d1878 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -288,6 +288,10 @@ int ip_options_compile(struct net *net, optptr++; continue; } + if (unlikely(l < 2)) { + pp_ptr = optptr; + goto error; + } optlen = optptr[1]; if (optlen<2 || optlen>l) { pp_ptr = optptr; From 6b3f0da3d2555bb9a2c03865f4af3324f2b08f44 Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Wed, 25 Jun 2014 10:09:07 +0900 Subject: [PATCH 0071/1185] perf/x86/intel: ignore CondChgd bit to avoid false NMI handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b292d7a10487aee6e74b1c18b8d95b92f40d4a4f upstream. Currently, any NMI is falsely handled by a NMI handler of NMI watchdog if CondChgd bit in MSR_CORE_PERF_GLOBAL_STATUS MSR is set. For example, we use external NMI to make system panic to get crash dump, but in this case, the external NMI is falsely handled do to the issue. This commit deals with the issue simply by ignoring CondChgd bit. Here is explanation in detail. On x86 NMI watchdog uses performance monitoring feature to periodically signal NMI each time performance counter gets overflowed. intel_pmu_handle_irq() is called as a NMI_LOCAL handler from a NMI handler of NMI watchdog, perf_event_nmi_handler(). It identifies an owner of a given NMI by looking at overflow status bits in MSR_CORE_PERF_GLOBAL_STATUS MSR. If some of the bits are set, then it handles the given NMI as its own NMI. The problem is that the intel_pmu_handle_irq() doesn't distinguish CondChgd bit from other bits. Unlike the other status bits, CondChgd bit doesn't represent overflow status for performance counters. Thus, CondChgd bit cannot be thought of as a mark indicating a given NMI is NMI watchdog's. As a result, if CondChgd bit is set, any NMI is falsely handled by the NMI handler of NMI watchdog. Also, if type of the falsely handled NMI is either NMI_UNKNOWN, NMI_SERR or NMI_IO_CHECK, the corresponding action is never performed until CondChgd bit is cleared. I noticed this behavior on systems with Ivy Bridge processors: Intel Xeon CPU E5-2630 v2 and Intel Xeon CPU E7-8890 v2. On both systems, CondChgd bit in MSR_CORE_PERF_GLOBAL_STATUS MSR has already been set in the beginning at boot. Then the CondChgd bit is immediately cleared by next wrmsr to MSR_CORE_PERF_GLOBAL_CTRL MSR and appears to remain 0. On the other hand, on older processors such as Nehalem, Xeon E7540, CondChgd bit is not set in the beginning at boot. I'm not sure about exact behavior of CondChgd bit, in particular when this bit is set. Although I read Intel System Programmer's Manual to figure out that, the descriptions I found are: In 18.9.1: "The MSR_PERF_GLOBAL_STATUS MSR also provides a ¡sticky bit¢ to indicate changes to the state of performancmonitoring hardware" In Table 35-2 IA-32 Architectural MSRs 63 CondChg: status bits of this register has changed. These are different from the bahviour I see on the actual system as I explained above. At least, I think ignoring CondChgd bit should be enough for NMI watchdog perspective. Signed-off-by: HATAYAMA Daisuke Acked-by: Don Zickus Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Linus Torvalds Cc: linux-kernel@vger.kernel.org Link: http://lkml.kernel.org/r/20140625.103503.409316067.d.hatayama@jp.fujitsu.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/perf_event_intel.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index a9e22073bd56..b45ac6affa9c 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -1198,6 +1198,15 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) intel_pmu_lbr_read(); + /* + * CondChgd bit 63 doesn't mean any overflow status. Ignore + * and clear the bit. + */ + if (__test_and_clear_bit(63, (unsigned long *)&status)) { + if (!status) + goto done; + } + /* * PEBS overflow sets bit 62 in the global status register */ From 8503df8d0c177e9e1c5468663b8954205ac069c9 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 20 Jun 2014 11:45:25 -0700 Subject: [PATCH 0072/1185] mwifiex: fix Tx timeout issue commit d76744a93246eccdca1106037e8ee29debf48277 upstream. https://bugzilla.kernel.org/show_bug.cgi?id=70191 https://bugzilla.kernel.org/show_bug.cgi?id=77581 It is observed that sometimes Tx packet is downloaded without adding driver's txpd header. This results in firmware parsing garbage data as packet length. Sometimes firmware is unable to read the packet if length comes out as invalid. This stops further traffic and timeout occurs. The root cause is uninitialized fields in tx_info(skb->cb) of packet used to get garbage values. In this case if MWIFIEX_BUF_FLAG_REQUEUED_PKT flag is mistakenly set, txpd header was skipped. This patch makes sure that tx_info is correctly initialized to fix the problem. Reported-by: Andrew Wiley Reported-by: Linus Gasser Reported-by: Michael Hirsch Tested-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Maithili Hinge Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/mwifiex/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index fc3fe8ddcf62..83c61964d082 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -501,6 +501,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) } tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; From 16de9ea386e182600a473a57edde7579a24d4664 Mon Sep 17 00:00:00 2001 From: Martin Lau Date: Mon, 9 Jun 2014 23:06:42 -0700 Subject: [PATCH 0073/1185] ring-buffer: Fix polling on trace_pipe commit 97b8ee845393701edc06e27ccec2876ff9596019 upstream. ring_buffer_poll_wait() should always put the poll_table to its wait_queue even there is immediate data available. Otherwise, the following epoll and read sequence will eventually hang forever: 1. Put some data to make the trace_pipe ring_buffer read ready first 2. epoll_ctl(efd, EPOLL_CTL_ADD, trace_pipe_fd, ee) 3. epoll_wait() 4. read(trace_pipe_fd) till EAGAIN 5. Add some more data to the trace_pipe ring_buffer 6. epoll_wait() -> this epoll_wait() will block forever ~ During the epoll_ctl(efd, EPOLL_CTL_ADD,...) call in step 2, ring_buffer_poll_wait() returns immediately without adding poll_table, which has poll_table->_qproc pointing to ep_poll_callback(), to its wait_queue. ~ During the epoll_wait() call in step 3 and step 6, ring_buffer_poll_wait() cannot add ep_poll_callback() to its wait_queue because the poll_table->_qproc is NULL and it is how epoll works. ~ When there is new data available in step 6, ring_buffer does not know it has to call ep_poll_callback() because it is not in its wait queue. Hence, block forever. Other poll implementation seems to call poll_wait() unconditionally as the very first thing to do. For example, tcp_poll() in tcp.c. Link: http://lkml.kernel.org/p/20140610060637.GA14045@devbig242.prn2.facebook.com Fixes: 2a2cc8f7c4d0 "ftrace: allow the event pipe to be polled" Reviewed-by: Chris Mason Signed-off-by: Martin Lau Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/ring_buffer.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 8e94c1102636..4063d5fe5e44 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -616,10 +616,6 @@ int ring_buffer_poll_wait(struct ring_buffer *buffer, int cpu, struct ring_buffer_per_cpu *cpu_buffer; struct rb_irq_work *work; - if ((cpu == RING_BUFFER_ALL_CPUS && !ring_buffer_empty(buffer)) || - (cpu != RING_BUFFER_ALL_CPUS && !ring_buffer_empty_cpu(buffer, cpu))) - return POLLIN | POLLRDNORM; - if (cpu == RING_BUFFER_ALL_CPUS) work = &buffer->irq_work; else { From db9e4bf382abcd9b57a283084072131f6569a802 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Thu, 3 Jul 2014 13:58:52 +0200 Subject: [PATCH 0074/1185] irqchip: gic: Add support for cortex a7 compatible string commit a97e8027b1d28eafe6bafe062556c1ec926a49c6 upstream. Patch 0a68214b "ARM: DT: Add binding for GIC virtualization extentions (VGIC)" added the "arm,cortex-a7-gic" compatible string, but the corresponding IRQCHIP_DECLARE was never added to the gic driver. To let real Cortex-A7 SoCs use it, add the necessary declaration to the device driver. Signed-off-by: Matthias Brugger Link: https://lkml.kernel.org/r/1404388732-28890-1-git-send-email-matthias.bgg@gmail.com Fixes: 0a68214b76ca ("ARM: DT: Add binding for GIC virtualization extentions (VGIC)") Signed-off-by: Jason Cooper Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-gic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 4e11218d644e..493ca430f093 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -858,6 +858,7 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent) } IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init); IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init); +IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init); IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init); IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init); From 4003b69e90b58f35a60061e6f78a4d98a6ff2727 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 17 Jul 2014 17:23:44 +0200 Subject: [PATCH 0075/1185] irqchip: gic: Fix core ID calculation when topology is read from DT commit 29e697b11853d3f83b1864ae385abdad4aa2c361 upstream. Certain GIC implementation, namely those found on earlier, single cluster, Exynos SoCs, have registers mapped without per-CPU banking, which means that the driver needs to use different offset for each CPU. Currently the driver calculates the offset by multiplying value returned by cpu_logical_map() by CPU offset parsed from DT. This is correct when CPU topology is not specified in DT and aforementioned function returns core ID alone. However when DT contains CPU topology, the function changes to return cluster ID as well, which is non-zero on mentioned SoCs and so breaks the calculation in GIC driver. This patch fixes this by masking out cluster ID in CPU offset calculation so that only core ID is considered. Multi-cluster Exynos SoCs already have banked GIC implementations, so this simple fix should be enough. Reported-by: Lorenzo Pieralisi Reported-by: Bartlomiej Zolnierkiewicz Signed-off-by: Tomasz Figa Fixes: db0d4db22a78d ("ARM: gic: allow GIC to support non-banked setups") Link: https://lkml.kernel.org/r/1405610624-18722-1-git-send-email-t.figa@samsung.com Signed-off-by: Jason Cooper Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-gic.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 493ca430f093..c8ee1cb023b8 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -754,7 +755,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, } for_each_possible_cpu(cpu) { - unsigned long offset = percpu_offset * cpu_logical_map(cpu); + u32 mpidr = cpu_logical_map(cpu); + u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); + unsigned long offset = percpu_offset * core_id; *per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset; *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset; } From e1bb259863012b0498678e0c319a9420d2108215 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 15 Jul 2014 09:48:53 -0400 Subject: [PATCH 0076/1185] drm/radeon: set default bl level to something reasonable commit 201bb62402e0227375c655446ea04fcd0acf7287 upstream. If the value in the scratch register is 0, set it to the max level. This fixes an issue where the console fb blanking code calls back into the backlight driver on unblank and then sets the backlight level to 0 after the driver has already set the mode and enabled the backlight. bugs: https://bugs.freedesktop.org/show_bug.cgi?id=81382 https://bugs.freedesktop.org/show_bug.cgi?id=70207 Signed-off-by: Alex Deucher Tested-by: David Heidelberger Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atombios_encoders.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index 5802d7486354..1b564d7e4191 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -183,7 +183,6 @@ void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder, struct backlight_properties props; struct radeon_backlight_privdata *pdata; struct radeon_encoder_atom_dig *dig; - u8 backlight_level; char bl_name[16]; /* Mac laptops with multiple GPUs use the gmux driver for backlight @@ -222,12 +221,17 @@ void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder, pdata->encoder = radeon_encoder; - backlight_level = radeon_atom_get_backlight_level_from_reg(rdev); - dig = radeon_encoder->enc_priv; dig->bl_dev = bd; bd->props.brightness = radeon_atom_backlight_get_brightness(bd); + /* Set a reasonable default here if the level is 0 otherwise + * fbdev will attempt to turn the backlight on after console + * unblanking and it will try and restore 0 which turns the backlight + * off again. + */ + if (bd->props.brightness == 0) + bd->props.brightness = RADEON_MAX_BL_LEVEL; bd->props.power = FB_BLANK_UNBLANK; backlight_update_status(bd); From 1ed9cbc93c613efa69df58a1d4c8037adb105f43 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Mon, 12 May 2014 16:35:39 +0800 Subject: [PATCH 0077/1185] drm/qxl: return IRQ_NONE if it was not our irq commit fbb60fe35ad579b511de8604b06a30b43846473b upstream. Return IRQ_NONE if it was not our irq. This is necessary for the case when qxl is sharing irq line with a device A in a crash kernel. If qxl is initialized before A and A's irq was raised during this gap, returning IRQ_HANDLED in this case will cause this irq to be raised again after EOI since kernel think it was handled but in fact it was not. Cc: Gerd Hoffmann Signed-off-by: Jason Wang Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/qxl/qxl_irq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/qxl/qxl_irq.c b/drivers/gpu/drm/qxl/qxl_irq.c index 21393dc4700a..f4b6b89b98f3 100644 --- a/drivers/gpu/drm/qxl/qxl_irq.c +++ b/drivers/gpu/drm/qxl/qxl_irq.c @@ -33,6 +33,9 @@ irqreturn_t qxl_irq_handler(DRM_IRQ_ARGS) pending = xchg(&qdev->ram_header->int_pending, 0); + if (!pending) + return IRQ_NONE; + atomic_inc(&qdev->irq_received); if (pending & QXL_INTERRUPT_DISPLAY) { From c08ca3d473e8500ca0128688ad311f06594430d2 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 14 Jul 2014 17:57:19 -0400 Subject: [PATCH 0078/1185] drm/radeon: avoid leaking edid data commit 0ac66effe7fcdee55bda6d5d10d3372c95a41920 upstream. In some cases we fetch the edid in the detect() callback in order to determine what sort of monitor is connected. If that happens, don't fetch the edid again in the get_modes() callback or we will leak the edid. Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_display.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 06ccfe477650..a84de32a91f5 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -688,6 +688,10 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) struct radeon_device *rdev = dev->dev_private; int ret = 0; + /* don't leak the edid if we already fetched it in detect() */ + if (radeon_connector->edid) + goto got_edid; + /* on hw with routers, select right port */ if (radeon_connector->router.ddc_valid) radeon_router_select_ddc_port(radeon_connector); @@ -727,6 +731,7 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) radeon_connector->edid = radeon_bios_get_hardcoded_edid(rdev); } if (radeon_connector->edid) { +got_edid: drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid); ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid); drm_edid_to_eld(&radeon_connector->base, radeon_connector->edid); From c933192733ddf436c578183ca0687c7db5fff468 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 7 Jul 2014 14:06:11 -0700 Subject: [PATCH 0079/1185] alarmtimer: Fix bug where relative alarm timers were treated as absolute commit 16927776ae757d0d132bdbfabbfe2c498342bd59 upstream. Sharvil noticed with the posix timer_settime interface, using the CLOCK_REALTIME_ALARM or CLOCK_BOOTTIME_ALARM clockid, if the users tried to specify a relative time timer, it would incorrectly be treated as absolute regardless of the state of the flags argument. This patch corrects this, properly checking the absolute/relative flag, as well as adds further error checking that no invalid flag bits are set. Reported-by: Sharvil Nanavati Signed-off-by: John Stultz Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Prarit Bhargava Cc: Sharvil Nanavati Link: http://lkml.kernel.org/r/1404767171-6902-1-git-send-email-john.stultz@linaro.org Signed-off-by: Thomas Gleixner Signed-off-by: Greg Kroah-Hartman --- kernel/time/alarmtimer.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index a8f5084dcde7..294bf4ef1f47 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -540,9 +540,14 @@ static int alarm_timer_set(struct k_itimer *timr, int flags, struct itimerspec *new_setting, struct itimerspec *old_setting) { + ktime_t exp; + if (!rtcdev) return -ENOTSUPP; + if (flags & ~TIMER_ABSTIME) + return -EINVAL; + if (old_setting) alarm_timer_get(timr, old_setting); @@ -552,8 +557,16 @@ static int alarm_timer_set(struct k_itimer *timr, int flags, /* start the timer */ timr->it.alarm.interval = timespec_to_ktime(new_setting->it_interval); - alarm_start(&timr->it.alarm.alarmtimer, - timespec_to_ktime(new_setting->it_value)); + exp = timespec_to_ktime(new_setting->it_value); + /* Convert (if necessary) to absolute time */ + if (flags != TIMER_ABSTIME) { + ktime_t now; + + now = alarm_bases[timr->it.alarm.alarmtimer.type].gettime(); + exp = ktime_add(now, exp); + } + + alarm_start(&timr->it.alarm.alarmtimer, exp); return 0; } @@ -685,6 +698,9 @@ static int alarm_timer_nsleep(const clockid_t which_clock, int flags, if (!alarmtimer_get_rtcdev()) return -ENOTSUPP; + if (flags & ~TIMER_ABSTIME) + return -EINVAL; + if (!capable(CAP_WAKE_ALARM)) return -EPERM; From 4bbfb80c25ba29ba4fa1d0c7ed99f3c8b32a500f Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Mon, 14 Jul 2014 16:35:54 -0400 Subject: [PATCH 0080/1185] dm thin metadata: do not allow the data block size to change commit 9aec8629ec829fc9403788cd959e05dd87988bd1 upstream. The block size for the thin-pool's data device must remained fixed for the life of the thin-pool. Disallow any attempt to change the thin-pool's data block size. It should be noted that attempting to change the data block size via thin-pool table reload will be ignored as a side-effect of the thin-pool handover that the thin-pool target does during thin-pool table reload. Here is an example outcome of attempting to load a thin-pool table that reduced the thin-pool's data block size from 1024K to 512K. Before: kernel: device-mapper: thin: 253:4: growing the data device from 204800 to 409600 blocks After: kernel: device-mapper: thin metadata: changing the data block size (from 2048 to 1024) is not supported kernel: device-mapper: table: 253:4: thin-pool: Error creating metadata object kernel: device-mapper: ioctl: error adding target to table Signed-off-by: Mike Snitzer Acked-by: Joe Thornber Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-thin-metadata.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 5f49d704f275..3b1503dc1f13 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -591,6 +591,15 @@ static int __open_metadata(struct dm_pool_metadata *pmd) disk_super = dm_block_data(sblock); + /* Verify the data block size hasn't changed */ + if (le32_to_cpu(disk_super->data_block_size) != pmd->data_block_size) { + DMERR("changing the data block size (from %u to %llu) is not supported", + le32_to_cpu(disk_super->data_block_size), + (unsigned long long)pmd->data_block_size); + r = -EINVAL; + goto bad_unlock_sblock; + } + r = __check_incompat_features(disk_super, pmd); if (r < 0) goto bad_unlock_sblock; From 8a09a31a13d9efce4e253e4facd63d631809b89e Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Mon, 14 Jul 2014 16:59:39 -0400 Subject: [PATCH 0081/1185] dm cache metadata: do not allow the data block size to change commit 048e5a07f282c57815b3901d4a68a77fa131ce0a upstream. The block size for the dm-cache's data device must remained fixed for the life of the cache. Disallow any attempt to change the cache's data block size. Signed-off-by: Mike Snitzer Acked-by: Joe Thornber Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-cache-metadata.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c index a33e07f4222e..de737ba1d351 100644 --- a/drivers/md/dm-cache-metadata.c +++ b/drivers/md/dm-cache-metadata.c @@ -384,6 +384,15 @@ static int __open_metadata(struct dm_cache_metadata *cmd) disk_super = dm_block_data(sblock); + /* Verify the data block size hasn't changed */ + if (le32_to_cpu(disk_super->data_block_size) != cmd->data_block_size) { + DMERR("changing the data block size (from %u to %llu) is not supported", + le32_to_cpu(disk_super->data_block_size), + (unsigned long long)cmd->data_block_size); + r = -EINVAL; + goto bad; + } + r = __check_incompat_features(disk_super, cmd); if (r < 0) goto bad; From 804536e8e033d7917a1384b89d1e29a3457ec429 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jul 2014 08:51:27 +0200 Subject: [PATCH 0082/1185] PM / sleep: Fix request_firmware() error at resume commit 4320f6b1d9db4ca912c5eb6ecb328b2e090e1586 upstream. The commit [247bc037: PM / Sleep: Mitigate race between the freezer and request_firmware()] introduced the finer state control, but it also leads to a new bug; for example, a bug report regarding the firmware loading of intel BT device at suspend/resume: https://bugzilla.novell.com/show_bug.cgi?id=873790 The root cause seems to be a small window between the process resume and the clear of usermodehelper lock. The request_firmware() function checks the UMH lock and gives up when it's in UMH_DISABLE state. This is for avoiding the invalid f/w loading during suspend/resume phase. The problem is, however, that usermodehelper_enable() is called at the end of thaw_processes(). Thus, a thawed process in between can kick off the f/w loader code path (in this case, via btusb_setup_intel()) even before the call of usermodehelper_enable(). Then usermodehelper_read_trylock() returns an error and request_firmware() spews WARN_ON() in the end. This oneliner patch fixes the issue just by setting to UMH_FREEZING state again before restarting tasks, so that the call of request_firmware() will be blocked until the end of this function instead of returning an error. Fixes: 247bc0374254 (PM / Sleep: Mitigate race between the freezer and request_firmware()) Link: https://bugzilla.novell.com/show_bug.cgi?id=873790 Signed-off-by: Takashi Iwai Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- kernel/power/process.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/power/process.c b/kernel/power/process.c index 98088e0e71e8..1b212bee1510 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -174,6 +174,7 @@ void thaw_processes(void) printk("Restarting tasks ... "); + __usermodehelper_set_disable_depth(UMH_FREEZING); thaw_workqueues(); read_lock(&tasklist_lock); From e6be7d3115436b2527c60973c901ec3a7c6afe15 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 6 Jun 2014 19:53:16 +0200 Subject: [PATCH 0083/1185] locking/mutex: Disable optimistic spinning on some architectures commit 4badad352a6bb202ec68afa7a574c0bb961e5ebc upstream. The optimistic spin code assumes regular stores and cmpxchg() play nice; this is found to not be true for at least: parisc, sparc32, tile32, metag-lock1, arc-!llsc and hexagon. There is further wreckage, but this in particular seemed easy to trigger, so blacklist this. Opt in for known good archs. Signed-off-by: Peter Zijlstra Reported-by: Mikulas Patocka Cc: David Miller Cc: Chris Metcalf Cc: James Bottomley Cc: Vineet Gupta Cc: Jason Low Cc: Waiman Long Cc: "James E.J. Bottomley" Cc: Paul McKenney Cc: John David Anglin Cc: James Hogan Cc: Linus Torvalds Cc: Davidlohr Bueso Cc: Benjamin Herrenschmidt Cc: Catalin Marinas Cc: Russell King Cc: Will Deacon Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Cc: linuxppc-dev@lists.ozlabs.org Cc: sparclinux@vger.kernel.org Link: http://lkml.kernel.org/r/20140606175316.GV13930@laptop.programming.kicks-ass.net Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/arm/Kconfig | 1 + arch/arm64/Kconfig | 1 + arch/powerpc/Kconfig | 1 + arch/sparc/Kconfig | 1 + arch/x86/Kconfig | 1 + kernel/Kconfig.locks | 5 ++++- 6 files changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 18a9f5ef643a..d41951246cd6 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -4,6 +4,7 @@ config ARM select ARCH_BINFMT_ELF_RANDOMIZE_PIE select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select ARCH_HAVE_CUSTOM_GPIO_H + select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_WANT_IPC_PARSE_VERSION select BUILDTIME_EXTABLE_SORT if MMU diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 56b3f6d447ae..0677ff4814fa 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1,6 +1,7 @@ config ARM64 def_bool y select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE + select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_WANT_OPTIONAL_GPIOLIB select ARCH_WANT_COMPAT_IPC_PARSE_VERSION select ARCH_WANT_FRAME_POINTERS diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index fe404e77246e..7f656f119ea6 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -138,6 +138,7 @@ config PPC select ARCH_USE_BUILTIN_BSWAP select OLD_SIGSUSPEND select OLD_SIGACTION if PPC32 + select ARCH_SUPPORTS_ATOMIC_RMW config EARLY_PRINTK bool diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 2668b3142fa2..03a1bc3c3dde 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -77,6 +77,7 @@ config SPARC64 select ARCH_HAVE_NMI_SAFE_CMPXCHG select HAVE_C_RECORDMCOUNT select NO_BOOTMEM + select ARCH_SUPPORTS_ATOMIC_RMW config ARCH_DEFCONFIG string diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index fe120da25625..af88b27ce313 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -121,6 +121,7 @@ config X86 select OLD_SIGACTION if X86_32 select COMPAT_OLD_SIGACTION if IA32_EMULATION select RTC_LIB + select ARCH_SUPPORTS_ATOMIC_RMW config INSTRUCTION_DECODER def_bool y diff --git a/kernel/Kconfig.locks b/kernel/Kconfig.locks index 44511d100eaa..e4d30533c562 100644 --- a/kernel/Kconfig.locks +++ b/kernel/Kconfig.locks @@ -220,6 +220,9 @@ config INLINE_WRITE_UNLOCK_IRQRESTORE endif +config ARCH_SUPPORTS_ATOMIC_RMW + bool + config MUTEX_SPIN_ON_OWNER def_bool y - depends on SMP && !DEBUG_MUTEXES + depends on SMP && !DEBUG_MUTEXES && ARCH_SUPPORTS_ATOMIC_RMW From 4aba6e36347232a9e0cc2e9c8daf42a6bdcdad66 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Sat, 14 Jun 2014 15:00:09 +0200 Subject: [PATCH 0084/1185] sched: Fix possible divide by zero in avg_atom() calculation commit b0ab99e7736af88b8ac1b7ae50ea287fffa2badc upstream. proc_sched_show_task() does: if (nr_switches) do_div(avg_atom, nr_switches); nr_switches is unsigned long and do_div truncates it to 32 bits, which means it can test non-zero on e.g. x86-64 and be truncated to zero for division. Fix the problem by using div64_ul() instead. As a side effect calculations of avg_atom for big nr_switches are now correct. Signed-off-by: Mateusz Guzik Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Link: http://lkml.kernel.org/r/1402750809-31991-1-git-send-email-mguzik@redhat.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/sched/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index e745a1548367..701b6c8a4b12 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -551,7 +551,7 @@ void proc_sched_show_task(struct task_struct *p, struct seq_file *m) avg_atom = p->se.sum_exec_runtime; if (nr_switches) - do_div(avg_atom, nr_switches); + avg_atom = div64_ul(avg_atom, nr_switches); else avg_atom = -1LL; From a290f3552cc7b68398df8bbca5290bad0867827b Mon Sep 17 00:00:00 2001 From: Anton Kolesov Date: Fri, 20 Jun 2014 20:28:39 +0400 Subject: [PATCH 0085/1185] ARC: Implement ptrace(PTRACE_GET_THREAD_AREA) commit a4b6cb735b25aa84a462a1985e3e43bebaf5beb4 upstream. This patch adds implementation of GET_THREAD_AREA ptrace request type. This is required by GDB to debug NPTL applications. Signed-off-by: Anton Kolesov Signed-off-by: Vineet Gupta Signed-off-by: Greg Kroah-Hartman --- arch/arc/include/uapi/asm/ptrace.h | 1 + arch/arc/kernel/ptrace.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/arch/arc/include/uapi/asm/ptrace.h b/arch/arc/include/uapi/asm/ptrace.h index 30333cec0fef..ef9d79a3db25 100644 --- a/arch/arc/include/uapi/asm/ptrace.h +++ b/arch/arc/include/uapi/asm/ptrace.h @@ -11,6 +11,7 @@ #ifndef _UAPI__ASM_ARC_PTRACE_H #define _UAPI__ASM_ARC_PTRACE_H +#define PTRACE_GET_THREAD_AREA 25 #ifndef __ASSEMBLY__ /* diff --git a/arch/arc/kernel/ptrace.c b/arch/arc/kernel/ptrace.c index 0851604bb9cd..f8a36ed9e0d5 100644 --- a/arch/arc/kernel/ptrace.c +++ b/arch/arc/kernel/ptrace.c @@ -136,6 +136,10 @@ long arch_ptrace(struct task_struct *child, long request, pr_debug("REQ=%ld: ADDR =0x%lx, DATA=0x%lx)\n", request, addr, data); switch (request) { + case PTRACE_GET_THREAD_AREA: + ret = put_user(task_thread_info(child)->thr_ptr, + (unsigned long __user *)data); + break; default: ret = ptrace_request(child, request, addr, data); break; From 92488f4c9f687cc0e274be561f7b168743f59f20 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 28 Jul 2014 08:00:59 -0700 Subject: [PATCH 0086/1185] Linux 3.10.50 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b8b8d33eab55..8d891c66803c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 10 -SUBLEVEL = 49 +SUBLEVEL = 50 EXTRAVERSION = NAME = TOSSUG Baby Fish From 687f999e1fbd3b553bccbd7f52996ae56c5e327e Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 23 Jul 2014 16:55:07 -0700 Subject: [PATCH 0087/1185] ext4: Add support for FIDTRIM, a best-effort ioctl for deep discard trim * What This provides an interface for issuing an FITRIM which uses the secure discard instead of just a discard. Only the eMMC command is "secure", and not how the FS uses it: due to the fact that the FS might reassign a region somewhere else, the original deleted data will not be affected by the "trim" which only handles un-used regions. So we'll just call it "deep discard", and note that this is a "best effort" cleanup. * Why Once in a while, We want to be able to cleanup most of the unused blocks after erasing a bunch of files. We don't want to constantly secure-discard via a mount option. From an eMMC spec perspective, it tells the device to really get rid of all the data for the specified blocks and not just put them back into the pool of free ones (unlike the normal TRIM). The eMMC spec says the secure trim handling must make sure the data (and metadata) is not available anymore. A simple TRIM doesn't clear the data, it just puts blocks in the free pool. JEDEC Standard No. 84-A441 7.6.9 Secure Erase 7.6.10 Secure Trim From an FS perspective, it is acceptable to leave some data behind. - directory entries related to deleted files - databases entries related to deleted files - small-file data stored in inode extents - blocks held by the FS waiting to be re-used (mitigated by sync). - blocks reassigned by the FS prior to FIDTRIM. Change-Id: I676a1404a80130d93930c84898360f2e6fb2f81e Signed-off-by: Geremy Condra Signed-off-by: JP Abgrall --- fs/ext4/ext4.h | 3 ++- fs/ext4/ioctl.c | 6 +++++- fs/ext4/mballoc.c | 28 ++++++++++++++++++---------- include/uapi/linux/fs.h | 2 ++ 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 5aae3d12d400..09ace38b451a 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2050,7 +2050,8 @@ extern int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t i, struct ext4_group_desc *desc); extern int ext4_group_add_blocks(handle_t *handle, struct super_block *sb, ext4_fsblk_t block, unsigned long count); -extern int ext4_trim_fs(struct super_block *, struct fstrim_range *); +extern int ext4_trim_fs(struct super_block *, struct fstrim_range *, + unsigned long blkdev_flags); /* inode.c */ struct buffer_head *ext4_getblk(handle_t *, struct inode *, diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 9491ac0590f7..d01a05593be5 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -594,11 +594,13 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return err; } + case FIDTRIM: case FITRIM: { struct request_queue *q = bdev_get_queue(sb->s_bdev); struct fstrim_range range; int ret = 0; + int flags = cmd == FIDTRIM ? BLKDEV_DISCARD_SECURE : 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -606,13 +608,15 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (!blk_queue_discard(q)) return -EOPNOTSUPP; + if ((flags & BLKDEV_DISCARD_SECURE) && !blk_queue_secdiscard(q)) + return -EOPNOTSUPP; if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range))) return -EFAULT; range.minlen = max((unsigned int)range.minlen, q->limits.discard_granularity); - ret = ext4_trim_fs(sb, &range); + ret = ext4_trim_fs(sb, &range, flags); if (ret < 0) return ret; diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index def84082a9a9..50375b0f6022 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2705,7 +2705,8 @@ int ext4_mb_release(struct super_block *sb) } static inline int ext4_issue_discard(struct super_block *sb, - ext4_group_t block_group, ext4_grpblk_t cluster, int count) + ext4_group_t block_group, ext4_grpblk_t cluster, int count, + unsigned long flags) { ext4_fsblk_t discard_block; @@ -2714,7 +2715,7 @@ static inline int ext4_issue_discard(struct super_block *sb, count = EXT4_C2B(EXT4_SB(sb), count); trace_ext4_discard_blocks(sb, (unsigned long long) discard_block, count); - return sb_issue_discard(sb, discard_block, count, GFP_NOFS, 0); + return sb_issue_discard(sb, discard_block, count, GFP_NOFS, flags); } /* @@ -2736,7 +2737,7 @@ static void ext4_free_data_callback(struct super_block *sb, if (test_opt(sb, DISCARD)) { err = ext4_issue_discard(sb, entry->efd_group, entry->efd_start_cluster, - entry->efd_count); + entry->efd_count, 0); if (err && err != -EOPNOTSUPP) ext4_msg(sb, KERN_WARNING, "discard request in" " group:%d block:%d count:%d failed" @@ -4755,7 +4756,8 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, * them with group lock_held */ if (test_opt(sb, DISCARD)) { - err = ext4_issue_discard(sb, block_group, bit, count); + err = ext4_issue_discard(sb, block_group, bit, count, + 0); if (err && err != -EOPNOTSUPP) ext4_msg(sb, KERN_WARNING, "discard request in" " group:%d block:%d count:%lu failed" @@ -4950,13 +4952,15 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb, * @count: number of blocks to TRIM * @group: alloc. group we are working with * @e4b: ext4 buddy for the group + * @blkdev_flags: flags for the block device * * Trim "count" blocks starting at "start" in the "group". To assure that no * one will allocate those blocks, mark it as used in buddy bitmap. This must * be called with under the group lock. */ static int ext4_trim_extent(struct super_block *sb, int start, int count, - ext4_group_t group, struct ext4_buddy *e4b) + ext4_group_t group, struct ext4_buddy *e4b, + unsigned long blkdev_flags) { struct ext4_free_extent ex; int ret = 0; @@ -4975,7 +4979,7 @@ static int ext4_trim_extent(struct super_block *sb, int start, int count, */ mb_mark_used(e4b, &ex); ext4_unlock_group(sb, group); - ret = ext4_issue_discard(sb, group, start, count); + ret = ext4_issue_discard(sb, group, start, count, blkdev_flags); ext4_lock_group(sb, group); mb_free_blocks(NULL, e4b, start, ex.fe_len); return ret; @@ -4988,6 +4992,7 @@ static int ext4_trim_extent(struct super_block *sb, int start, int count, * @start: first group block to examine * @max: last group block to examine * @minblocks: minimum extent block count + * @blkdev_flags: flags for the block device * * ext4_trim_all_free walks through group's buddy bitmap searching for free * extents. When the free block is found, ext4_trim_extent is called to TRIM @@ -5002,7 +5007,7 @@ static int ext4_trim_extent(struct super_block *sb, int start, int count, static ext4_grpblk_t ext4_trim_all_free(struct super_block *sb, ext4_group_t group, ext4_grpblk_t start, ext4_grpblk_t max, - ext4_grpblk_t minblocks) + ext4_grpblk_t minblocks, unsigned long blkdev_flags) { void *bitmap; ext4_grpblk_t next, count = 0, free_count = 0; @@ -5035,7 +5040,8 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group, if ((next - start) >= minblocks) { ret = ext4_trim_extent(sb, start, - next - start, group, &e4b); + next - start, group, &e4b, + blkdev_flags); if (ret && ret != -EOPNOTSUPP) break; ret = 0; @@ -5077,6 +5083,7 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group, * ext4_trim_fs() -- trim ioctl handle function * @sb: superblock for filesystem * @range: fstrim_range structure + * @blkdev_flags: flags for the block device * * start: First Byte to trim * len: number of Bytes to trim from start @@ -5085,7 +5092,8 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group, * start to start+len. For each such a group ext4_trim_all_free function * is invoked to trim all free space. */ -int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) +int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range, + unsigned long blkdev_flags) { struct ext4_group_info *grp; ext4_group_t group, first_group, last_group; @@ -5141,7 +5149,7 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) if (grp->bb_free >= minlen) { cnt = ext4_trim_all_free(sb, group, first_cluster, - end, minlen); + end, minlen, blkdev_flags); if (cnt < 0) { ret = cnt; break; diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index a4ed56cf0eac..5014a5c472ed 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -154,6 +154,8 @@ struct inodes_stat_t { #define FITHAW _IOWR('X', 120, int) /* Thaw */ #define FITRIM _IOWR('X', 121, struct fstrim_range) /* Trim */ +#define FIDTRIM _IOWR('f', 128, struct fstrim_range) /* Deep discard trim */ + #define FS_IOC_GETFLAGS _IOR('f', 1, long) #define FS_IOC_SETFLAGS _IOW('f', 2, long) #define FS_IOC_GETVERSION _IOR('v', 1, long) From 926a693a348963a08b5710552e4e7209d2dc126c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 16 Jun 2014 09:08:29 -0300 Subject: [PATCH 0088/1185] media: hdpvr: fix two audio bugs commit 3445857b22eafb70a6ac258979e955b116bfd2c6 upstream. When the audio encoding is changed the driver calls hdpvr_set_audio with the current opt->audio_input value. However, that should have been opt->audio_input + 1. So changing the audio encoding inadvertently changes the input as well. This bug has always been there. The second bug was introduced in kernel 3.10 and that broke the default_audio_input module option handling: the audio encoding was never switched to AC3 if default_audio_input was set to 2 (SPDIF input). In addition, since starting with 3.10 the audio encoding is always set at the start the first bug now always happens when the driver is loaded. In the past this bug would only surface if the user would change the audio encoding after the driver was loaded. Also fixes a small trivial typo (bufffer -> buffer). Signed-off-by: Hans Verkuil Reported-by: Scott Doty Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/hdpvr/hdpvr-video.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 774ba0e820be..eed70a4d24e6 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -81,7 +81,7 @@ static void hdpvr_read_bulk_callback(struct urb *urb) } /*=========================================================================*/ -/* bufffer bits */ +/* buffer bits */ /* function expects dev->io_mutex to be hold by caller */ int hdpvr_cancel_queue(struct hdpvr_device *dev) @@ -921,7 +921,7 @@ static int hdpvr_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_AUDIO_ENCODING: if (dev->flags & HDPVR_FLAG_AC3_CAP) { opt->audio_codec = ctrl->val; - return hdpvr_set_audio(dev, opt->audio_input, + return hdpvr_set_audio(dev, opt->audio_input + 1, opt->audio_codec); } return 0; @@ -1191,7 +1191,7 @@ int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops, V4L2_CID_MPEG_AUDIO_ENCODING, ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 : V4L2_MPEG_AUDIO_ENCODING_AAC, - 0x7, V4L2_MPEG_AUDIO_ENCODING_AAC); + 0x7, ac3 ? dev->options.audio_codec : V4L2_MPEG_AUDIO_ENCODING_AAC); v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops, V4L2_CID_MPEG_VIDEO_ENCODING, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 0x3, From 18bfdaeaa42e36d49bf82443e890db9448d4bebf Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 4 Jul 2014 05:44:39 -0300 Subject: [PATCH 0089/1185] media: tda10071: force modulation to QPSK on DVB-S commit db4175ae2095634dbecd4c847da439f9c83e1b3b upstream. Only supported modulation for DVB-S is QPSK. Modulation parameter contains invalid value for DVB-S on some cases, which leads driver refusing tuning attempt. Due to that, hard code modulation to QPSK in case of DVB-S. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/dvb-frontends/tda10071.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 36eb27d3fdf1..def7812d7b22 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -667,6 +667,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; u8 mode, rolloff, pilot, inversion, div; + fe_modulation_t modulation; dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d modulation=%d " \ "frequency=%d symbol_rate=%d inversion=%d pilot=%d " \ @@ -701,10 +702,13 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) switch (c->delivery_system) { case SYS_DVBS: + modulation = QPSK; rolloff = 0; pilot = 2; break; case SYS_DVBS2: + modulation = c->modulation; + switch (c->rolloff) { case ROLLOFF_20: rolloff = 2; @@ -749,7 +753,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) for (i = 0, mode = 0xff; i < ARRAY_SIZE(TDA10071_MODCOD); i++) { if (c->delivery_system == TDA10071_MODCOD[i].delivery_system && - c->modulation == TDA10071_MODCOD[i].modulation && + modulation == TDA10071_MODCOD[i].modulation && c->fec_inner == TDA10071_MODCOD[i].fec) { mode = TDA10071_MODCOD[i].val; dev_dbg(&priv->i2c->dev, "%s: mode found=%02x\n", From 668b7a05f2fa59c60ca9820269e3d8dfaa218693 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Wed, 2 Jul 2014 12:46:23 -0400 Subject: [PATCH 0090/1185] block: provide compat ioctl for BLKZEROOUT commit 3b3a1814d1703027f9867d0f5cbbfaf6c7482474 upstream. This patch provides the compat BLKZEROOUT ioctl. The argument is a pointer to two uint64_t values, so there is no need to translate it. Signed-off-by: Mikulas Patocka Acked-by: Martin K. Petersen Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/compat_ioctl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c index 7c668c8a6f95..21ad6869a5ce 100644 --- a/block/compat_ioctl.c +++ b/block/compat_ioctl.c @@ -689,6 +689,7 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) case BLKROSET: case BLKDISCARD: case BLKSECDISCARD: + case BLKZEROOUT: /* * the ones below are implemented in blkdev_locked_ioctl, * but we call blkdev_ioctl, which gets the lock for us From cb454b6d31756674d2e0ceaa336ec87019728d9b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Jul 2014 12:25:28 +0200 Subject: [PATCH 0091/1185] block: don't assume last put of shared tags is for the host commit d45b3279a5a2252cafcd665bbf2db8c9b31ef783 upstream. There is no inherent reason why the last put of a tag structure must be the one for the Scsi_Host, as device model objects can be held for arbitrary periods. Merge blk_free_tags and __blk_free_tags into a single funtion that just release a references and get rid of the BUG() when the host reference wasn't the last. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk-tag.c | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/block/blk-tag.c b/block/blk-tag.c index cc345e1d8d4e..0c51b4b34f47 100644 --- a/block/blk-tag.c +++ b/block/blk-tag.c @@ -27,18 +27,15 @@ struct request *blk_queue_find_tag(struct request_queue *q, int tag) EXPORT_SYMBOL(blk_queue_find_tag); /** - * __blk_free_tags - release a given set of tag maintenance info + * blk_free_tags - release a given set of tag maintenance info * @bqt: the tag map to free * - * Tries to free the specified @bqt. Returns true if it was - * actually freed and false if there are still references using it + * Drop the reference count on @bqt and frees it when the last reference + * is dropped. */ -static int __blk_free_tags(struct blk_queue_tag *bqt) +void blk_free_tags(struct blk_queue_tag *bqt) { - int retval; - - retval = atomic_dec_and_test(&bqt->refcnt); - if (retval) { + if (atomic_dec_and_test(&bqt->refcnt)) { BUG_ON(find_first_bit(bqt->tag_map, bqt->max_depth) < bqt->max_depth); @@ -50,9 +47,8 @@ static int __blk_free_tags(struct blk_queue_tag *bqt) kfree(bqt); } - - return retval; } +EXPORT_SYMBOL(blk_free_tags); /** * __blk_queue_free_tags - release tag maintenance info @@ -69,27 +65,12 @@ void __blk_queue_free_tags(struct request_queue *q) if (!bqt) return; - __blk_free_tags(bqt); + blk_free_tags(bqt); q->queue_tags = NULL; queue_flag_clear_unlocked(QUEUE_FLAG_QUEUED, q); } -/** - * blk_free_tags - release a given set of tag maintenance info - * @bqt: the tag map to free - * - * For externally managed @bqt frees the map. Callers of this - * function must guarantee to have released all the queues that - * might have been using this tag map. - */ -void blk_free_tags(struct blk_queue_tag *bqt) -{ - if (unlikely(!__blk_free_tags(bqt))) - BUG(); -} -EXPORT_SYMBOL(blk_free_tags); - /** * blk_queue_free_tags - release tag maintenance info * @q: the request queue for the device From 97a230703c9f6cbed3bdd4d4a627863c14a8a2de Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Sat, 12 Jul 2014 12:08:24 +0800 Subject: [PATCH 0092/1185] libata: support the ata host which implements a queue depth less than 32 commit 1871ee134b73fb4cadab75752a7152ed2813c751 upstream. The sata on fsl mpc8315e is broken after the commit 8a4aeec8d2d6 ("libata/ahci: accommodate tag ordered controllers"). The reason is that the ata controller on this SoC only implement a queue depth of 16. When issuing the commands in tag order, all the commands in tag 16 ~ 31 are mapped to tag 0 unconditionally and then causes the sata malfunction. It makes no senses to use a 32 queue in software while the hardware has less queue depth. So consider the queue depth implemented by the hardware when requesting a command tag. Fixes: 8a4aeec8d2d6 ("libata/ahci: accommodate tag ordered controllers") Signed-off-by: Kevin Hao Acked-by: Dan Williams Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-core.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index bf00fbcde8ad..d7fb8269cb73 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4758,6 +4758,10 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words) * ata_qc_new - Request an available ATA command, for queueing * @ap: target port * + * Some ATA host controllers may implement a queue depth which is less + * than ATA_MAX_QUEUE. So we shouldn't allocate a tag which is beyond + * the hardware limitation. + * * LOCKING: * None. */ @@ -4765,14 +4769,16 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words) static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap) { struct ata_queued_cmd *qc = NULL; - unsigned int i, tag; + unsigned int i, tag, max_queue; + + max_queue = ap->scsi_host->can_queue; /* no command while frozen */ if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) return NULL; - for (i = 0; i < ATA_MAX_QUEUE; i++) { - tag = (i + ap->last_tag + 1) % ATA_MAX_QUEUE; + for (i = 0, tag = ap->last_tag + 1; i < max_queue; i++, tag++) { + tag = tag < max_queue ? tag : 0; /* the last tag is reserved for internal command. */ if (tag == ATA_TAG_INTERNAL) @@ -6154,6 +6160,16 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) { int i, rc; + /* + * The max queue supported by hardware must not be greater than + * ATA_MAX_QUEUE. + */ + if (sht->can_queue > ATA_MAX_QUEUE) { + dev_err(host->dev, "BUG: the hardware max queue is too large\n"); + WARN_ON(1); + return -EINVAL; + } + /* host must have been started */ if (!(host->flags & ATA_HOST_STARTED)) { dev_err(host->dev, "BUG: trying to register unstarted host\n"); From 03cccb9c9ec674407285d3a000b9380c946e21c1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 23 Jul 2014 09:05:27 -0400 Subject: [PATCH 0093/1185] libata: introduce ata_host->n_tags to avoid oops on SAS controllers commit 1a112d10f03e83fb3a2fdc4c9165865dec8a3ca6 upstream. 1871ee134b73 ("libata: support the ata host which implements a queue depth less than 32") directly used ata_port->scsi_host->can_queue from ata_qc_new() to determine the number of tags supported by the host; unfortunately, SAS controllers doing SATA don't initialize ->scsi_host leading to the following oops. BUG: unable to handle kernel NULL pointer dereference at 0000000000000058 IP: [] ata_qc_new_init+0x188/0x1b0 PGD 0 Oops: 0002 [#1] SMP Modules linked in: isci libsas scsi_transport_sas mgag200 drm_kms_helper ttm CPU: 1 PID: 518 Comm: udevd Not tainted 3.16.0-rc6+ #62 Hardware name: Intel Corporation S2600CO/S2600CO, BIOS SE5C600.86B.02.02.0002.122320131210 12/23/2013 task: ffff880c1a00b280 ti: ffff88061a000000 task.ti: ffff88061a000000 RIP: 0010:[] [] ata_qc_new_init+0x188/0x1b0 RSP: 0018:ffff88061a003ae8 EFLAGS: 00010012 RAX: 0000000000000001 RBX: ffff88000241ca80 RCX: 00000000000000fa RDX: 0000000000000020 RSI: 0000000000000020 RDI: ffff8806194aa298 RBP: ffff88061a003ae8 R08: ffff8806194a8000 R09: 0000000000000000 R10: 0000000000000000 R11: ffff88000241ca80 R12: ffff88061ad58200 R13: ffff8806194aa298 R14: ffffffff814e67a0 R15: ffff8806194a8000 FS: 00007f3ad7fe3840(0000) GS:ffff880627620000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000058 CR3: 000000061a118000 CR4: 00000000001407e0 Stack: ffff88061a003b20 ffffffff814e96e1 ffff88000241ca80 ffff88061ad58200 ffff8800b6bf6000 ffff880c1c988000 ffff880619903850 ffff88061a003b68 ffffffffa0056ce1 ffff88061a003b48 0000000013d6e6f8 ffff88000241ca80 Call Trace: [] ata_sas_queuecmd+0xa1/0x430 [] sas_queuecommand+0x191/0x220 [libsas] [] scsi_dispatch_cmd+0x10e/0x300 [] scsi_request_fn+0x2f5/0x550 [] __blk_run_queue+0x33/0x40 [] queue_unplugged+0x2a/0x90 [] blk_flush_plug_list+0x1b4/0x210 [] blk_finish_plug+0x14/0x50 [] __do_page_cache_readahead+0x198/0x1f0 [] force_page_cache_readahead+0x31/0x50 [] page_cache_sync_readahead+0x3e/0x50 [] generic_file_read_iter+0x496/0x5a0 [] blkdev_read_iter+0x37/0x40 [] new_sync_read+0x7e/0xb0 [] vfs_read+0x94/0x170 [] SyS_read+0x46/0xb0 [] ? SyS_lseek+0x91/0xb0 [] system_call_fastpath+0x16/0x1b Code: 00 00 00 88 50 29 83 7f 08 01 19 d2 83 e2 f0 83 ea 50 88 50 34 c6 81 1d 02 00 00 40 c6 81 17 02 00 00 00 5d c3 66 0f 1f 44 00 00 <89> 14 25 58 00 00 00 Fix it by introducing ata_host->n_tags which is initialized to ATA_MAX_QUEUE - 1 in ata_host_init() for SAS controllers and set to scsi_host_template->can_queue in ata_host_register() for !SAS ones. As SAS hosts are never registered, this will give them the same ATA_MAX_QUEUE - 1 as before. Note that we can't use scsi_host->can_queue directly for SAS hosts anyway as they can go higher than the libata maximum. Signed-off-by: Tejun Heo Reported-by: Mike Qiu Reported-by: Jesse Brandeburg Reported-by: Peter Hurley Reported-by: Peter Zijlstra Tested-by: Alexey Kardashevskiy Fixes: 1871ee134b73 ("libata: support the ata host which implements a queue depth less than 32") Cc: Kevin Hao Cc: Dan Williams Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-core.c | 16 ++++------------ include/linux/libata.h | 1 + 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index d7fb8269cb73..ca7c23d58a03 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4769,9 +4769,8 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words) static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap) { struct ata_queued_cmd *qc = NULL; - unsigned int i, tag, max_queue; - - max_queue = ap->scsi_host->can_queue; + unsigned int max_queue = ap->host->n_tags; + unsigned int i, tag; /* no command while frozen */ if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) @@ -6079,6 +6078,7 @@ void ata_host_init(struct ata_host *host, struct device *dev, { spin_lock_init(&host->lock); mutex_init(&host->eh_mutex); + host->n_tags = ATA_MAX_QUEUE - 1; host->dev = dev; host->ops = ops; } @@ -6160,15 +6160,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) { int i, rc; - /* - * The max queue supported by hardware must not be greater than - * ATA_MAX_QUEUE. - */ - if (sht->can_queue > ATA_MAX_QUEUE) { - dev_err(host->dev, "BUG: the hardware max queue is too large\n"); - WARN_ON(1); - return -EINVAL; - } + host->n_tags = clamp(sht->can_queue, 1, ATA_MAX_QUEUE - 1); /* host must have been started */ if (!(host->flags & ATA_HOST_STARTED)) { diff --git a/include/linux/libata.h b/include/linux/libata.h index eec130af2dfa..cc82cfb66259 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -547,6 +547,7 @@ struct ata_host { struct device *dev; void __iomem * const *iomap; unsigned int n_ports; + unsigned int n_tags; /* nr of NCQ tags */ void *private_data; struct ata_port_operations *ops; unsigned long flags; From d35acb6ef571d995a03dd03b03f69f051bf018e0 Mon Sep 17 00:00:00 2001 From: Romain Degez Date: Fri, 11 Jul 2014 18:08:13 +0200 Subject: [PATCH 0094/1185] ahci: add support for the Promise FastTrak TX8660 SATA HBA (ahci mode) commit b32bfc06aefab61acc872dec3222624e6cd867ed upstream. Add support of the Promise FastTrak TX8660 SATA HBA in ahci mode by registering the board in the ahci_pci_tbl[]. Note: this HBA also provide a hardware RAID mode when activated in BIOS but specific drivers from the manufacturer are required in this case. Signed-off-by: Romain Degez Tested-by: Romain Degez Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- drivers/ata/ahci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index b0d33d9533aa..3b39687c6336 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -455,6 +455,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { /* Promise */ { PCI_VDEVICE(PROMISE, 0x3f20), board_ahci }, /* PDC42819 */ + { PCI_VDEVICE(PROMISE, 0x3781), board_ahci }, /* FastTrak TX8660 ahci-mode */ /* Asmedia */ { PCI_VDEVICE(ASMEDIA, 0x0601), board_ahci }, /* ASM1060 */ From cebdb6fa24dfd48af3bba1af6ba485b45430fb1c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 5 Jul 2014 18:43:21 -0400 Subject: [PATCH 0095/1185] blkcg: don't call into policy draining if root_blkg is already gone commit 0b462c89e31f7eb6789713437eb551833ee16ff3 upstream. While a queue is being destroyed, all the blkgs are destroyed and its ->root_blkg pointer is set to NULL. If someone else starts to drain while the queue is in this state, the following oops happens. NULL pointer dereference at 0000000000000028 IP: [] blk_throtl_drain+0x84/0x230 PGD e4a1067 PUD b773067 PMD 0 Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC Modules linked in: cfq_iosched(-) [last unloaded: cfq_iosched] CPU: 1 PID: 537 Comm: bash Not tainted 3.16.0-rc3-work+ #2 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 task: ffff88000e222250 ti: ffff88000efd4000 task.ti: ffff88000efd4000 RIP: 0010:[] [] blk_throtl_drain+0x84/0x230 RSP: 0018:ffff88000efd7bf0 EFLAGS: 00010046 RAX: 0000000000000000 RBX: ffff880015091450 RCX: 0000000000000001 RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 RBP: ffff88000efd7c10 R08: 0000000000000000 R09: 0000000000000001 R10: ffff88000e222250 R11: 0000000000000000 R12: ffff880015091450 R13: ffff880015092e00 R14: ffff880015091d70 R15: ffff88001508fc28 FS: 00007f1332650740(0000) GS:ffff88001fa80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000028 CR3: 0000000009446000 CR4: 00000000000006e0 Stack: ffffffff8144e8f6 ffff880015091450 0000000000000000 ffff880015091d80 ffff88000efd7c28 ffffffff8144ae2f ffff880015091450 ffff88000efd7c58 ffffffff81427641 ffff880015091450 ffffffff82401f00 ffff880015091450 Call Trace: [] blkcg_drain_queue+0x1f/0x60 [] __blk_drain_queue+0x71/0x180 [] blk_queue_bypass_start+0x6e/0xb0 [] blkcg_deactivate_policy+0x38/0x120 [] blk_throtl_exit+0x34/0x50 [] blkcg_exit_queue+0x35/0x40 [] blk_release_queue+0x26/0xd0 [] kobject_cleanup+0x38/0x70 [] kobject_put+0x28/0x60 [] blk_put_queue+0x15/0x20 [] scsi_device_dev_release_usercontext+0x16b/0x1c0 [] execute_in_process_context+0x89/0xa0 [] scsi_device_dev_release+0x1c/0x20 [] device_release+0x32/0xa0 [] kobject_cleanup+0x38/0x70 [] kobject_put+0x28/0x60 [] put_device+0x17/0x20 [] __scsi_remove_device+0xa9/0xe0 [] scsi_remove_device+0x2b/0x40 [] sdev_store_delete+0x27/0x30 [] dev_attr_store+0x18/0x30 [] sysfs_kf_write+0x3e/0x50 [] kernfs_fop_write+0xe7/0x170 [] vfs_write+0xaf/0x1d0 [] SyS_write+0x4d/0xc0 [] system_call_fastpath+0x16/0x1b 776687bce42b ("block, blk-mq: draining can't be skipped even if bypass_depth was non-zero") made it easier to trigger this bug by making blk_queue_bypass_start() drain even when it loses the first bypass test to blk_cleanup_queue(); however, the bug has always been there even before the commit as blk_queue_bypass_start() could race against queue destruction, win the initial bypass test but perform the actual draining after blk_cleanup_queue() already destroyed all blkgs. Fix it by skippping calling into policy draining if all the blkgs are already gone. Signed-off-by: Tejun Heo Reported-by: Shirish Pargaonkar Reported-by: Sasha Levin Reported-by: Jet Chen Tested-by: Shirish Pargaonkar Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk-cgroup.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index e8918ffaf96d..b95219d2168d 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -876,6 +876,13 @@ void blkcg_drain_queue(struct request_queue *q) { lockdep_assert_held(q->queue_lock); + /* + * @q could be exiting and already have destroyed all blkgs as + * indicated by NULL root_blkg. If so, don't confuse policies. + */ + if (!q->root_blkg) + return; + blk_throtl_drain(q); } From efd39f7786aa4e7a847d5f8c9f5506e3b9ad6b38 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Fri, 18 Jul 2014 11:43:01 -0700 Subject: [PATCH 0096/1185] tracing: Fix wraparound problems in "uptime" trace clock commit 58d4e21e50ff3cc57910a8abc20d7e14375d2f61 upstream. The "uptime" trace clock added in: commit 8aacf017b065a805d27467843490c976835eb4a5 tracing: Add "uptime" trace clock that uses jiffies has wraparound problems when the system has been up more than 1 hour 11 minutes and 34 seconds. It converts jiffies to nanoseconds using: (u64)jiffies_to_usecs(jiffy) * 1000ULL but since jiffies_to_usecs() only returns a 32-bit value, it truncates at 2^32 microseconds. An additional problem on 32-bit systems is that the argument is "unsigned long", so fixing the return value only helps until 2^32 jiffies (49.7 days on a HZ=1000 system). Avoid these problems by using jiffies_64 as our basis, and not converting to nanoseconds (we do convert to clock_t because user facing API must not be dependent on internal kernel HZ values). Link: http://lkml.kernel.org/p/99d63c5bfe9b320a3b428d773825a37095bf6a51.1405708254.git.tony.luck@intel.com Fixes: 8aacf017b065 "tracing: Add "uptime" trace clock that uses jiffies" Signed-off-by: Tony Luck Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace.c | 2 +- kernel/trace/trace_clock.c | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 98a830d079b9..18cdf91b2f85 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -741,7 +741,7 @@ static struct { { trace_clock_local, "local", 1 }, { trace_clock_global, "global", 1 }, { trace_clock_counter, "counter", 0 }, - { trace_clock_jiffies, "uptime", 1 }, + { trace_clock_jiffies, "uptime", 0 }, { trace_clock, "perf", 1 }, ARCH_TRACE_CLOCKS }; diff --git a/kernel/trace/trace_clock.c b/kernel/trace/trace_clock.c index 26dc348332b7..57b67b1f24d1 100644 --- a/kernel/trace/trace_clock.c +++ b/kernel/trace/trace_clock.c @@ -59,13 +59,14 @@ u64 notrace trace_clock(void) /* * trace_jiffy_clock(): Simply use jiffies as a clock counter. + * Note that this use of jiffies_64 is not completely safe on + * 32-bit systems. But the window is tiny, and the effect if + * we are affected is that we will have an obviously bogus + * timestamp on a trace event - i.e. not life threatening. */ u64 notrace trace_clock_jiffies(void) { - u64 jiffy = jiffies - INITIAL_JIFFIES; - - /* Return nsecs */ - return (u64)jiffies_to_usecs(jiffy) * 1000ULL; + return jiffies_64_to_clock_t(jiffies_64 - INITIAL_JIFFIES); } /* From a654d23f04adca40c74c4e820a6bf67fe9a187a1 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Sat, 21 Sep 2013 21:56:34 +0000 Subject: [PATCH 0097/1185] slab_common: Do not check for duplicate slab names commit 3e374919b314f20e2a04f641ebc1093d758f66a4 upstream. SLUB can alias multiple slab kmem_create_requests to one slab cache to save memory and increase the cache hotness. As a result the name of the slab can be stale. Only check the name for duplicates if we are in debug mode where we do not merge multiple caches. This fixes the following problem reported by Jonathan Brassow: The problem with kmem_cache* is this: *) Assume CONFIG_SLUB is set 1) kmem_cache_create(name="foo-a") - creates new kmem_cache structure 2) kmem_cache_create(name="foo-b") - If identical cache characteristics, it will be merged with the previously created cache associated with "foo-a". The cache's refcount will be incremented and an alias will be created via sysfs_slab_alias(). 3) kmem_cache_destroy() - Attempting to destroy cache associated with "foo-a", but instead the refcount is simply decremented. I don't even think the sysfs aliases are ever removed... 4) kmem_cache_create(name="foo-a") - This FAILS because kmem_cache_sanity_check colides with the existing name ("foo-a") associated with the non-removed cache. This is a problem for RAID (specifically dm-raid) because the name used for the kmem_cache_create is ("raid%d-%p", level, mddev). If the cache persists for long enough, the memory address of an old mddev will be reused for a new mddev - causing an identical formulation of the cache name. Even though kmem_cache_destory had long ago been used to delete the old cache, the merging of caches has cause the name and cache of that old instance to be preserved and causes a colision (and thus failure) in kmem_cache_create(). I see this regularly in my testing. Reported-by: Jonathan Brassow Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- mm/slab_common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/slab_common.c b/mm/slab_common.c index 2d414508e9ec..8b05120dfc09 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -55,6 +55,7 @@ static int kmem_cache_sanity_check(struct mem_cgroup *memcg, const char *name, continue; } +#if !defined(CONFIG_SLUB) || !defined(CONFIG_SLUB_DEBUG_ON) /* * For simplicity, we won't check this in the list of memcg * caches. We have control over memcg naming, and if there @@ -68,6 +69,7 @@ static int kmem_cache_sanity_check(struct mem_cgroup *memcg, const char *name, s = NULL; return -EINVAL; } +#endif } WARN_ON(strchr(name, ' ')); /* It confuses parsers */ From 6264198b34d26aa752f89fe9c5fcfdf4290c7fb5 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Tue, 4 Mar 2014 17:13:47 -0500 Subject: [PATCH 0098/1185] slab_common: fix the check for duplicate slab names commit 694617474e33b8603fc76e090ed7d09376514b1a upstream. The patch 3e374919b314f20e2a04f641ebc1093d758f66a4 is supposed to fix the problem where kmem_cache_create incorrectly reports duplicate cache name and fails. The problem is described in the header of that patch. However, the patch doesn't really fix the problem because of these reasons: * the logic to test for debugging is reversed. It was intended to perform the check only if slub debugging is enabled (which implies that caches with the same parameters are not merged). Therefore, there should be #if !defined(CONFIG_SLUB) || defined(CONFIG_SLUB_DEBUG_ON) The current code has the condition reversed and performs the test if debugging is disabled. * slub debugging may be enabled or disabled based on kernel command line, CONFIG_SLUB_DEBUG_ON is just the default settings. Therefore the test based on definition of CONFIG_SLUB_DEBUG_ON is unreliable. This patch fixes the problem by removing the test "!defined(CONFIG_SLUB_DEBUG_ON)". Therefore, duplicate names are never checked if the SLUB allocator is used. Note to stable kernel maintainers: when backporint this patch, please backport also the patch 3e374919b314f20e2a04f641ebc1093d758f66a4. Acked-by: David Rientjes Acked-by: Christoph Lameter Signed-off-by: Mikulas Patocka Signed-off-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- mm/slab_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/slab_common.c b/mm/slab_common.c index 8b05120dfc09..7d21d3fddbf0 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -55,7 +55,7 @@ static int kmem_cache_sanity_check(struct mem_cgroup *memcg, const char *name, continue; } -#if !defined(CONFIG_SLUB) || !defined(CONFIG_SLUB_DEBUG_ON) +#if !defined(CONFIG_SLUB) /* * For simplicity, we won't check this in the list of memcg * caches. We have control over memcg naming, and if there From 6d53522cde3626ce08e280dc979bb0bb08d6d08b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 19 Jul 2014 16:30:31 -0700 Subject: [PATCH 0099/1185] Input: fix defuzzing logic commit 50c5d36dab930b1f1b1e3348b8608aa8b9ee7610 upstream. We attempt to remove noise from coordinates reported by devices in input_handle_abs_event(), unfortunately, unless we were dropping the event altogether, we were ignoring the adjusted value and were passing on the original value instead. Reviewed-by: Andrew de los Reyes Reviewed-by: Benson Leung Reviewed-by: David Herrmann Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/input.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/input/input.c b/drivers/input/input.c index 66984e272c45..a161021c4526 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -257,9 +257,10 @@ static int input_handle_abs_event(struct input_dev *dev, } static int input_get_disposition(struct input_dev *dev, - unsigned int type, unsigned int code, int value) + unsigned int type, unsigned int code, int *pval) { int disposition = INPUT_IGNORE_EVENT; + int value = *pval; switch (type) { @@ -357,6 +358,7 @@ static int input_get_disposition(struct input_dev *dev, break; } + *pval = value; return disposition; } @@ -365,7 +367,7 @@ static void input_handle_event(struct input_dev *dev, { int disposition; - disposition = input_get_disposition(dev, type, code, value); + disposition = input_get_disposition(dev, type, code, &value); if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) dev->event(dev, type, code, value); From d1cc001905146d58c17ac8452eb96f226767819d Mon Sep 17 00:00:00 2001 From: Silesh C V Date: Wed, 23 Jul 2014 13:59:59 -0700 Subject: [PATCH 0100/1185] coredump: fix the setting of PF_DUMPCORE commit aed8adb7688d5744cb484226820163af31d2499a upstream. Commit 079148b919d0 ("coredump: factor out the setting of PF_DUMPCORE") cleaned up the setting of PF_DUMPCORE by removing it from all the linux_binfmt->core_dump() and moving it to zap_threads().But this ended up clearing all the previously set flags. This causes issues during core generation when tsk->flags is checked again (eg. for PF_USED_MATH to dump floating point registers). Fix this. Signed-off-by: Silesh C V Acked-by: Oleg Nesterov Cc: Mandeep Singh Baines Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/coredump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/coredump.c b/fs/coredump.c index dafafbafa731..1d402ce5b72f 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -299,7 +299,7 @@ static int zap_threads(struct task_struct *tsk, struct mm_struct *mm, if (unlikely(nr < 0)) return nr; - tsk->flags = PF_DUMPCORE; + tsk->flags |= PF_DUMPCORE; if (atomic_read(&mm->mm_users) == nr + 1) goto done; /* From c423ba6f8e807b1d18baa729a58e52f625406d1e Mon Sep 17 00:00:00 2001 From: John David Anglin Date: Wed, 23 Jul 2014 19:44:12 -0400 Subject: [PATCH 0101/1185] parisc: Remove SA_RESTORER define commit 20dbea494543aefaace874cc3ec93a39b94b1ec4 upstream. The sa_restorer field in struct sigaction is obsolete and no longer in the parisc implementation. However, the core code assumes the field is present if SA_RESTORER is defined. So, the define needs to be removed. Signed-off-by: John David Anglin Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- arch/parisc/include/uapi/asm/signal.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/parisc/include/uapi/asm/signal.h b/arch/parisc/include/uapi/asm/signal.h index a2fa297196bc..f5645d6a89f2 100644 --- a/arch/parisc/include/uapi/asm/signal.h +++ b/arch/parisc/include/uapi/asm/signal.h @@ -69,8 +69,6 @@ #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_RESTORER 0x04000000 /* obsolete -- ignored */ - #define MINSIGSTKSZ 2048 #define SIGSTKSZ 8192 From 69d15f41f7ee1ae8694511a1f134174ec1ffa337 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 18 Jul 2014 07:31:18 -0700 Subject: [PATCH 0102/1185] hwmon: (smsc47m192) Fix temperature limit and vrm write operations commit 043572d5444116b9d9ad8ae763cf069e7accbc30 upstream. Temperature limit clamps are applied after converting the temperature from milli-degrees C to degrees C, so either the clamp limit needs to be specified in degrees C, not milli-degrees C, or clamping must happen before converting to degrees C. Use the latter method to avoid overflows. vrm is an u8, so the written value needs to be limited to [0, 255]. Cc: Axel Lin Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/smsc47m192.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c index efee4c59239f..34b9a601ad07 100644 --- a/drivers/hwmon/smsc47m192.c +++ b/drivers/hwmon/smsc47m192.c @@ -86,7 +86,7 @@ static inline u8 IN_TO_REG(unsigned long val, int n) */ static inline s8 TEMP_TO_REG(int val) { - return clamp_val(SCALE(val, 1, 1000), -128000, 127000); + return SCALE(clamp_val(val, -128000, 127000), 1, 1000); } static inline int TEMP_FROM_REG(s8 val) @@ -384,6 +384,8 @@ static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, err = kstrtoul(buf, 10, &val); if (err) return err; + if (val > 255) + return -EINVAL; data->vrm = val; return count; From b0c6b604a01b85e4f0ce86272e8e49ae9e52a8d5 Mon Sep 17 00:00:00 2001 From: Sven Wegener Date: Tue, 22 Jul 2014 10:26:06 +0200 Subject: [PATCH 0103/1185] x86_32, entry: Store badsys error code in %eax commit 8142b215501f8b291a108a202b3a053a265b03dd upstream. Commit 554086d ("x86_32, entry: Do syscall exit work on badsys (CVE-2014-4508)") introduced a regression in the x86_32 syscall entry code, resulting in syscall() not returning proper errors for undefined syscalls on CPUs supporting the sysenter feature. The following code: > int result = syscall(666); > printf("result=%d errno=%d error=%s\n", result, errno, strerror(errno)); results in: > result=666 errno=0 error=Success Obviously, the syscall return value is the called syscall number, but it should have been an ENOSYS error. When run under ptrace it behaves correctly, which makes it hard to debug in the wild: > result=-1 errno=38 error=Function not implemented The %eax register is the return value register. For debugging via ptrace the syscall entry code stores the complete register context on the stack. The badsys handlers only store the ENOSYS error code in the ptrace register set and do not set %eax like a regular syscall handler would. The old resume_userspace call chain contains code that clobbers %eax and it restores %eax from the ptrace registers afterwards. The same goes for the ptrace-enabled call chain. When ptrace is not used, the syscall return value is the passed-in syscall number from the untouched %eax register. Use %eax as the return value register in syscall_badsys and sysenter_badsys, like a real syscall handler does, and have the caller push the value onto the stack for ptrace access. Signed-off-by: Sven Wegener Link: http://lkml.kernel.org/r/alpine.LNX.2.11.1407221022380.31021@titan.int.lan.stealer.net Reviewed-and-tested-by: Andy Lutomirski Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/entry_32.S | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index ac6328176097..08fa44443a01 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -436,8 +436,8 @@ sysenter_do_call: cmpl $(NR_syscalls), %eax jae sysenter_badsys call *sys_call_table(,%eax,4) - movl %eax,PT_EAX(%esp) sysenter_after_call: + movl %eax,PT_EAX(%esp) LOCKDEP_SYS_EXIT DISABLE_INTERRUPTS(CLBR_ANY) TRACE_IRQS_OFF @@ -517,6 +517,7 @@ ENTRY(system_call) jae syscall_badsys syscall_call: call *sys_call_table(,%eax,4) +syscall_after_call: movl %eax,PT_EAX(%esp) # store the return value syscall_exit: LOCKDEP_SYS_EXIT @@ -686,12 +687,12 @@ syscall_fault: END(syscall_fault) syscall_badsys: - movl $-ENOSYS,PT_EAX(%esp) - jmp syscall_exit + movl $-ENOSYS,%eax + jmp syscall_after_call END(syscall_badsys) sysenter_badsys: - movl $-ENOSYS,PT_EAX(%esp) + movl $-ENOSYS,%eax jmp sysenter_after_call END(syscall_badsys) CFI_ENDPROC From 32226c206801036be8d17e183ee51391f601a6cc Mon Sep 17 00:00:00 2001 From: Naoya Horiguchi Date: Wed, 23 Jul 2014 14:00:19 -0700 Subject: [PATCH 0104/1185] mm: hugetlb: fix copy_hugetlb_page_range() commit 0253d634e0803a8376a0d88efee0bf523d8673f9 upstream. Commit 4a705fef9862 ("hugetlb: fix copy_hugetlb_page_range() to handle migration/hwpoisoned entry") changed the order of huge_ptep_set_wrprotect() and huge_ptep_get(), which leads to breakage in some workloads like hugepage-backed heap allocation via libhugetlbfs. This patch fixes it. The test program for the problem is shown below: $ cat heap.c #include #include #include #define HPS 0x200000 int main() { int i; char *p = malloc(HPS); memset(p, '1', HPS); for (i = 0; i < 5; i++) { if (!fork()) { memset(p, '2', HPS); p = malloc(HPS); memset(p, '3', HPS); free(p); return 0; } } sleep(1); free(p); return 0; } $ export HUGETLB_MORECORE=yes ; export HUGETLB_NO_PREFAULT= ; hugectl --heap ./heap Fixes 4a705fef9862 ("hugetlb: fix copy_hugetlb_page_range() to handle migration/hwpoisoned entry"), so is applicable to -stable kernels which include it. Signed-off-by: Naoya Horiguchi Reported-by: Guillaume Morin Suggested-by: Guillaume Morin Acked-by: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/hugetlb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index dbc949c409c7..7de4f67c81fe 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -2400,6 +2400,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, } else { if (cow) huge_ptep_set_wrprotect(src, addr, src_pte); + entry = huge_ptep_get(src_pte); ptepage = pte_page(entry); get_page(ptepage); page_dup_rmap(ptepage); From e1d8240bdd4d65dd1de6eda2b2351ddd2fbb9ca4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 26 Jul 2014 14:52:01 -0700 Subject: [PATCH 0105/1185] Fix gcc-4.9.0 miscompilation of load_balance() in scheduler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2062afb4f804afef61cbe62a30cac9a46e58e067 upstream. Michel Dänzer and a couple of other people reported inexplicable random oopses in the scheduler, and the cause turns out to be gcc mis-compiling the load_balance() function when debugging is enabled. The gcc bug apparently goes back to gcc-4.5, but slight optimization changes means that it now showed up as a problem in 4.9.0 and 4.9.1. The instruction scheduling problem causes gcc to schedule a spill operation to before the stack frame has been created, which in turn can corrupt the spilled value if an interrupt comes in. There may be other effects of this bug too, but that's the code generation problem seen in Michel's case. This is fixed in current gcc HEAD, but the workaround as suggested by Markus Trippelsdorf is pretty simple: use -fno-var-tracking-assignments when compiling the kernel, which disables the gcc code that causes the problem. This can result in slightly worse debug information for variable accesses, but that is infinitely preferable to actual code generation problems. Doing this unconditionally (not just for CONFIG_DEBUG_INFO) also allows non-debug builds to verify that the debug build would be identical: we can do export GCC_COMPARE_DEBUG=1 to make gcc internally verify that the result of the build is independent of the "-g" flag (it will make the compiler build everything twice, toggling the debug flag, and compare the results). Without the "-fno-var-tracking-assignments" option, the build would fail (even with 4.8.3 that didn't show the actual stack frame bug) with a gcc compare failure. See also gcc bugzilla: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61801 Reported-by: Michel Dänzer Suggested-by: Markus Trippelsdorf Cc: Jakub Jelinek Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 8d891c66803c..ffd8082dceb2 100644 --- a/Makefile +++ b/Makefile @@ -614,6 +614,8 @@ KBUILD_CFLAGS += -fomit-frame-pointer endif endif +KBUILD_CFLAGS += $(call cc-option, -fno-var-tracking-assignments) + ifdef CONFIG_DEBUG_INFO KBUILD_CFLAGS += -g KBUILD_AFLAGS += -gdwarf-2 From a940d7b23bc073c774f3733c79f82102ffccff4e Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 23 Jun 2014 15:29:40 +0200 Subject: [PATCH 0106/1185] s390/ptrace: fix PSW mask check commit dab6cf55f81a6e16b8147aed9a843e1691dcd318 upstream. The PSW mask check of the PTRACE_POKEUSR_AREA command is incorrect. The PSW_MASK_USER define contains the PSW_MASK_ASC bits, the ptrace interface accepts all combinations for the address-space-control bits. To protect the kernel space the PSW mask check in ptrace needs to reject the address-space-control bit combination for home space. Fixes CVE-2014-3534 Signed-off-by: Martin Schwidefsky Cc: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- arch/s390/kernel/ptrace.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index a314c57f4e94..9677d935583c 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -314,7 +314,9 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data) * psw and gprs are stored on the stack */ if (addr == (addr_t) &dummy->regs.psw.mask && - ((data & ~PSW_MASK_USER) != psw_user_bits || + (((data^psw_user_bits) & ~PSW_MASK_USER) || + (((data^psw_user_bits) & PSW_MASK_ASC) && + ((data|psw_user_bits) & PSW_MASK_ASC) == PSW_MASK_ASC) || ((data & PSW_MASK_EA) && !(data & PSW_MASK_BA)))) /* Invalid psw mask. */ return -EINVAL; @@ -627,7 +629,10 @@ static int __poke_user_compat(struct task_struct *child, */ if (addr == (addr_t) &dummy32->regs.psw.mask) { /* Build a 64 bit psw mask from 31 bit mask. */ - if ((tmp & ~PSW32_MASK_USER) != psw32_user_bits) + if (((tmp^psw32_user_bits) & ~PSW32_MASK_USER) || + (((tmp^psw32_user_bits) & PSW32_MASK_ASC) && + ((tmp|psw32_user_bits) & PSW32_MASK_ASC) + == PSW32_MASK_ASC)) /* Invalid psw mask. */ return -EINVAL; regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | From 819ab9941c98f18b0f8c7ffb815e4f07186d2a5f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 10 Jul 2014 12:26:20 +0100 Subject: [PATCH 0107/1185] x86/efi: Include a .bss section within the PE/COFF headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c7fb93ec51d462ec3540a729ba446663c26a0505 upstream. The PE/COFF headers currently describe only the initialised-data portions of the image, and result in no space being allocated for the uninitialised-data portions. Consequently, the EFI boot stub will end up overwriting unexpected areas of memory, with unpredictable results. Fix by including a .bss section in the PE/COFF headers (functionally equivalent to the init_size field in the bzImage header). Signed-off-by: Michael Brown Cc: Thomas Bächler Cc: Josh Boyer Signed-off-by: Matt Fleming Signed-off-by: Greg Kroah-Hartman --- arch/x86/boot/header.S | 26 ++++++++++++++++++++++---- arch/x86/boot/tools/build.c | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S index 9ec06a1f6d61..425712462178 100644 --- a/arch/x86/boot/header.S +++ b/arch/x86/boot/header.S @@ -91,10 +91,9 @@ bs_die: .section ".bsdata", "a" bugger_off_msg: - .ascii "Direct floppy boot is not supported. " - .ascii "Use a boot loader program instead.\r\n" + .ascii "Use a boot loader.\r\n" .ascii "\n" - .ascii "Remove disk and press any key to reboot ...\r\n" + .ascii "Remove disk and press any key to reboot...\r\n" .byte 0 #ifdef CONFIG_EFI_STUB @@ -108,7 +107,7 @@ coff_header: #else .word 0x8664 # x86-64 #endif - .word 3 # nr_sections + .word 4 # nr_sections .long 0 # TimeDateStamp .long 0 # PointerToSymbolTable .long 1 # NumberOfSymbols @@ -250,6 +249,25 @@ section_table: .word 0 # NumberOfLineNumbers .long 0x60500020 # Characteristics (section flags) + # + # The offset & size fields are filled in by build.c. + # + .ascii ".bss" + .byte 0 + .byte 0 + .byte 0 + .byte 0 + .long 0 + .long 0x0 + .long 0 # Size of initialized data + # on disk + .long 0x0 + .long 0 # PointerToRelocations + .long 0 # PointerToLineNumbers + .word 0 # NumberOfRelocations + .word 0 # NumberOfLineNumbers + .long 0xc8000080 # Characteristics (section flags) + #endif /* CONFIG_EFI_STUB */ # Kernel attributes; used by setup. This is part 1 of the diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c index 94c544650020..971a0ce062aa 100644 --- a/arch/x86/boot/tools/build.c +++ b/arch/x86/boot/tools/build.c @@ -141,7 +141,7 @@ static void usage(void) #ifdef CONFIG_EFI_STUB -static void update_pecoff_section_header(char *section_name, u32 offset, u32 size) +static void update_pecoff_section_header_fields(char *section_name, u32 vma, u32 size, u32 datasz, u32 offset) { unsigned int pe_header; unsigned short num_sections; @@ -162,10 +162,10 @@ static void update_pecoff_section_header(char *section_name, u32 offset, u32 siz put_unaligned_le32(size, section + 0x8); /* section header vma field */ - put_unaligned_le32(offset, section + 0xc); + put_unaligned_le32(vma, section + 0xc); /* section header 'size of initialised data' field */ - put_unaligned_le32(size, section + 0x10); + put_unaligned_le32(datasz, section + 0x10); /* section header 'file offset' field */ put_unaligned_le32(offset, section + 0x14); @@ -177,6 +177,11 @@ static void update_pecoff_section_header(char *section_name, u32 offset, u32 siz } } +static void update_pecoff_section_header(char *section_name, u32 offset, u32 size) +{ + update_pecoff_section_header_fields(section_name, offset, size, size, offset); +} + static void update_pecoff_setup_and_reloc(unsigned int size) { u32 setup_offset = 0x200; @@ -201,9 +206,6 @@ static void update_pecoff_text(unsigned int text_start, unsigned int file_sz) pe_header = get_unaligned_le32(&buf[0x3c]); - /* Size of image */ - put_unaligned_le32(file_sz, &buf[pe_header + 0x50]); - /* * Size of code: Subtract the size of the first sector (512 bytes) * which includes the header. @@ -218,6 +220,22 @@ static void update_pecoff_text(unsigned int text_start, unsigned int file_sz) update_pecoff_section_header(".text", text_start, text_sz); } +static void update_pecoff_bss(unsigned int file_sz, unsigned int init_sz) +{ + unsigned int pe_header; + unsigned int bss_sz = init_sz - file_sz; + + pe_header = get_unaligned_le32(&buf[0x3c]); + + /* Size of uninitialized data */ + put_unaligned_le32(bss_sz, &buf[pe_header + 0x24]); + + /* Size of image */ + put_unaligned_le32(init_sz, &buf[pe_header + 0x50]); + + update_pecoff_section_header_fields(".bss", file_sz, bss_sz, 0, 0); +} + #endif /* CONFIG_EFI_STUB */ @@ -268,6 +286,9 @@ int main(int argc, char ** argv) int fd; void *kernel; u32 crc = 0xffffffffUL; +#ifdef CONFIG_EFI_STUB + unsigned int init_sz; +#endif /* Defaults for old kernel */ #ifdef CONFIG_X86_32 @@ -338,7 +359,9 @@ int main(int argc, char ** argv) put_unaligned_le32(sys_size, &buf[0x1f4]); #ifdef CONFIG_EFI_STUB - update_pecoff_text(setup_sectors * 512, sz + i + ((sys_size * 16) - sz)); + update_pecoff_text(setup_sectors * 512, i + (sys_size * 16)); + init_sz = get_unaligned_le32(&buf[0x260]); + update_pecoff_bss(i + (sys_size * 16), init_sz); #ifdef CONFIG_X86_64 /* Yes, this is really how we defined it :( */ efi_stub_entry -= 0x200; From c5f0c0e7525443add533495e93ba8de6feab2396 Mon Sep 17 00:00:00 2001 From: Zoltan Kiss Date: Wed, 26 Mar 2014 22:37:45 +0000 Subject: [PATCH 0108/1185] core, nfqueue, openvswitch: Orphan frags in skb_zerocopy and handle errors commit 36d5fe6a000790f56039afe26834265db0a3ad4c upstream. skb_zerocopy can copy elements of the frags array between skbs, but it doesn't orphan them. Also, it doesn't handle errors, so this patch takes care of that as well, and modify the callers accordingly. skb_tx_error() is also added to the callers so they will signal the failed delivery towards the creator of the skb. Signed-off-by: Zoltan Kiss Signed-off-by: David S. Miller [bwh: Backported to 3.13: skb_zerocopy() is new in 3.14, but was moved from a static function in nfnetlink_queue. We need to patch that and its caller, but not openvswitch.] Signed-off-by: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nfnetlink_queue_core.c | 29 ++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index 5352b2d2d5bf..2b8199f68785 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -227,22 +227,23 @@ nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data) spin_unlock_bh(&queue->lock); } -static void +static int nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen) { int i, j = 0; int plen = 0; /* length of skb->head fragment */ + int ret; struct page *page; unsigned int offset; /* dont bother with small payloads */ - if (len <= skb_tailroom(to)) { - skb_copy_bits(from, 0, skb_put(to, len), len); - return; - } + if (len <= skb_tailroom(to)) + return skb_copy_bits(from, 0, skb_put(to, len), len); if (hlen) { - skb_copy_bits(from, 0, skb_put(to, hlen), hlen); + ret = skb_copy_bits(from, 0, skb_put(to, hlen), hlen); + if (unlikely(ret)) + return ret; len -= hlen; } else { plen = min_t(int, skb_headlen(from), len); @@ -260,6 +261,11 @@ nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen) to->len += len + plen; to->data_len += len + plen; + if (unlikely(skb_orphan_frags(from, GFP_ATOMIC))) { + skb_tx_error(from); + return -ENOMEM; + } + for (i = 0; i < skb_shinfo(from)->nr_frags; i++) { if (!len) break; @@ -270,6 +276,8 @@ nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen) j++; } skb_shinfo(to)->nr_frags = j; + + return 0; } static int nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet) @@ -355,13 +363,16 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, skb = nfnetlink_alloc_skb(&init_net, size, queue->peer_portid, GFP_ATOMIC); - if (!skb) + if (!skb) { + skb_tx_error(entskb); return NULL; + } nlh = nlmsg_put(skb, 0, 0, NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET, sizeof(struct nfgenmsg), 0); if (!nlh) { + skb_tx_error(entskb); kfree_skb(skb); return NULL; } @@ -481,13 +492,15 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, nla->nla_type = NFQA_PAYLOAD; nla->nla_len = nla_attr_size(data_len); - nfqnl_zcopy(skb, entskb, data_len, hlen); + if (nfqnl_zcopy(skb, entskb, data_len, hlen)) + goto nla_put_failure; } nlh->nlmsg_len = skb->len; return skb; nla_put_failure: + skb_tx_error(entskb); kfree_skb(skb); net_err_ratelimited("nf_queue: error creating packet message\n"); return NULL; From 10a622493d7f9343e8b4118031ff0c21a27cc4e9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 31 Jul 2014 14:55:39 -0700 Subject: [PATCH 0109/1185] Linux 3.10.51 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ffd8082dceb2..f9f6ee59c61a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 10 -SUBLEVEL = 50 +SUBLEVEL = 51 EXTRAVERSION = NAME = TOSSUG Baby Fish From e31266f78058409d18d48e0afa8339e77322b17f Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Mon, 4 Aug 2014 15:47:44 +0100 Subject: [PATCH 0110/1185] gator: Version 5.19 Signed-off-by: Jon Medhurst --- drivers/gator/Makefile | 15 +- drivers/gator/gator.h | 16 +- drivers/gator/gator_backtrace.c | 2 +- drivers/gator/gator_buffer.c | 6 +- drivers/gator/gator_events_armv7.c | 4 +- drivers/gator/gator_events_block.c | 12 +- drivers/gator/gator_events_mali_4xx.c | 144 ++++------- drivers/gator/gator_events_mali_common.c | 36 +-- drivers/gator/gator_events_mali_common.h | 20 +- drivers/gator/gator_events_mali_t6xx.c | 5 +- drivers/gator/gator_events_mali_t6xx_hw.c | 185 ++++++++++++--- drivers/gator/gator_events_mmapped.c | 26 +- drivers/gator/gator_events_perf_pmu.c | 6 +- drivers/gator/gator_events_scorpion.c | 4 +- drivers/gator/gator_events_threads.c | 115 +++++++++ drivers/gator/gator_iks.c | 2 +- drivers/gator/gator_main.c | 126 ++++++++-- drivers/gator/gator_marshaling.c | 119 ++++------ drivers/gator/gator_trace_gpu.c | 178 ++++++++------ drivers/gator/gator_trace_gpu.h | 79 ------- drivers/gator/gator_trace_power.c | 4 +- drivers/gator/gator_trace_sched.c | 85 ++++--- drivers/gator/mali/mali_dd_gator_api.h | 40 ++++ drivers/gator/mali_t6xx.mk | 4 + tools/gator/daemon/Android.mk | 6 +- tools/gator/daemon/Application.mk | 1 + tools/gator/daemon/Buffer.cpp | 36 ++- tools/gator/daemon/Buffer.h | 11 +- tools/gator/daemon/CapturedXML.cpp | 11 +- tools/gator/daemon/CapturedXML.h | 2 +- tools/gator/daemon/Child.cpp | 45 ++-- tools/gator/daemon/Child.h | 2 +- tools/gator/daemon/ConfigurationXML.cpp | 10 +- tools/gator/daemon/Counter.h | 4 + tools/gator/daemon/DriverSource.cpp | 53 ++++- tools/gator/daemon/DriverSource.h | 5 + tools/gator/daemon/EventsXML.cpp | 16 +- tools/gator/daemon/EventsXML.h | 5 +- tools/gator/daemon/ExternalSource.cpp | 177 +++++++++++++- tools/gator/daemon/ExternalSource.h | 11 +- tools/gator/daemon/FSDriver.cpp | 212 +++++++++++++++++ tools/gator/daemon/FSDriver.h | 44 ++++ tools/gator/daemon/Fifo.h | 2 +- tools/gator/daemon/Hwmon.cpp | 16 +- tools/gator/daemon/KMod.cpp | 11 +- tools/gator/daemon/LocalCapture.h | 2 +- tools/gator/daemon/Logging.h | 2 +- tools/gator/daemon/Makefile | 8 +- tools/gator/daemon/Makefile_aarch64 | 9 +- tools/gator/daemon/MaliVideoDriver.cpp | 253 ++++++++++++++++++++ tools/gator/daemon/MaliVideoDriver.h | 50 ++++ tools/gator/daemon/Monitor.cpp | 11 +- tools/gator/daemon/Monitor.h | 1 + tools/gator/daemon/OlySocket.cpp | 77 ++---- tools/gator/daemon/OlySocket.h | 14 +- tools/gator/daemon/PerfDriver.cpp | 92 ++++++-- tools/gator/daemon/PerfDriver.h | 6 +- tools/gator/daemon/PerfGroup.cpp | 28 ++- tools/gator/daemon/PerfGroup.h | 2 + tools/gator/daemon/PerfSource.cpp | 14 +- tools/gator/daemon/Proc.cpp | 106 ++++++--- tools/gator/daemon/Proc.h | 2 +- tools/gator/daemon/Sender.h | 2 +- tools/gator/daemon/SessionData.cpp | 55 ++++- tools/gator/daemon/SessionData.h | 17 +- tools/gator/daemon/SessionXML.cpp | 8 +- tools/gator/daemon/StreamlineSetup.cpp | 2 +- tools/gator/daemon/StreamlineSetup.h | 4 +- tools/gator/daemon/UEvent.cpp | 3 +- tools/gator/daemon/UserSpaceSource.cpp | 14 +- tools/gator/daemon/UserSpaceSource.h | 2 +- tools/gator/daemon/c++.cpp | 40 ++++ tools/gator/daemon/common.mk | 16 +- tools/gator/daemon/defaults.xml | 5 + tools/gator/daemon/escape.c | 2 +- tools/gator/daemon/events-CCI-400.xml | 21 +- tools/gator/daemon/events-CCN-504.xml | 9 - tools/gator/daemon/events-Cortex-A53.xml | 84 ------- tools/gator/daemon/events-Cortex-A57.xml | 84 ------- tools/gator/daemon/events-Filesystem.xml | 11 + tools/gator/daemon/events-L2C-310.xml | 30 +-- tools/gator/daemon/events-Linux.xml | 5 +- tools/gator/daemon/events-Mali-4xx.xml | 126 +++++----- tools/gator/daemon/events-Mali-T6xx.xml | 26 +- tools/gator/daemon/events-Mali-T6xx_hw.xml | 33 +-- tools/gator/daemon/events-Mali-V500.xml | 29 +++ tools/gator/daemon/main.cpp | 262 ++++++++++++--------- 87 files changed, 2316 insertions(+), 1164 deletions(-) create mode 100644 drivers/gator/gator_events_threads.c delete mode 100644 drivers/gator/gator_trace_gpu.h create mode 100644 drivers/gator/mali/mali_dd_gator_api.h create mode 100644 tools/gator/daemon/Application.mk create mode 100644 tools/gator/daemon/FSDriver.cpp create mode 100644 tools/gator/daemon/FSDriver.h create mode 100644 tools/gator/daemon/MaliVideoDriver.cpp create mode 100644 tools/gator/daemon/MaliVideoDriver.h create mode 100644 tools/gator/daemon/c++.cpp create mode 100644 tools/gator/daemon/events-Filesystem.xml create mode 100644 tools/gator/daemon/events-Mali-V500.xml diff --git a/drivers/gator/Makefile b/drivers/gator/Makefile index 3dc9d059a4b4..2f86823313c6 100644 --- a/drivers/gator/Makefile +++ b/drivers/gator/Makefile @@ -7,13 +7,14 @@ CONFIG_GATOR ?= m obj-$(CONFIG_GATOR) := gator.o gator-y := gator_main.o \ - gator_events_irq.o \ - gator_events_sched.o \ - gator_events_net.o \ gator_events_block.o \ + gator_events_irq.o \ gator_events_meminfo.o \ - gator_events_perf_pmu.o \ gator_events_mmapped.o \ + gator_events_net.o \ + gator_events_perf_pmu.o \ + gator_events_sched.o \ + gator_events_threads.o \ # Convert the old GATOR_WITH_MALI_SUPPORT to the new kernel flags ifneq ($(GATOR_WITH_MALI_SUPPORT),) @@ -48,10 +49,14 @@ ifeq ($(CONFIG_GATOR_WITH_MALI_SUPPORT),y) ccflags-$(CONFIG_GATOR_MALI_T6XX) += -DMALI_SUPPORT=MALI_T6xx endif -# GATOR_TEST controls whether to include (=1) or exclude (=0) test code. +# GATOR_TEST controls whether to include (=1) or exclude (=0) test code. GATOR_TEST ?= 0 EXTRA_CFLAGS += -DGATOR_TEST=$(GATOR_TEST) +# Should the original or new block_rq_complete API be used? +OLD_BLOCK_RQ_COMPLETE := $(shell grep -A3 block_rq_complete include/trace/events/block.h | grep nr_bytes > /dev/null; echo $$?) +EXTRA_CFLAGS += -DOLD_BLOCK_RQ_COMPLETE=$(OLD_BLOCK_RQ_COMPLETE) + gator-$(CONFIG_ARM) += gator_events_armv6.o \ gator_events_armv7.o \ gator_events_ccn-504.o \ diff --git a/drivers/gator/gator.h b/drivers/gator/gator.h index 586cd9e742fb..5ad0254d86a9 100644 --- a/drivers/gator/gator.h +++ b/drivers/gator/gator.h @@ -42,6 +42,10 @@ #define AARCH64 0xd0f #define OTHER 0xfff +// gpu enums +#define MALI_4xx 1 +#define MALI_T6xx 2 + #define MAXSIZE_CORE_NAME 32 struct gator_cpu { @@ -82,13 +86,21 @@ int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, register_trace_##probe_name(probe_##probe_name) # define GATOR_UNREGISTER_TRACE(probe_name) \ unregister_trace_##probe_name(probe_##probe_name) -#else +#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) # define GATOR_DEFINE_PROBE(probe_name, proto) \ static void probe_##probe_name(void *data, PARAMS(proto)) # define GATOR_REGISTER_TRACE(probe_name) \ register_trace_##probe_name(probe_##probe_name, NULL) # define GATOR_UNREGISTER_TRACE(probe_name) \ unregister_trace_##probe_name(probe_##probe_name, NULL) +#else +# define GATOR_DEFINE_PROBE(probe_name, proto) \ + extern struct tracepoint *gator_tracepoint_##probe_name; \ + static void probe_##probe_name(void *data, PARAMS(proto)) +# define GATOR_REGISTER_TRACE(probe_name) \ + tracepoint_probe_register(gator_tracepoint_##probe_name, probe_##probe_name, NULL) +# define GATOR_UNREGISTER_TRACE(probe_name) \ + tracepoint_probe_unregister(gator_tracepoint_##probe_name, probe_##probe_name, NULL) #endif /****************************************************************************** @@ -115,6 +127,8 @@ u32 gator_cpuid(void); void gator_backtrace_handler(struct pt_regs *const regs); +void gator_marshal_activity_switch(int core, int key, int activity, int pid); + #if !GATOR_IKS_SUPPORT #define get_physical_cpu() smp_processor_id() diff --git a/drivers/gator/gator_backtrace.c b/drivers/gator/gator_backtrace.c index 9f305cf7242c..e03c1653c5b5 100644 --- a/drivers/gator/gator_backtrace.c +++ b/drivers/gator/gator_backtrace.c @@ -178,7 +178,7 @@ static void kernel_backtrace(int cpu, struct pt_regs *const regs) marshal_backtrace(PC_REG & ~1, NO_COOKIE, 1); #endif } - + static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time) { bool in_kernel; diff --git a/drivers/gator/gator_buffer.c b/drivers/gator/gator_buffer.c index eba22dfe3bf2..dfbc97d80221 100644 --- a/drivers/gator/gator_buffer.c +++ b/drivers/gator/gator_buffer.c @@ -37,12 +37,12 @@ static void marshal_frame(int cpu, int buftype) case SCHED_TRACE_BUF: frame = FRAME_SCHED_TRACE; break; - case GPU_TRACE_BUF: - frame = FRAME_GPU_TRACE; - break; case IDLE_BUF: frame = FRAME_IDLE; break; + case ACTIVITY_BUF: + frame = FRAME_ACTIVITY; + break; default: frame = -1; break; diff --git a/drivers/gator/gator_events_armv7.c b/drivers/gator/gator_events_armv7.c index 153119b463e6..bd8a9ba24e99 100644 --- a/drivers/gator/gator_events_armv7.c +++ b/drivers/gator/gator_events_armv7.c @@ -27,9 +27,9 @@ // ccnt reg #define CCNT_REG (1 << 31) -#define CCNT 0 +#define CCNT 0 #define CNT0 1 -#define CNTMAX (6+1) +#define CNTMAX (6+1) static const char *pmnc_name; static int pmnc_counters; diff --git a/drivers/gator/gator_events_block.c b/drivers/gator/gator_events_block.c index b2bc414e462e..03eed4fb9ebb 100644 --- a/drivers/gator/gator_events_block.c +++ b/drivers/gator/gator_events_block.c @@ -28,15 +28,25 @@ static ulong block_rq_rd_key; static atomic_t blockCnt[BLOCK_TOTAL]; static int blockGet[BLOCK_TOTAL * 4]; +// Tracepoint changed in 3.15 backported to older kernels. The Makefile tries to autodetect the correct value, but if it fails change the #if below +#if OLD_BLOCK_RQ_COMPLETE GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq)) +#else +GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq, unsigned int nr_bytes)) +#endif { - int write, size; + int write; + unsigned int size; if (!rq) return; write = rq->cmd_flags & EVENTWRITE; +#if OLD_BLOCK_RQ_COMPLETE size = rq->resid_len; +#else + size = nr_bytes; +#endif if (!size) return; diff --git a/drivers/gator/gator_events_mali_4xx.c b/drivers/gator/gator_events_mali_4xx.c index 85d47645a9d9..9e1c7064bd73 100644 --- a/drivers/gator/gator_events_mali_4xx.c +++ b/drivers/gator/gator_events_mali_4xx.c @@ -18,17 +18,27 @@ #include "gator_events_mali_4xx.h" /* - * There are (currently) four different variants of the comms between gator and Mali: - * 1 (deprecated): No software counter support - * 2 (deprecated): Tracepoint called for each separate s/w counter value as it appears - * 3 (default): Single tracepoint for all s/w counters in a bundle. - * Interface style 3 is the default if no other is specified. 1 and 2 will be eliminated when - * existing Mali DDKs are upgraded. - * 4. As above, but for the Utgard (Mali-450) driver. - */ +* There have been four different variants of the comms between gator and Mali depending on driver version: +* # | DDK vsn range | Support | Notes +* +* 1 | (obsolete) | No software counter support | Obsolete patches +* 2 | (obsolete) | Tracepoint called for each separate s/w counter value as it appears | Obsolete patches +* 3 | r3p0-04rel0 - r3p2-01rel2 | Single tracepoint for all s/w counters in a bundle. | +* 4 | r3p2-01rel3 - date | As above but with extensions for MP devices (Mali-450) | At least r4p0-00rel1 +*/ #if !defined(GATOR_MALI_INTERFACE_STYLE) -#define GATOR_MALI_INTERFACE_STYLE (3) +#define GATOR_MALI_INTERFACE_STYLE (4) +#endif + +#if GATOR_MALI_INTERFACE_STYLE == 1 +#error GATOR_MALI_INTERFACE_STYLE 1 is obsolete +#elif GATOR_MALI_INTERFACE_STYLE == 2 +#error GATOR_MALI_INTERFACE_STYLE 2 is obsolete +#elif GATOR_MALI_INTERFACE_STYLE >= 3 +// Valid GATOR_MALI_INTERFACE_STYLE +#else +#error Unknown GATOR_MALI_INTERFACE_STYLE option. #endif #if GATOR_MALI_INTERFACE_STYLE < 4 @@ -44,6 +54,8 @@ #error MALI_SUPPORT set to an invalid device code: expecting MALI_4xx #endif +static const char mali_name[] = "Mali-4xx"; + /* gatorfs variables for counter enable state, * the event the counter should count and the * 'key' (a unique id set by gatord and returned @@ -63,6 +75,7 @@ static u32 *counter_address[NUMBER_OF_EVENTS]; */ static unsigned long counter_dump[NUMBER_OF_EVENTS * 2]; static unsigned long counter_prev[NUMBER_OF_EVENTS]; +static bool prev_set[NUMBER_OF_EVENTS]; /* Note whether tracepoints have been registered */ static int trace_registered; @@ -76,18 +89,11 @@ static unsigned int n_vp_cores = MAX_NUM_VP_CORES; static unsigned int n_l2_cores = MAX_NUM_L2_CACHE_CORES; static unsigned int n_fp_cores = MAX_NUM_FP_CORES; -/** - * Calculate the difference and handle the overflow. - */ -static u32 get_difference(u32 start, u32 end) -{ - if (start - end >= 0) { - return start - end; - } - - // Mali counters are unsigned 32 bit values that wrap. - return (4294967295u - end) + start; -} +extern mali_counter mali_activity[2]; +static const char* const mali_activity_names[] = { + "fragment", + "vertex", +}; /** * Returns non-zero if the given counter ID is an activity counter. @@ -112,40 +118,6 @@ static inline int is_hw_counter(unsigned int event_id) typedef void _mali_profiling_get_mali_version_type(struct _mali_profiling_mali_version *values); typedef u32 _mali_profiling_get_l2_counters_type(_mali_profiling_l2_counter_values *values); -#if GATOR_MALI_INTERFACE_STYLE == 2 -/** - * Returns non-zero if the given counter ID is a software counter. - */ -static inline int is_sw_counter(unsigned int event_id) -{ - return (event_id >= FIRST_SW_COUNTER && event_id <= LAST_SW_COUNTER); -} -#endif - -#if GATOR_MALI_INTERFACE_STYLE == 2 -/* - * The Mali DDK uses s64 types to contain software counter values, but gator - * can only use a maximum of 32 bits. This function scales a software counter - * to an appropriate range. - */ -static u32 scale_sw_counter_value(unsigned int event_id, signed long long value) -{ - u32 scaled_value; - - switch (event_id) { - case COUNTER_GLES_UPLOAD_TEXTURE_TIME: - case COUNTER_GLES_UPLOAD_VBO_TIME: - scaled_value = (u32)div_s64(value, 1000000); - break; - default: - scaled_value = (u32)value; - break; - } - - return scaled_value; -} -#endif - /* Probe for continuously sampled counter */ #if 0 //WE_DONT_CURRENTLY_USE_THIS_SO_SUPPRESS_WARNING GATOR_DEFINE_PROBE(mali_sample_address, TP_PROTO(unsigned int event_id, u32 *addr)) @@ -172,16 +144,6 @@ GATOR_DEFINE_PROBE(mali_hw_counter, TP_PROTO(unsigned int event_id, unsigned int } } -#if GATOR_MALI_INTERFACE_STYLE == 2 -GATOR_DEFINE_PROBE(mali_sw_counter, TP_PROTO(unsigned int event_id, signed long long value)) -{ - if (is_sw_counter(event_id)) { - counter_data[event_id] = scale_sw_counter_value(event_id, value); - } -} -#endif /* GATOR_MALI_INTERFACE_STYLE == 2 */ - -#if GATOR_MALI_INTERFACE_STYLE >= 3 GATOR_DEFINE_PROBE(mali_sw_counters, TP_PROTO(pid_t pid, pid_t tid, void *surface_id, unsigned int *counters)) { u32 i; @@ -193,7 +155,6 @@ GATOR_DEFINE_PROBE(mali_sw_counters, TP_PROTO(pid_t pid, pid_t tid, void *surfac } } } -#endif /* GATOR_MALI_INTERFACE_STYLE >= 3 */ /** * Create a single filesystem entry for a specified event. @@ -254,6 +215,7 @@ static void initialise_version_info(void) symbol_put(_mali_profiling_get_mali_version); } else { printk("gator: mali online _mali_profiling_get_mali_version symbol not found\n"); + printk("gator: check your Mali DDK version versus the GATOR_MALI_INTERFACE_STYLE setting\n"); } } #endif @@ -261,7 +223,6 @@ static void initialise_version_info(void) static int create_files(struct super_block *sb, struct dentry *root) { int event; - const char *mali_name = gator_mali_get_mali_name(); char buf[40]; int core_id; @@ -278,6 +239,14 @@ static int create_files(struct super_block *sb, struct dentry *root) initialise_version_info(); #endif + mali_activity[0].cores = n_fp_cores; + mali_activity[1].cores = n_vp_cores; + for (event = 0; event < ARRAY_SIZE(mali_activity); event++) { + if (gator_mali_create_file_system(mali_name, mali_activity_names[event], sb, root, &mali_activity[event], NULL) != 0) { + return -1; + } + } + /* Vertex processor counters */ for (core_id = 0; core_id < n_vp_cores; core_id++) { int activity_counter_id = ACTIVITY_VP_0; @@ -413,7 +382,6 @@ static void init_counters(unsigned int from_counter, unsigned int to_counter) static void mali_counter_initialize(void) { int i; - int core_id; mali_profiling_control_type *mali_control; @@ -463,15 +431,10 @@ static void mali_counter_initialize(void) n_l2_cores = 0; } - for (core_id = 0; core_id < n_l2_cores; core_id++) { - int counter_id = COUNTER_L2_0_C0 + (2 * core_id); - counter_prev[counter_id] = 0; - counter_prev[counter_id + 1] = 0; - } - /* Clear counters in the start */ for (i = 0; i < NUMBER_OF_EVENTS; i++) { counter_data[i] = 0; + prev_set[i] = false; } } @@ -528,23 +491,11 @@ static int start(void) return -1; } -#if GATOR_MALI_INTERFACE_STYLE == 1 - /* None. */ -#elif GATOR_MALI_INTERFACE_STYLE == 2 - /* For patched Mali driver. */ - if (GATOR_REGISTER_TRACE(mali_sw_counter)) { - printk("gator: mali_sw_counter tracepoint failed to activate\n"); - return -1; - } -#elif GATOR_MALI_INTERFACE_STYLE >= 3 /* For Mali drivers with built-in support. */ if (GATOR_REGISTER_TRACE(mali_sw_counters)) { printk("gator: mali_sw_counters tracepoint failed to activate\n"); return -1; } -#else -#error Unknown GATOR_MALI_INTERFACE_STYLE option. -#endif trace_registered = 1; @@ -561,17 +512,8 @@ static void stop(void) if (trace_registered) { GATOR_UNREGISTER_TRACE(mali_hw_counter); -#if GATOR_MALI_INTERFACE_STYLE == 1 - /* None. */ -#elif GATOR_MALI_INTERFACE_STYLE == 2 - /* For patched Mali driver. */ - GATOR_UNREGISTER_TRACE(mali_sw_counter); -#elif GATOR_MALI_INTERFACE_STYLE >= 3 /* For Mali drivers with built-in support. */ GATOR_UNREGISTER_TRACE(mali_sw_counters); -#else -#error Unknown GATOR_MALI_INTERFACE_STYLE option. -#endif pr_debug("gator: mali timeline tracepoint deactivated\n"); @@ -636,21 +578,23 @@ static int read(int **buffer) per_core = &cache_values.cores[cache_id]; - if (counter_enabled[counter_id_0]) { + if (counter_enabled[counter_id_0] && prev_set[counter_id_0]) { // Calculate and save src0's counter val0 counter_dump[len++] = counter_key[counter_id_0]; - counter_dump[len++] = get_difference(per_core->value0, counter_prev[counter_id_0]); + counter_dump[len++] = per_core->value0 - counter_prev[counter_id_0]; } - if (counter_enabled[counter_id_1]) { + if (counter_enabled[counter_id_1] && prev_set[counter_id_1]) { // Calculate and save src1's counter val1 counter_dump[len++] = counter_key[counter_id_1]; - counter_dump[len++] = get_difference(per_core->value1, counter_prev[counter_id_1]); + counter_dump[len++] = per_core->value1 - counter_prev[counter_id_1]; } // Save the previous values for the counters. counter_prev[counter_id_0] = per_core->value0; + prev_set[counter_id_0] = true; counter_prev[counter_id_1] = per_core->value1; + prev_set[counter_id_1] = true; } } @@ -709,6 +653,8 @@ int gator_events_mali_init(void) pr_debug("gator: mali init\n"); + gator_mali_initialise_counters(mali_activity, ARRAY_SIZE(mali_activity)); + for (cnt = 0; cnt < NUMBER_OF_EVENTS; cnt++) { counter_enabled[cnt] = 0; counter_event[cnt] = 0; diff --git a/drivers/gator/gator_events_mali_common.c b/drivers/gator/gator_events_mali_common.c index dc58dcf0c662..4f2cce4ce67b 100644 --- a/drivers/gator/gator_events_mali_common.c +++ b/drivers/gator/gator_events_mali_common.c @@ -8,26 +8,6 @@ */ #include "gator_events_mali_common.h" -static u32 gator_mali_get_id(void) -{ - return MALI_SUPPORT; -} - -extern const char *gator_mali_get_mali_name(void) -{ - u32 id = gator_mali_get_id(); - - switch (id) { - case MALI_T6xx: - return "Mali-T6xx"; - case MALI_4xx: - return "Mali-4xx"; - default: - pr_debug("gator: Mali-T6xx: unknown Mali ID (%d)\n", id); - return "Mali-Unknown"; - } -} - extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter, unsigned long *event) { int err; @@ -42,24 +22,31 @@ extern int gator_mali_create_file_system(const char *mali_name, const char *even dir = gatorfs_mkdir(sb, root, buf); if (dir == NULL) { - pr_debug("gator: Mali-T6xx: error creating file system for: %s (%s)", event_name, buf); + pr_debug("gator: %s: error creating file system for: %s (%s)", mali_name, event_name, buf); return -1; } err = gatorfs_create_ulong(sb, dir, "enabled", &counter->enabled); if (err != 0) { - pr_debug("gator: Mali-T6xx: error calling gatorfs_create_ulong for: %s (%s)", event_name, buf); + pr_debug("gator: %s: error calling gatorfs_create_ulong for: %s (%s)", mali_name, event_name, buf); return -1; } err = gatorfs_create_ro_ulong(sb, dir, "key", &counter->key); if (err != 0) { - pr_debug("gator: Mali-T6xx: error calling gatorfs_create_ro_ulong for: %s (%s)", event_name, buf); + pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)", mali_name, event_name, buf); return -1; } + if (counter->cores != -1) { + err = gatorfs_create_ro_ulong(sb, dir, "cores", &counter->cores); + if (err != 0) { + pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)", mali_name, event_name, buf); + return -1; + } + } if (event != NULL) { err = gatorfs_create_ulong(sb, dir, "event", event); if (err != 0) { - pr_debug("gator: Mali-T6xx: error calling gatorfs_create_ro_ulong for: %s (%s)", event_name, buf); + pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)", mali_name, event_name, buf); return -1; } } @@ -77,5 +64,6 @@ extern void gator_mali_initialise_counters(mali_counter counters[], unsigned int counter->key = gator_events_get_key(); counter->enabled = 0; + counter->cores = -1; } } diff --git a/drivers/gator/gator_events_mali_common.h b/drivers/gator/gator_events_mali_common.h index 41c2a3c13fae..91d871bc915a 100644 --- a/drivers/gator/gator_events_mali_common.h +++ b/drivers/gator/gator_events_mali_common.h @@ -18,10 +18,6 @@ #include #include -/* Device codes for each known GPU */ -#define MALI_4xx (0x0b07) -#define MALI_T6xx (0x0056) - /* Ensure that MALI_SUPPORT has been defined to something. */ #ifndef MALI_SUPPORT #error MALI_SUPPORT not defined! @@ -35,8 +31,12 @@ * Runtime state information for a counter. */ typedef struct { - unsigned long key; /* 'key' (a unique id set by gatord and returned by gator.ko) */ - unsigned long enabled; /* counter enable state */ + // 'key' (a unique id set by gatord and returned by gator.ko) + unsigned long key; + // counter enable state + unsigned long enabled; + // for activity counters, the number of cores, otherwise -1 + unsigned long cores; } mali_counter; /* @@ -53,18 +53,10 @@ extern int _mali_profiling_set_event(unsigned int, int); extern void _mali_profiling_control(unsigned int, unsigned int); extern void _mali_profiling_get_counters(unsigned int *, unsigned int *, unsigned int *, unsigned int *); -/** - * Returns a name which identifies the GPU type (eg Mali-4xx, Mali-T6xx). - * - * @return The name as a constant string. - */ -extern const char *gator_mali_get_mali_name(void); - /** * Creates a filesystem entry under /dev/gator relating to the specified event name and key, and * associate the key/enable values with this entry point. * - * @param mali_name A name related to the type of GPU, obtained from a call to gator_mali_get_mali_name() * @param event_name The name of the event. * @param sb Linux super block * @param root Directory under which the entry will be created. diff --git a/drivers/gator/gator_events_mali_t6xx.c b/drivers/gator/gator_events_mali_t6xx.c index 76f14eee7676..e56ba84aefb8 100644 --- a/drivers/gator/gator_events_mali_t6xx.c +++ b/drivers/gator/gator_events_mali_t6xx.c @@ -32,6 +32,8 @@ #error MALI_SUPPORT set to an invalid device code: expecting MALI_T6xx #endif +static const char mali_name[] = "Mali-T6xx"; + /* Counters for Mali-T6xx: * * - Timeline events @@ -292,7 +294,6 @@ static int create_files(struct super_block *sb, struct dentry *root) * Create the filesystem for all events */ int counter_index = 0; - const char *mali_name = gator_mali_get_mali_name(); mali_profiling_control_type *mali_control; for (event = FIRST_TIMELINE_EVENT; event < FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS; event++) { @@ -317,7 +318,7 @@ static int create_files(struct super_block *sb, struct dentry *root) } mali_control = symbol_get(_mali_profiling_control); - if (mali_control) { + if (mali_control) { if (gator_mali_create_file_system(mali_name, "Filmstrip_cnt0", sb, root, &counters[FILMSTRIP], &filmstrip_event) != 0) { return -1; } diff --git a/drivers/gator/gator_events_mali_t6xx_hw.c b/drivers/gator/gator_events_mali_t6xx_hw.c index dfbc91ffd765..3a072bb6ac06 100644 --- a/drivers/gator/gator_events_mali_t6xx_hw.c +++ b/drivers/gator/gator_events_mali_t6xx_hw.c @@ -16,7 +16,10 @@ #include /* Mali T6xx DDK includes */ -#ifdef MALI_DIR_MIDGARD +#if defined(MALI_SIMPLE_API) +/* Header with wrapper functions to kbase structures and functions */ +#include "mali/mali_dd_gator_api.h" +#elif defined(MALI_DIR_MIDGARD) /* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/ #include "mali_linux_trace.h" #include "mali_kbase.h" @@ -28,37 +31,49 @@ #include "kbase/src/linux/mali_kbase_mem_linux.h" #endif -#include "gator_events_mali_common.h" - /* If API version is not specified then assume API version 1. */ #ifndef MALI_DDK_GATOR_API_VERSION #define MALI_DDK_GATOR_API_VERSION 1 #endif -#if (MALI_DDK_GATOR_API_VERSION != 1) && (MALI_DDK_GATOR_API_VERSION != 2) -#error MALI_DDK_GATOR_API_VERSION is invalid (must be 1 for r1/r2 DDK, or 2 for r3 DDK). +#if (MALI_DDK_GATOR_API_VERSION != 1) && (MALI_DDK_GATOR_API_VERSION != 2) && (MALI_DDK_GATOR_API_VERSION != 3) +#error MALI_DDK_GATOR_API_VERSION is invalid (must be 1 for r1/r2 DDK, or 2 for r3 DDK, or 3 for r? DDK). #endif +#include "gator_events_mali_common.h" + /* * Mali-T6xx */ +#if MALI_DDK_GATOR_API_VERSION == 3 +typedef uint32_t kbase_dd_instr_hwcnt_dump_irq_type(struct mali_dd_hwcnt_handles *); +typedef uint32_t kbase_dd_instr_hwcnt_dump_complete_type(struct mali_dd_hwcnt_handles *, uint32_t *); +typedef struct mali_dd_hwcnt_handles* mali_dd_hwcnt_init_type(struct mali_dd_hwcnt_info *); +typedef void mali_dd_hwcnt_clear_type(struct mali_dd_hwcnt_info *, struct mali_dd_hwcnt_handles *); + +static kbase_dd_instr_hwcnt_dump_irq_type *kbase_dd_instr_hwcnt_dump_irq_symbol; +static kbase_dd_instr_hwcnt_dump_complete_type *kbase_dd_instr_hwcnt_dump_complete_symbol; +static mali_dd_hwcnt_init_type *mali_dd_hwcnt_init_symbol; +static mali_dd_hwcnt_clear_type *mali_dd_hwcnt_clear_symbol; + +#else typedef struct kbase_device *kbase_find_device_type(int); -typedef kbase_context *kbase_create_context_type(kbase_device *); -typedef void kbase_destroy_context_type(kbase_context *); +typedef struct kbase_context *kbase_create_context_type(struct kbase_device *); +typedef void kbase_destroy_context_type(struct kbase_context *); #if MALI_DDK_GATOR_API_VERSION == 1 -typedef void *kbase_va_alloc_type(kbase_context *, u32); -typedef void kbase_va_free_type(kbase_context *, void *); +typedef void *kbase_va_alloc_type(struct kbase_context *, u32); +typedef void kbase_va_free_type(struct kbase_context *, void *); #elif MALI_DDK_GATOR_API_VERSION == 2 -typedef void *kbase_va_alloc_type(kbase_context *, u32, kbase_hwc_dma_mapping * handle); -typedef void kbase_va_free_type(kbase_context *, kbase_hwc_dma_mapping * handle); +typedef void *kbase_va_alloc_type(struct kbase_context *, u32, kbase_hwc_dma_mapping * handle); +typedef void kbase_va_free_type(struct kbase_context *, kbase_hwc_dma_mapping * handle); #endif -typedef mali_error kbase_instr_hwcnt_enable_type(kbase_context *, kbase_uk_hwcnt_setup *); -typedef mali_error kbase_instr_hwcnt_disable_type(kbase_context *); -typedef mali_error kbase_instr_hwcnt_clear_type(kbase_context *); -typedef mali_error kbase_instr_hwcnt_dump_irq_type(kbase_context *); -typedef mali_bool kbase_instr_hwcnt_dump_complete_type(kbase_context *, mali_bool *); +typedef mali_error kbase_instr_hwcnt_enable_type(struct kbase_context *, struct kbase_uk_hwcnt_setup *); +typedef mali_error kbase_instr_hwcnt_disable_type(struct kbase_context *); +typedef mali_error kbase_instr_hwcnt_clear_type(struct kbase_context *); +typedef mali_error kbase_instr_hwcnt_dump_irq_type(struct kbase_context *); +typedef mali_bool kbase_instr_hwcnt_dump_complete_type(struct kbase_context *, mali_bool *); static kbase_find_device_type *kbase_find_device_symbol; static kbase_create_context_type *kbase_create_context_symbol; @@ -70,6 +85,7 @@ static kbase_instr_hwcnt_dump_complete_type *kbase_instr_hwcnt_dump_complete_sym static kbase_instr_hwcnt_disable_type *kbase_instr_hwcnt_disable_symbol; static kbase_va_free_type *kbase_va_free_symbol; static kbase_destroy_context_type *kbase_destroy_context_symbol; +#endif static long shader_present_low = 0; @@ -99,6 +115,8 @@ enum { MMU_BLOCK }; +static const char mali_name[] = "Mali-T6xx"; + /* Counters for Mali-T6xx: * * - HW counters, 4 blocks @@ -381,6 +399,14 @@ static const char *const hardware_counter_names[] = { #define GET_HW_BLOCK(c) (((c) >> 6) & 0x3) #define GET_COUNTER_OFFSET(c) ((c) & 0x3f) +#if MALI_DDK_GATOR_API_VERSION == 3 +/* Opaque handles for kbase_context and kbase_hwc_dma_mapping */ +static struct mali_dd_hwcnt_handles *handles; + +/* Information about hardware counters */ +static struct mali_dd_hwcnt_info *in_out_info; + +#else /* Memory to dump hardware counters into */ static void *kernel_dump_buffer; @@ -390,14 +416,9 @@ kbase_hwc_dma_mapping kernel_dump_buffer_handle; #endif /* kbase context and device */ -static kbase_context *kbcontext = NULL; +static struct kbase_context *kbcontext = NULL; static struct kbase_device *kbdevice = NULL; - -/* - * The following function has no external prototype in older DDK revisions. When the DDK - * is updated then this should be removed. - */ -struct kbase_device *kbase_find_device(int minor); +#endif static volatile bool kbase_device_busy = false; static unsigned int num_hardware_counters_enabled; @@ -412,6 +433,13 @@ static mali_counter counters[NUMBER_OF_HARDWARE_COUNTERS]; */ static unsigned long counter_dump[NUMBER_OF_HARDWARE_COUNTERS * 2]; +extern mali_counter mali_activity[3]; +static const char* const mali_activity_names[] = { + "fragment", + "vertex", + "opencl", +}; + #define SYMBOL_GET(FUNCTION, ERROR_COUNT) \ if(FUNCTION ## _symbol) \ { \ @@ -431,8 +459,8 @@ static unsigned long counter_dump[NUMBER_OF_HARDWARE_COUNTERS * 2]; #define SYMBOL_CLEANUP(FUNCTION) \ if(FUNCTION ## _symbol) \ { \ - symbol_put(FUNCTION); \ - FUNCTION ## _symbol = NULL; \ + symbol_put(FUNCTION); \ + FUNCTION ## _symbol = NULL; \ } /** @@ -442,6 +470,12 @@ static unsigned long counter_dump[NUMBER_OF_HARDWARE_COUNTERS * 2]; static int init_symbols(void) { int error_count = 0; +#if MALI_DDK_GATOR_API_VERSION == 3 + SYMBOL_GET(kbase_dd_instr_hwcnt_dump_irq, error_count); + SYMBOL_GET(kbase_dd_instr_hwcnt_dump_complete, error_count); + SYMBOL_GET(mali_dd_hwcnt_init, error_count); + SYMBOL_GET(mali_dd_hwcnt_clear, error_count); +#else SYMBOL_GET(kbase_find_device, error_count); SYMBOL_GET(kbase_create_context, error_count); SYMBOL_GET(kbase_va_alloc, error_count); @@ -452,6 +486,7 @@ static int init_symbols(void) SYMBOL_GET(kbase_instr_hwcnt_disable, error_count); SYMBOL_GET(kbase_va_free, error_count); SYMBOL_GET(kbase_destroy_context, error_count); +#endif return error_count; } @@ -461,6 +496,12 @@ static int init_symbols(void) */ static void clean_symbols(void) { +#if MALI_DDK_GATOR_API_VERSION == 3 + SYMBOL_CLEANUP(kbase_dd_instr_hwcnt_dump_irq); + SYMBOL_CLEANUP(kbase_dd_instr_hwcnt_dump_complete); + SYMBOL_CLEANUP(mali_dd_hwcnt_init); + SYMBOL_CLEANUP(mali_dd_hwcnt_clear); +#else SYMBOL_CLEANUP(kbase_find_device); SYMBOL_CLEANUP(kbase_create_context); SYMBOL_CLEANUP(kbase_va_alloc); @@ -471,6 +512,7 @@ static void clean_symbols(void) SYMBOL_CLEANUP(kbase_instr_hwcnt_disable); SYMBOL_CLEANUP(kbase_va_free); SYMBOL_CLEANUP(kbase_destroy_context); +#endif } /** @@ -502,11 +544,13 @@ static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time static int start(void) { - kbase_uk_hwcnt_setup setup; - mali_error err; - int cnt; - u16 bitmask[] = { 0, 0, 0, 0 }; +#if MALI_DDK_GATOR_API_VERSION < 3 + struct kbase_uk_hwcnt_setup setup; unsigned long long shadersPresent = 0; + u16 bitmask[] = { 0, 0, 0, 0 }; + mali_error err; +#endif + int cnt; /* Setup HW counters */ num_hardware_counters_enabled = 0; @@ -515,18 +559,52 @@ static int start(void) pr_debug("Unexpected number of hardware counters defined: expecting 256, got %d\n", NUMBER_OF_HARDWARE_COUNTERS); } +#if MALI_DDK_GATOR_API_VERSION == 3 + /* Declare and initialise mali_dd_hwcnt_info structure */ + in_out_info = kmalloc(sizeof(struct mali_dd_hwcnt_info), GFP_KERNEL); + for (cnt = 0; cnt < 4; cnt++){ + in_out_info->bitmask[cnt] = 0; + } +#endif /* Calculate enable bitmasks based on counters_enabled array */ for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) { const mali_counter *counter = &counters[cnt]; if (counter->enabled) { int block = GET_HW_BLOCK(cnt); int enable_bit = GET_COUNTER_OFFSET(cnt) / 4; +#if MALI_DDK_GATOR_API_VERSION == 3 + in_out_info->bitmask[block] |= (1 << enable_bit); +#else bitmask[block] |= (1 << enable_bit); +#endif pr_debug("gator: Mali-T6xx: hardware counter %s selected [%d]\n", hardware_counter_names[cnt], cnt); num_hardware_counters_enabled++; } } +#if MALI_DDK_GATOR_API_VERSION == 3 + /* Create a kbase context for HW counters */ + if (num_hardware_counters_enabled > 0) { + if (init_symbols() > 0) { + clean_symbols(); + /* No Mali driver code entrypoints found - not a fault. */ + return 0; + } + + handles = mali_dd_hwcnt_init_symbol(in_out_info); + + if(handles == NULL) { + goto out; + } + + /* See if we can get the number of shader cores */ + shader_present_low = (unsigned long)in_out_info->shader_present_bitmap; + + kbase_device_busy = false; + } + + return 0; +#else /* Create a kbase context for HW counters */ if (num_hardware_counters_enabled > 0) { if (init_symbols() > 0) { @@ -606,6 +684,7 @@ static int start(void) destroy_context: kbase_destroy_context_symbol(kbcontext); +#endif out: clean_symbols(); @@ -615,7 +694,11 @@ static int start(void) static void stop(void) { unsigned int cnt; - kbase_context *temp_kbcontext; +#if MALI_DDK_GATOR_API_VERSION == 3 + struct mali_dd_hwcnt_handles *temp_hand; +#else + struct kbase_context *temp_kbcontext; +#endif pr_debug("gator: Mali-T6xx: stop\n"); @@ -625,6 +708,20 @@ static void stop(void) } /* Destroy the context for HW counters */ +#if MALI_DDK_GATOR_API_VERSION == 3 + if (num_hardware_counters_enabled > 0 && handles != NULL) { + /* + * Set the global variable to NULL before destroying it, because + * other function will check this before using it. + */ + temp_hand = handles; + handles = NULL; + + mali_dd_hwcnt_clear_symbol(in_out_info, temp_hand); + + kfree(in_out_info); + +#else if (num_hardware_counters_enabled > 0 && kbcontext != NULL) { /* * Set the global variable to NULL before destroying it, because @@ -642,6 +739,7 @@ static void stop(void) #endif kbase_destroy_context_symbol(temp_kbcontext); +#endif pr_debug("gator: Mali-T6xx: hardware counters stopped\n"); @@ -654,7 +752,7 @@ static int read(int **buffer) int cnt; int len = 0; u32 value = 0; - mali_bool success; + uint32_t success; struct timespec current_time; static u32 prev_time_s = 0; @@ -686,12 +784,21 @@ static int read(int **buffer) 0x500 /* VITHAR_MEMORY_SYSTEM, Block 3 */ }; +#if MALI_DDK_GATOR_API_VERSION == 3 + if (!handles) { + return -1; + } + + /* Mali symbols can be called safely since a kbcontext is valid */ + if (kbase_dd_instr_hwcnt_dump_complete_symbol(handles, &success) == MALI_TRUE) { +#else if (!kbcontext) { return -1; } /* Mali symbols can be called safely since a kbcontext is valid */ if (kbase_instr_hwcnt_dump_complete_symbol(kbcontext, &success) == MALI_TRUE) { +#endif kbase_device_busy = false; if (success == MALI_TRUE) { @@ -702,7 +809,11 @@ static int read(int **buffer) const int block = GET_HW_BLOCK(cnt); const int counter_offset = GET_COUNTER_OFFSET(cnt); +#if MALI_DDK_GATOR_API_VERSION == 3 + const char* block_base_address = (char*)in_out_info->kernel_dump_buffer + vithar_blocks[block]; +#else const char* block_base_address = (char*)kernel_dump_buffer + vithar_blocks[block]; +#endif /* If counter belongs to shader block need to take into account all cores */ if (block == SHADER_BLOCK) { @@ -741,7 +852,11 @@ static int read(int **buffer) if (!kbase_device_busy) { kbase_device_busy = true; +#if MALI_DDK_GATOR_API_VERSION == 3 + kbase_dd_instr_hwcnt_dump_irq_symbol(handles); +#else kbase_instr_hwcnt_dump_irq_symbol(kbcontext); +#endif } } @@ -760,7 +875,12 @@ static int create_files(struct super_block *sb, struct dentry *root) * Create the filesystem for all events */ int counter_index = 0; - const char *mali_name = gator_mali_get_mali_name(); + + for (event = 0; event < ARRAY_SIZE(mali_activity); event++) { + if (gator_mali_create_file_system(mali_name, mali_activity_names[event], sb, root, &mali_activity[event], NULL) != 0) { + return -1; + } + } for (event = 0; event < NUMBER_OF_HARDWARE_COUNTERS; event++) { if (gator_mali_create_file_system(mali_name, hardware_counter_names[counter_index], sb, root, &counters[event], NULL) != 0) @@ -786,6 +906,7 @@ int gator_events_mali_t6xx_hw_init(void) test_all_is_read_scheduled(); #endif + gator_mali_initialise_counters(mali_activity, ARRAY_SIZE(mali_activity)); gator_mali_initialise_counters(counters, NUMBER_OF_HARDWARE_COUNTERS); return gator_events_install(&gator_events_mali_t6xx_interface); diff --git a/drivers/gator/gator_events_mmapped.c b/drivers/gator/gator_events_mmapped.c index 3b248ec24e6e..5bc01c42c3a2 100644 --- a/drivers/gator/gator_events_mmapped.c +++ b/drivers/gator/gator_events_mmapped.c @@ -8,21 +8,25 @@ * published by the Free Software Foundation. * * Similar entries to those below must be present in the events.xml file. - * To add them to the events.xml, create an events-mmap.xml with the + * To add them to the events.xml, create an events-mmap.xml with the * following contents and rebuild gatord: * - * - * - * - * - * + * + * + * + * * * - * When adding custom events, be sure do the following + * When adding custom events, be sure to do the following: * - add any needed .c files to the gator driver Makefile * - call gator_events_install in the events init function * - add the init function to GATOR_EVENTS_LIST in gator_main.c * - add a new events-*.xml file to the gator daemon and rebuild + * + * Troubleshooting: + * - verify the new events are part of events.xml, which is created when building the daemon + * - verify the new events exist at /dev/gator/events/ once gatord is launched + * - verify the counter name in the XML matches the name at /dev/gator/events */ #include @@ -37,7 +41,6 @@ static int mmapped_global_enabled; static struct { unsigned long enabled; - unsigned long event; unsigned long key; } mmapped_counters[MMAPPED_COUNTERS_NUM]; @@ -47,7 +50,7 @@ static s64 prev_time; /* Adds mmapped_cntX directories and enabled, event, and key files to /dev/gator/events */ static int gator_events_mmapped_create_files(struct super_block *sb, - struct dentry *root) + struct dentry *root) { int i; @@ -61,8 +64,6 @@ static int gator_events_mmapped_create_files(struct super_block *sb, return -1; gatorfs_create_ulong(sb, dir, "enabled", &mmapped_counters[i].enabled); - gatorfs_create_ulong(sb, dir, "event", - &mmapped_counters[i].event); gatorfs_create_ro_ulong(sb, dir, "key", &mmapped_counters[i].key); } @@ -177,8 +178,7 @@ static int gator_events_mmapped_read(int **buffer) if (mmapped_counters[i].enabled) { mmapped_buffer[len++] = mmapped_counters[i].key; mmapped_buffer[len++] = - mmapped_simulate(mmapped_counters[i].event, - delta_in_us); + mmapped_simulate(i, delta_in_us); } } diff --git a/drivers/gator/gator_events_perf_pmu.c b/drivers/gator/gator_events_perf_pmu.c index 8b2d67a058b3..06bbad5b10c3 100644 --- a/drivers/gator/gator_events_perf_pmu.c +++ b/drivers/gator/gator_events_perf_pmu.c @@ -470,10 +470,10 @@ static void gator_events_perf_pmu_cci_init(const int type) switch (probe_cci_revision()) { case 0: - cci_name = "cci-400"; + cci_name = "CCI_400"; break; case 1: - cci_name = "cci-400-r1"; + cci_name = "CCI_400-r1"; break; default: pr_debug("gator: unrecognized cci-400 revision\n"); @@ -549,7 +549,7 @@ int gator_events_perf_pmu_init(void) } if (pe->pmu != NULL && type == pe->pmu->type) { - if (strcmp("CCI", pe->pmu->name) == 0 || strcmp("CCI_400", pe->pmu->name) == 0) { + if (strcmp("CCI", pe->pmu->name) == 0 || strcmp("CCI_400", pe->pmu->name) == 0 || strcmp("CCI_400-r1", pe->pmu->name) == 0) { gator_events_perf_pmu_cci_init(type); } else if ((gator_cpu = gator_find_cpu_by_pmu_name(pe->pmu->name)) != NULL) { found_cpu = true; diff --git a/drivers/gator/gator_events_scorpion.c b/drivers/gator/gator_events_scorpion.c index 8ca251af0e26..2e5be8d50e9d 100644 --- a/drivers/gator/gator_events_scorpion.c +++ b/drivers/gator/gator_events_scorpion.c @@ -26,9 +26,9 @@ static int pmnc_counters; // ccnt reg #define CCNT_REG (1 << 31) -#define CCNT 0 +#define CCNT 0 #define CNT0 1 -#define CNTMAX (4+1) +#define CNTMAX (4+1) static unsigned long pmnc_enabled[CNTMAX]; static unsigned long pmnc_event[CNTMAX]; diff --git a/drivers/gator/gator_events_threads.c b/drivers/gator/gator_events_threads.c new file mode 100644 index 000000000000..9de85862fe6c --- /dev/null +++ b/drivers/gator/gator_events_threads.c @@ -0,0 +1,115 @@ +/* + * Sample activity provider + * + * Copyright (C) ARM Limited 2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * See gator_events_mmapped.c for additional directions and + * troubleshooting. + * + * For this sample to work these entries must be present in the + * events.xml file. So create an events-threads.xml in the gator + * daemon source directory with the following contents and rebuild + * gatord: + * + * + * + * + */ + +#include + +#include "gator.h" + +static ulong threads_enabled; +static ulong threads_key; +static ulong threads_cores; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) +#else +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next)) +#endif +{ + int cpu = get_physical_cpu(); + int pid = next->pid; + if (pid == 0) { + // idle + gator_marshal_activity_switch(cpu, threads_key, 0, 0); + } else if (pid & 1) { + // odd + gator_marshal_activity_switch(cpu, threads_key, 1, pid); + } else { + // even + //gator_marshal_activity_switch(cpu, threads_key, 2, current->pid); + // Multiple activities are not yet supported so emit idle + gator_marshal_activity_switch(cpu, threads_key, 0, 0); + } +} + +// Adds Linux_threads directory and enabled, key, and cores files to /dev/gator/events +static int gator_events_threads_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + dir = gatorfs_mkdir(sb, root, "Linux_threads"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &threads_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &threads_key); + // Number of cores associated with this activity + gatorfs_create_ro_ulong(sb, dir, "cores", &threads_cores); + + return 0; +} + +static int gator_events_threads_start(void) +{ + int cpu; + + if (threads_enabled) { + preempt_disable(); + for (cpu = 0; cpu < nr_cpu_ids; ++cpu) { + gator_marshal_activity_switch(cpu, threads_key, 0, 0); + } + preempt_enable(); + + if (GATOR_REGISTER_TRACE(sched_switch)) { + goto fail_sched_switch; + } + } + + return 0; + +fail_sched_switch: + return -1; +} + +static void gator_events_threads_stop(void) +{ + if (threads_enabled) { + GATOR_UNREGISTER_TRACE(sched_switch); + } + + threads_enabled = 0; +} + +static struct gator_interface gator_events_threads_interface = { + .create_files = gator_events_threads_create_files, + .start = gator_events_threads_start, + .stop = gator_events_threads_stop, +}; + +// Must not be static. Ensure that this init function is added to GATOR_EVENTS_LIST in gator_main.c +int __init gator_events_threads_init(void) +{ + threads_enabled = 0; + threads_key = gator_events_get_key(); + threads_cores = nr_cpu_ids; + + return gator_events_install(&gator_events_threads_interface); +} diff --git a/drivers/gator/gator_iks.c b/drivers/gator/gator_iks.c index e90dfcce9381..9180b874457a 100644 --- a/drivers/gator/gator_iks.c +++ b/drivers/gator/gator_iks.c @@ -150,7 +150,7 @@ static void gator_send_iks_core_names(void) preempt_disable(); for (cpu = 0; cpu < nr_cpu_ids; ++cpu) { if (mpidr_cpus[cpu] != NULL) { - gator_send_core_name(cpu, mpidr_cpus[cpu]->cpuid, mpidr_cpus[cpu]); + gator_send_core_name(cpu, mpidr_cpus[cpu]->cpuid); } } preempt_enable(); diff --git a/drivers/gator/gator_main.c b/drivers/gator/gator_main.c index e67f7c5cc61d..0d867f22364f 100644 --- a/drivers/gator/gator_main.c +++ b/drivers/gator/gator_main.c @@ -8,7 +8,7 @@ */ // This version must match the gator daemon version -#define PROTOCOL_VERSION 18 +#define PROTOCOL_VERSION 19 static unsigned long gator_protocol_version = PROTOCOL_VERSION; #include @@ -71,8 +71,8 @@ static unsigned long gator_protocol_version = PROTOCOL_VERSION; #define BLOCK_COUNTER_BUFFER_SIZE (128*1024) #define ANNOTATE_BUFFER_SIZE (128*1024) // annotate counters have the core as part of the data and the core value in the frame header may be discarded #define SCHED_TRACE_BUFFER_SIZE (128*1024) -#define GPU_TRACE_BUFFER_SIZE (64*1024) // gpu trace counters have the core as part of the data and the core value in the frame header may be discarded #define IDLE_BUFFER_SIZE (32*1024) // idle counters have the core as part of the data and the core value in the frame header may be discarded +#define ACTIVITY_BUFFER_SIZE (128*1024) #define NO_COOKIE 0U #define UNRESOLVED_COOKIE ~0U @@ -84,8 +84,8 @@ static unsigned long gator_protocol_version = PROTOCOL_VERSION; #define FRAME_BLOCK_COUNTER 5 #define FRAME_ANNOTATE 6 #define FRAME_SCHED_TRACE 7 -#define FRAME_GPU_TRACE 8 #define FRAME_IDLE 9 +#define FRAME_ACTIVITY 13 #define MESSAGE_END_BACKTRACE 1 @@ -94,14 +94,9 @@ static unsigned long gator_protocol_version = PROTOCOL_VERSION; #define MESSAGE_THREAD_NAME 2 #define MESSAGE_LINK 4 -// GPU Trace Frame Messages -#define MESSAGE_GPU_START 1 -#define MESSAGE_GPU_STOP 2 - // Scheduler Trace Frame Messages #define MESSAGE_SCHED_SWITCH 1 #define MESSAGE_SCHED_EXIT 2 -#define MESSAGE_SCHED_START 3 // Idle Frame Messages #define MESSAGE_IDLE_ENTER 1 @@ -111,6 +106,10 @@ static unsigned long gator_protocol_version = PROTOCOL_VERSION; #define MESSAGE_SUMMARY 1 #define MESSAGE_CORE_NAME 3 +// Activity Frame Messages +#define MESSAGE_SWITCH 2 +#define MESSAGE_EXIT 3 + #define MAXSIZE_PACK32 5 #define MAXSIZE_PACK64 10 @@ -132,8 +131,8 @@ enum { BLOCK_COUNTER_BUF, ANNOTATE_BUF, SCHED_TRACE_BUF, - GPU_TRACE_BUF, IDLE_BUF, + ACTIVITY_BUF, NUM_GATOR_BUFS }; @@ -175,6 +174,7 @@ static DEFINE_PER_CPU(u64, last_timestamp); static bool printed_monotonic_warning; +static u32 gator_cpuids[NR_CPUS]; static bool sent_core_name[NR_CPUS]; static DEFINE_PER_CPU(bool, in_scheduler_context); @@ -226,6 +226,7 @@ static DEFINE_PER_CPU(u64, gator_buffer_commit_time); GATOR_EVENT(gator_events_perf_pmu_init) \ GATOR_EVENT(gator_events_sched_init) \ GATOR_EVENT(gator_events_scorpion_init) \ + GATOR_EVENT(gator_events_threads_init) \ #define GATOR_EVENT(EVENT_INIT) __weak int EVENT_INIT(void); GATOR_EVENTS_LIST @@ -570,25 +571,37 @@ static void gator_timer_stop(void) } } -#if defined(__arm__) || defined(__aarch64__) -static void gator_send_core_name(int cpu, const u32 cpuid, const struct gator_cpu *const gator_cpu) +static void gator_send_core_name(const int cpu, const u32 cpuid) { - const char *core_name = NULL; - char core_name_buf[32]; +#if defined(__arm__) || defined(__aarch64__) + if (!sent_core_name[cpu] || (cpuid != gator_cpuids[cpu])) { + const struct gator_cpu *const gator_cpu = gator_find_cpu_by_cpuid(cpuid); + const char *core_name = NULL; + char core_name_buf[32]; - if (!sent_core_name[cpu]) { + // Save off this cpuid + gator_cpuids[cpu] = cpuid; if (gator_cpu != NULL) { core_name = gator_cpu->core_name; } else { - snprintf(core_name_buf, sizeof(core_name_buf), "Unknown (0x%.3x)", cpuid); + if (cpuid == -1) { + snprintf(core_name_buf, sizeof(core_name_buf), "Unknown"); + } else { + snprintf(core_name_buf, sizeof(core_name_buf), "Unknown (0x%.3x)", cpuid); + } core_name = core_name_buf; } marshal_core_name(cpu, cpuid, core_name); sent_core_name[cpu] = true; } -} #endif +} + +static void gator_read_cpuid(void * arg) +{ + gator_cpuids[get_physical_cpu()] = gator_cpuid(); +} // This function runs in interrupt context and on the appropriate core static void gator_timer_online(void *migrate) @@ -598,6 +611,9 @@ static void gator_timer_online(void *migrate) int *buffer; u64 time; + // Send what is currently running on this core + marshal_sched_trace_switch(current->pid, 0); + gator_trace_power_online(); // online any events and output counters @@ -617,12 +633,7 @@ static void gator_timer_online(void *migrate) gator_hrtimer_online(); } -#if defined(__arm__) || defined(__aarch64__) - if (!sent_core_name[cpu]) { - const u32 cpuid = gator_cpuid(); - gator_send_core_name(cpu, cpuid, gator_find_cpu_by_cpuid(cpuid)); - } -#endif + gator_send_core_name(cpu, gator_cpuid()); } // This function runs in interrupt context and may be running on a core other than core 'cpu' @@ -658,6 +669,13 @@ static int gator_timer_start(unsigned long sample_rate) if (gator_hrtimer_init(sample_rate, gator_timer_interrupt) == -1) return -1; + // Send off the previously saved cpuids + for_each_present_cpu(cpu) { + preempt_disable(); + gator_send_core_name(cpu, gator_cpuids[cpu]); + preempt_enable(); + } + gator_send_iks_core_names(); for_each_online_cpu(cpu) { gator_timer_online_dispatch(lcpu_to_pcpu(cpu), false); @@ -1009,12 +1027,12 @@ static int gator_op_setup(void) gator_buffer_size[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE; gator_buffer_mask[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE - 1; - gator_buffer_size[GPU_TRACE_BUF] = GPU_TRACE_BUFFER_SIZE; - gator_buffer_mask[GPU_TRACE_BUF] = GPU_TRACE_BUFFER_SIZE - 1; - gator_buffer_size[IDLE_BUF] = IDLE_BUFFER_SIZE; gator_buffer_mask[IDLE_BUF] = IDLE_BUFFER_SIZE - 1; + gator_buffer_size[ACTIVITY_BUF] = ACTIVITY_BUFFER_SIZE; + gator_buffer_mask[ACTIVITY_BUF] = ACTIVITY_BUFFER_SIZE - 1; + // Initialize percpu per buffer variables for (i = 0; i < NUM_GATOR_BUFS; i++) { // Verify buffers are a power of 2 @@ -1349,8 +1367,62 @@ static void gator_op_create_files(struct super_block *sb, struct dentry *root) /****************************************************************************** * Module ******************************************************************************/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + +#define GATOR_TRACEPOINTS \ + GATOR_HANDLE_TRACEPOINT(block_rq_complete); \ + GATOR_HANDLE_TRACEPOINT(cpu_frequency); \ + GATOR_HANDLE_TRACEPOINT(cpu_idle); \ + GATOR_HANDLE_TRACEPOINT(cpu_migrate_begin); \ + GATOR_HANDLE_TRACEPOINT(cpu_migrate_current); \ + GATOR_HANDLE_TRACEPOINT(cpu_migrate_finish); \ + GATOR_HANDLE_TRACEPOINT(irq_handler_exit); \ + GATOR_HANDLE_TRACEPOINT(mali_hw_counter); \ + GATOR_HANDLE_TRACEPOINT(mali_job_slots_event); \ + GATOR_HANDLE_TRACEPOINT(mali_mmu_as_in_use); \ + GATOR_HANDLE_TRACEPOINT(mali_mmu_as_released); \ + GATOR_HANDLE_TRACEPOINT(mali_page_fault_insert_pages); \ + GATOR_HANDLE_TRACEPOINT(mali_pm_status); \ + GATOR_HANDLE_TRACEPOINT(mali_sw_counter); \ + GATOR_HANDLE_TRACEPOINT(mali_sw_counters); \ + GATOR_HANDLE_TRACEPOINT(mali_timeline_event); \ + GATOR_HANDLE_TRACEPOINT(mali_total_alloc_pages_change); \ + GATOR_HANDLE_TRACEPOINT(mm_page_alloc); \ + GATOR_HANDLE_TRACEPOINT(mm_page_free); \ + GATOR_HANDLE_TRACEPOINT(mm_page_free_batched); \ + GATOR_HANDLE_TRACEPOINT(sched_process_exec); \ + GATOR_HANDLE_TRACEPOINT(sched_process_fork); \ + GATOR_HANDLE_TRACEPOINT(sched_process_free); \ + GATOR_HANDLE_TRACEPOINT(sched_switch); \ + GATOR_HANDLE_TRACEPOINT(softirq_exit); \ + +#define GATOR_HANDLE_TRACEPOINT(probe_name) \ + struct tracepoint *gator_tracepoint_##probe_name +GATOR_TRACEPOINTS; +#undef GATOR_HANDLE_TRACEPOINT + +static void gator_fct(struct tracepoint *tp, void *priv) +{ +#define GATOR_HANDLE_TRACEPOINT(probe_name) \ + if (strcmp(tp->name, #probe_name) == 0) { \ + gator_tracepoint_##probe_name = tp; \ + return; \ + } +GATOR_TRACEPOINTS; +#undef GATOR_HANDLE_TRACEPOINT +} + +#else + +#define for_each_kernel_tracepoint(fct, priv) + +#endif + static int __init gator_module_init(void) { + for_each_kernel_tracepoint(gator_fct, NULL); + if (gatorfs_register()) { return -1; } @@ -1362,6 +1434,10 @@ static int __init gator_module_init(void) setup_timer(&gator_buffer_wake_up_timer, gator_buffer_wake_up, 0); + // Initialize the list of cpuids + memset(gator_cpuids, -1, sizeof(gator_cpuids)); + on_each_cpu(gator_read_cpuid, NULL, 1); + return 0; } diff --git a/drivers/gator/gator_marshaling.c b/drivers/gator/gator_marshaling.c index fd413ad1331c..97b4ae6f9d4d 100644 --- a/drivers/gator/gator_marshaling.c +++ b/drivers/gator/gator_marshaling.c @@ -231,75 +231,28 @@ static void marshal_event_single(int core, int key, int value) // Check and commit; commit is set to occur once buffer is 3/4 full buffer_check(cpu, COUNTER_BUF, time); } + +static void marshal_event_single64(int core, int key, long long value) +{ + unsigned long flags, cpu; + u64 time; + + local_irq_save(flags); + cpu = get_physical_cpu(); + time = gator_get_time(); + if (buffer_check_space(cpu, COUNTER_BUF, 2 * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int64(cpu, COUNTER_BUF, time); + gator_buffer_write_packed_int(cpu, COUNTER_BUF, core); + gator_buffer_write_packed_int(cpu, COUNTER_BUF, key); + gator_buffer_write_packed_int64(cpu, COUNTER_BUF, value); + } + local_irq_restore(flags); + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, COUNTER_BUF, time); +} #endif -static void marshal_sched_gpu_start(int unit, int core, int tgid, int pid) -{ - unsigned long cpu = get_physical_cpu(), flags; - u64 time; - - if (!per_cpu(gator_buffer, cpu)[GPU_TRACE_BUF]) - return; - - local_irq_save(flags); - time = gator_get_time(); - if (buffer_check_space(cpu, GPU_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, MESSAGE_GPU_START); - gator_buffer_write_packed_int64(cpu, GPU_TRACE_BUF, time); - gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, unit); - gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, core); - gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, tgid); - gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, pid); - } - local_irq_restore(flags); - // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, GPU_TRACE_BUF, time); -} - -static void marshal_sched_gpu_stop(int unit, int core) -{ - unsigned long cpu = get_physical_cpu(), flags; - u64 time; - - if (!per_cpu(gator_buffer, cpu)[GPU_TRACE_BUF]) - return; - - local_irq_save(flags); - time = gator_get_time(); - if (buffer_check_space(cpu, GPU_TRACE_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, MESSAGE_GPU_STOP); - gator_buffer_write_packed_int64(cpu, GPU_TRACE_BUF, time); - gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, unit); - gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, core); - } - local_irq_restore(flags); - // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, GPU_TRACE_BUF, time); -} - -static void marshal_sched_trace_start(int tgid, int pid, int cookie) -{ - unsigned long cpu = get_physical_cpu(), flags; - u64 time; - - if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF]) - return; - - local_irq_save(flags); - time = gator_get_time(); - if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, MESSAGE_SCHED_START); - gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time); - gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, tgid); - gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); - gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie); - } - local_irq_restore(flags); - // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, SCHED_TRACE_BUF, time); -} - -static void marshal_sched_trace_switch(int tgid, int pid, int cookie, int state) +static void marshal_sched_trace_switch(int pid, int state) { unsigned long cpu = get_physical_cpu(), flags; u64 time; @@ -312,9 +265,7 @@ static void marshal_sched_trace_switch(int tgid, int pid, int cookie, int state) if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, MESSAGE_SCHED_SWITCH); gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time); - gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, tgid); gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); - gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie); gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, state); } local_irq_restore(flags); @@ -379,3 +330,33 @@ static void marshal_core_name(const int core, const int cpuid, const char *name) gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time()); } #endif + +static void marshal_activity_switch(int core, int key, int activity, int pid, int state) +{ + unsigned long cpu = get_physical_cpu(), flags; + u64 time; + + if (!per_cpu(gator_buffer, cpu)[ACTIVITY_BUF]) + return; + + local_irq_save(flags); + time = gator_get_time(); + if (buffer_check_space(cpu, ACTIVITY_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, MESSAGE_SWITCH); + gator_buffer_write_packed_int64(cpu, ACTIVITY_BUF, time); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, core); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, key); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, activity); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, pid); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, state); + } + local_irq_restore(flags); + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, ACTIVITY_BUF, time); +} + +void gator_marshal_activity_switch(int core, int key, int activity, int pid) +{ + // state is reserved for cpu use only + marshal_activity_switch(core, key, activity, pid, 0); +} diff --git a/drivers/gator/gator_trace_gpu.c b/drivers/gator/gator_trace_gpu.c index 6332098e5958..a8b9e7d61ece 100644 --- a/drivers/gator/gator_trace_gpu.c +++ b/drivers/gator/gator_trace_gpu.c @@ -23,8 +23,6 @@ #endif #endif -#include "gator_trace_gpu.h" - /* * Taken from MALI_PROFILING_EVENT_TYPE_* items in Mali DDK. */ @@ -37,7 +35,6 @@ /* Note whether tracepoints have been registered */ static int mali_timeline_trace_registered; static int mali_job_slots_trace_registered; -static int gpu_trace_registered; enum { GPU_UNIT_NONE = 0, @@ -47,19 +44,19 @@ enum { NUMBER_OF_GPU_UNITS }; -#define MALI_4xx (0x0b07) -#define MALI_T6xx (0x0056) +#if defined(MALI_SUPPORT) -struct mali_gpu_job { +struct mali_activity { + int core; + int key; int count; - int last_tgid; + int last_activity; int last_pid; - int last_job_id; }; #define NUMBER_OF_GPU_CORES 16 -static struct mali_gpu_job mali_gpu_jobs[NUMBER_OF_GPU_UNITS][NUMBER_OF_GPU_CORES]; -static DEFINE_SPINLOCK(mali_gpu_jobs_lock); +static struct mali_activity mali_activities[NUMBER_OF_GPU_UNITS*NUMBER_OF_GPU_CORES]; +static DEFINE_SPINLOCK(mali_activities_lock); /* Only one event should be running on a unit and core at a time (ie, a start * event can only be followed by a stop and vice versa), but because the kernel @@ -67,53 +64,97 @@ static DEFINE_SPINLOCK(mali_gpu_jobs_lock); * start1, start2, stop1, stop2. Change it back into start1, stop1, start2, * stop2 by queueing up start2 and releasing it when stop1 is received. */ -static void mali_gpu_enqueue(int unit, int core, int tgid, int pid, int job_id) + +static int mali_activity_index(int core, int key) { + int i; + + for (i = 0; i < ARRAY_SIZE(mali_activities); ++i) { + if ((mali_activities[i].core == core) && (mali_activities[i].key == key)) { + break; + } + if ((mali_activities[i].core == 0) && (mali_activities[i].key == 0)) { + mali_activities[i].core = core; + mali_activities[i].key = key; + break; + } + } + BUG_ON(i >= ARRAY_SIZE(mali_activities)); + + return i; +} + +static void mali_activity_enqueue(int core, int key, int activity, int pid) +{ + int i; int count; - spin_lock(&mali_gpu_jobs_lock); - count = mali_gpu_jobs[unit][core].count; + spin_lock(&mali_activities_lock); + i = mali_activity_index(core, key); + + count = mali_activities[i].count; BUG_ON(count < 0); - ++mali_gpu_jobs[unit][core].count; + ++mali_activities[i].count; if (count) { - mali_gpu_jobs[unit][core].last_tgid = tgid; - mali_gpu_jobs[unit][core].last_pid = pid; - mali_gpu_jobs[unit][core].last_job_id = job_id; + mali_activities[i].last_activity = activity; + mali_activities[i].last_pid = pid; } - spin_unlock(&mali_gpu_jobs_lock); + spin_unlock(&mali_activities_lock); if (!count) { - marshal_sched_gpu_start(unit, core, tgid, pid/*, job_id*/); + gator_marshal_activity_switch(core, key, activity, pid); } } -static void mali_gpu_stop(int unit, int core) +static void mali_activity_stop(int core, int key) { + int i; int count; - int last_tgid = 0; + int last_activity = 0; int last_pid = 0; - //int last_job_id = 0; - spin_lock(&mali_gpu_jobs_lock); - if (mali_gpu_jobs[unit][core].count == 0) { - spin_unlock(&mali_gpu_jobs_lock); + spin_lock(&mali_activities_lock); + i = mali_activity_index(core, key); + + if (mali_activities[i].count == 0) { + spin_unlock(&mali_activities_lock); return; } - --mali_gpu_jobs[unit][core].count; - count = mali_gpu_jobs[unit][core].count; + --mali_activities[i].count; + count = mali_activities[i].count; if (count) { - last_tgid = mali_gpu_jobs[unit][core].last_tgid; - last_pid = mali_gpu_jobs[unit][core].last_pid; - //last_job_id = mali_gpu_jobs[unit][core].last_job_id; + last_activity = mali_activities[i].last_activity; + last_pid = mali_activities[i].last_pid; } - spin_unlock(&mali_gpu_jobs_lock); + spin_unlock(&mali_activities_lock); - marshal_sched_gpu_stop(unit, core); + gator_marshal_activity_switch(core, key, 0, 0); if (count) { - marshal_sched_gpu_start(unit, core, last_tgid, last_pid/*, last_job_id*/); + gator_marshal_activity_switch(core, key, last_activity, last_pid); } } +void mali_activity_clear(mali_counter mali_activity[], size_t mali_activity_size) +{ + int activity; + int cores; + int core; + + for (activity = 0; activity < mali_activity_size; ++activity) { + cores = mali_activity[activity].cores; + if (cores < 0) { + cores = 1; + } + for (core = 0; core < cores; ++core) { + if (mali_activity[activity].enabled) { + gator_marshal_activity_switch(core, mali_activity[activity].key, 0, 0); + } + } + } +} + +#endif + #if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) #include "gator_events_mali_4xx.h" @@ -142,6 +183,8 @@ enum { EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE = 1, }; +mali_counter mali_activity[2]; + GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4)) { unsigned int component, state; @@ -154,18 +197,26 @@ GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned case EVENT_TYPE_START: if (component == EVENT_CHANNEL_VP0) { /* tgid = d0; pid = d1; */ - mali_gpu_enqueue(GPU_UNIT_VP, 0, d0, d1, 0); + if (mali_activity[1].enabled) { + mali_activity_enqueue(0, mali_activity[1].key, 1, d1); + } } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) { /* tgid = d0; pid = d1; */ - mali_gpu_enqueue(GPU_UNIT_FP, component - EVENT_CHANNEL_FP0, d0, d1, 0); + if (mali_activity[0].enabled) { + mali_activity_enqueue(component - EVENT_CHANNEL_FP0, mali_activity[0].key, 1, d1); + } } break; case EVENT_TYPE_STOP: if (component == EVENT_CHANNEL_VP0) { - mali_gpu_stop(GPU_UNIT_VP, 0); + if (mali_activity[1].enabled) { + mali_activity_stop(0, mali_activity[1].key); + } } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) { - mali_gpu_stop(GPU_UNIT_FP, component - EVENT_CHANNEL_FP0); + if (mali_activity[0].enabled) { + mali_activity_stop(component - EVENT_CHANNEL_FP0, mali_activity[0].key); + } } break; @@ -186,6 +237,9 @@ GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned #endif #if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) + +mali_counter mali_activity[3]; + #if defined(MALI_JOB_SLOTS_EVENT_CHANGED) GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigned int tgid, unsigned int pid, unsigned char job_id)) #else @@ -217,31 +271,21 @@ GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigne if (unit != GPU_UNIT_NONE) { switch (state) { case EVENT_TYPE_START: - mali_gpu_enqueue(unit, 0, tgid, (pid != 0 ? pid : tgid), job_id); + if (mali_activity[component].enabled) { + mali_activity_enqueue(0, mali_activity[component].key, 1, (pid != 0 ? pid : tgid)); + } break; case EVENT_TYPE_STOP: - mali_gpu_stop(unit, 0); + default: // Some jobs can be soft-stopped, so ensure that this terminates the activity trace. + if (mali_activity[component].enabled) { + mali_activity_stop(0, mali_activity[component].key); + } break; - default: - /* - * Some jobs can be soft-stopped, so ensure that this terminates the activity trace. - */ - mali_gpu_stop(unit, 0); } } } #endif -GATOR_DEFINE_PROBE(gpu_activity_start, TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p)) -{ - mali_gpu_enqueue(gpu_unit, gpu_core, (int)p->tgid, (int)p->pid, 0); -} - -GATOR_DEFINE_PROBE(gpu_activity_stop, TP_PROTO(int gpu_unit, int gpu_core)) -{ - mali_gpu_stop(gpu_unit, gpu_core); -} - static int gator_trace_gpu_start(void) { /* @@ -249,32 +293,25 @@ static int gator_trace_gpu_start(void) * Absence of gpu trace points is not an error */ - memset(&mali_gpu_jobs, 0, sizeof(mali_gpu_jobs)); - gpu_trace_registered = mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; +#if defined(MALI_SUPPORT) + memset(&mali_activities, 0, sizeof(mali_activities)); +#endif + mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; #if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) + mali_activity_clear(mali_activity, ARRAY_SIZE(mali_activity)); if (!GATOR_REGISTER_TRACE(mali_timeline_event)) { mali_timeline_trace_registered = 1; } #endif #if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) + mali_activity_clear(mali_activity, ARRAY_SIZE(mali_activity)); if (!GATOR_REGISTER_TRACE(mali_job_slots_event)) { mali_job_slots_trace_registered = 1; } #endif - if (!mali_timeline_trace_registered) { - if (GATOR_REGISTER_TRACE(gpu_activity_start)) { - return 0; - } - if (GATOR_REGISTER_TRACE(gpu_activity_stop)) { - GATOR_UNREGISTER_TRACE(gpu_activity_start); - return 0; - } - gpu_trace_registered = 1; - } - return 0; } @@ -292,10 +329,5 @@ static void gator_trace_gpu_stop(void) } #endif - if (gpu_trace_registered) { - GATOR_UNREGISTER_TRACE(gpu_activity_stop); - GATOR_UNREGISTER_TRACE(gpu_activity_start); - } - - gpu_trace_registered = mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; + mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; } diff --git a/drivers/gator/gator_trace_gpu.h b/drivers/gator/gator_trace_gpu.h deleted file mode 100644 index 5113d459e24c..000000000000 --- a/drivers/gator/gator_trace_gpu.h +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#undef TRACE_GPU -#define TRACE_GPU gpu - -#if !defined(_TRACE_GPU_H) -#define _TRACE_GPU_H - -#include - -/* - * UNIT - the GPU processor type - * 1 = Vertex Processor - * 2 = Fragment Processor - * - * CORE - the GPU processor core number - * this is not the CPU core number - */ - -/* - * Tracepoint for calling GPU unit start activity on core - */ -TRACE_EVENT(gpu_activity_start, - - TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p), - - TP_ARGS(gpu_unit, gpu_core, p), - - TP_STRUCT__entry( - __field(int, gpu_unit) - __field(int, gpu_core) - __array(char, comm, TASK_COMM_LEN) - __field(pid_t, pid) - ), - - TP_fast_assign( - __entry->gpu_unit = gpu_unit; - __entry->gpu_core = gpu_core; - memcpy(__entry->comm, p->comm, TASK_COMM_LEN); - __entry->pid = p->pid; - ), - - TP_printk("unit=%d core=%d comm=%s pid=%d", - __entry->gpu_unit, __entry->gpu_core, __entry->comm, - __entry->pid) - ); - -/* - * Tracepoint for calling GPU unit stop activity on core - */ -TRACE_EVENT(gpu_activity_stop, - - TP_PROTO(int gpu_unit, int gpu_core), - - TP_ARGS(gpu_unit, gpu_core), - - TP_STRUCT__entry( - __field(int, gpu_unit) - __field(int, gpu_core) - ), - - TP_fast_assign( - __entry->gpu_unit = gpu_unit; - __entry->gpu_core = gpu_core; - ), - - TP_printk("unit=%d core=%d", __entry->gpu_unit, __entry->gpu_core) - ); - -#endif /* _TRACE_GPU_H */ - -/* This part must be outside protection */ -#include diff --git a/drivers/gator/gator_trace_power.c b/drivers/gator/gator_trace_power.c index 1895bb988c9f..f2754b1c2b56 100644 --- a/drivers/gator/gator_trace_power.c +++ b/drivers/gator/gator_trace_power.c @@ -75,7 +75,7 @@ static int gator_trace_power_create_files(struct super_block *sb, struct dentry GATOR_DEFINE_PROBE(cpu_frequency, TP_PROTO(unsigned int frequency, unsigned int cpu)) { cpu = lcpu_to_pcpu(cpu); - marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], frequency * 1000); + marshal_event_single64(cpu, power_cpu_key[POWER_CPU_FREQ], frequency * 1000L); } GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu)) @@ -109,7 +109,7 @@ static void gator_trace_power_online(void) int pcpu = get_physical_cpu(); int lcpu = get_logical_cpu(); if (power_cpu_enabled[POWER_CPU_FREQ]) { - marshal_event_single(pcpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(lcpu) * 1000); + marshal_event_single64(pcpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(lcpu) * 1000L); } } diff --git a/drivers/gator/gator_trace_sched.c b/drivers/gator/gator_trace_sched.c index 52990e9d4811..655008628933 100644 --- a/drivers/gator/gator_trace_sched.c +++ b/drivers/gator/gator_trace_sched.c @@ -114,7 +114,7 @@ static void collect_counters(u64 time, struct task_struct *task) // Commit buffers on timeout if (gator_live_rate > 0 && time >= per_cpu(gator_buffer_commit_time, cpu)) { - static const int buftypes[] = { NAME_BUF, COUNTER_BUF, BLOCK_COUNTER_BUF, SCHED_TRACE_BUF }; + static const int buftypes[] = { NAME_BUF, COUNTER_BUF, BLOCK_COUNTER_BUF, SCHED_TRACE_BUF, ACTIVITY_BUF }; int i; for (i = 0; i < ARRAY_SIZE(buftypes); ++i) { @@ -137,35 +137,44 @@ static void collect_counters(u64 time, struct task_struct *task) // special case used during a suspend of the system static void trace_sched_insert_idle(void) { - marshal_sched_trace_switch(0, 0, 0, 0); + marshal_sched_trace_switch(0, 0); } -GATOR_DEFINE_PROBE(sched_process_fork, TP_PROTO(struct task_struct *parent, struct task_struct *child)) +static void gator_trace_emit_link(struct task_struct *p) { int cookie; int cpu = get_physical_cpu(); - cookie = get_exec_cookie(cpu, child); - emit_pid_name(child); + cookie = get_exec_cookie(cpu, p); + emit_pid_name(p); - marshal_sched_trace_start(child->tgid, child->pid, cookie); + marshal_link(cookie, p->tgid, p->pid); } +GATOR_DEFINE_PROBE(sched_process_fork, TP_PROTO(struct task_struct *parent, struct task_struct *child)) +{ + gator_trace_emit_link(child); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +GATOR_DEFINE_PROBE(sched_process_exec, TP_PROTO(struct task_struct *p, pid_t old_pid, struct linux_binprm *bprm)) +{ + gator_trace_emit_link(p); +} +#endif + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) #else GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next)) #endif { - int cookie; int state; int cpu = get_physical_cpu(); per_cpu(in_scheduler_context, cpu) = true; // do as much work as possible before disabling interrupts - cookie = get_exec_cookie(cpu, next); - emit_pid_name(next); if (prev->state == TASK_RUNNING) { state = STATE_CONTENTION; } else if (prev->in_iowait) { @@ -178,7 +187,10 @@ GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_ collect_counters(gator_get_time(), prev); per_cpu(collecting, cpu) = 0; - marshal_sched_trace_switch(next->tgid, next->pid, cookie, state); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + gator_trace_emit_link(next); +#endif + marshal_sched_trace_switch(next->pid, state); per_cpu(in_scheduler_context, cpu) = false; } @@ -199,6 +211,10 @@ static int register_scheduler_tracepoints(void) // register tracepoints if (GATOR_REGISTER_TRACE(sched_process_fork)) goto fail_sched_process_fork; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + if (GATOR_REGISTER_TRACE(sched_process_exec)) + goto fail_sched_process_exec; +#endif if (GATOR_REGISTER_TRACE(sched_switch)) goto fail_sched_switch; if (GATOR_REGISTER_TRACE(sched_process_free)) @@ -216,15 +232,42 @@ static int register_scheduler_tracepoints(void) GATOR_UNREGISTER_TRACE(sched_switch); fail_sched_switch: GATOR_UNREGISTER_TRACE(sched_process_fork); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +fail_sched_process_exec: + GATOR_UNREGISTER_TRACE(sched_process_exec); +#endif fail_sched_process_fork: pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); return -1; } +static void unregister_scheduler_tracepoints(void) +{ + GATOR_UNREGISTER_TRACE(sched_process_fork); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + GATOR_UNREGISTER_TRACE(sched_process_exec); +#endif + GATOR_UNREGISTER_TRACE(sched_switch); + GATOR_UNREGISTER_TRACE(sched_process_free); + pr_debug("gator: unregistered tracepoints\n"); +} + +static void gator_trace_sched_stop(void) +{ + int cpu; + + unregister_scheduler_tracepoints(); + + for_each_present_cpu(cpu) { + kfree(per_cpu(taskname_keys, cpu)); + } +} + static int gator_trace_sched_start(void) { int cpu, size; + int ret; for_each_present_cpu(cpu) { size = TASK_MAP_ENTRIES * TASK_MAX_COLLISIONS * sizeof(uint64_t); @@ -234,7 +277,9 @@ static int gator_trace_sched_start(void) memset(per_cpu(taskname_keys, cpu), 0, size); } - return register_scheduler_tracepoints(); + ret = register_scheduler_tracepoints(); + + return ret; } static void gator_trace_sched_offline(void) @@ -242,24 +287,6 @@ static void gator_trace_sched_offline(void) trace_sched_insert_idle(); } -static void unregister_scheduler_tracepoints(void) -{ - GATOR_UNREGISTER_TRACE(sched_process_fork); - GATOR_UNREGISTER_TRACE(sched_switch); - GATOR_UNREGISTER_TRACE(sched_process_free); - pr_debug("gator: unregistered tracepoints\n"); -} - -static void gator_trace_sched_stop(void) -{ - int cpu; - unregister_scheduler_tracepoints(); - - for_each_present_cpu(cpu) { - kfree(per_cpu(taskname_keys, cpu)); - } -} - static void gator_trace_sched_init(void) { int i; diff --git a/drivers/gator/mali/mali_dd_gator_api.h b/drivers/gator/mali/mali_dd_gator_api.h new file mode 100644 index 000000000000..104b34f2d72a --- /dev/null +++ b/drivers/gator/mali/mali_dd_gator_api.h @@ -0,0 +1,40 @@ +#if !defined(MALI_DDK_GATOR_API_VERSION) + #define MALI_DDK_GATOR_API_VERSION 3 +#endif +#if !defined(MALI_TRUE) + #define MALI_TRUE ((unsigned int)1) +#endif + +#if !defined(MALI_FALSE) + #define MALI_FALSE ((unsigned int)0) +#endif + +struct mali_dd_hwcnt_info { + + /* Passed from Gator to kbase */ + //u32 in_mali_dd_hwcnt_version; + unsigned short int bitmask[4]; + + /* Passed from kbase to Gator */ + + /* ptr to counter dump memory */ + void *kernel_dump_buffer; + + /* size of counter dump memory */ + unsigned int size; + + unsigned int gpu_id; + + unsigned int nr_cores; + + unsigned int nr_core_groups; + + /* The cached present bitmaps - these are the same as the corresponding hardware registers*/ + unsigned long int shader_present_bitmap; +}; + +struct mali_dd_hwcnt_handles; +extern struct mali_dd_hwcnt_handles* mali_dd_hwcnt_init(struct mali_dd_hwcnt_info *in_out_info); +extern void mali_dd_hwcnt_clear(struct mali_dd_hwcnt_info *in_out_info, struct mali_dd_hwcnt_handles *opaque_handles); +extern unsigned int kbase_dd_instr_hwcnt_dump_complete(struct mali_dd_hwcnt_handles *opaque_handles, unsigned int * const success); +extern unsigned int kbase_dd_instr_hwcnt_dump_irq(struct mali_dd_hwcnt_handles *opaque_handles); diff --git a/drivers/gator/mali_t6xx.mk b/drivers/gator/mali_t6xx.mk index 059d47aec910..fa7571ded17b 100644 --- a/drivers/gator/mali_t6xx.mk +++ b/drivers/gator/mali_t6xx.mk @@ -21,6 +21,10 @@ OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/midgard/osk EXTRA_CFLAGS += -DMALI_DIR_MIDGARD=1 endif +ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/midgard/mali_dd_gator_api.h),) +EXTRA_CFLAGS += -DMALI_SIMPLE_API=1 +endif + UMP_DIR = $(DDK_DIR)/include/linux # Include directories in the DDK diff --git a/tools/gator/daemon/Android.mk b/tools/gator/daemon/Android.mk index 045d028fda5f..44c069cc7e24 100644 --- a/tools/gator/daemon/Android.mk +++ b/tools/gator/daemon/Android.mk @@ -3,7 +3,7 @@ include $(CLEAR_VARS) XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h defaults_xml.h) -LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors +LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -pthread -DETCDIR=\"/etc\" -Ilibsensors LOCAL_SRC_FILES := \ Buffer.cpp \ @@ -15,12 +15,14 @@ LOCAL_SRC_FILES := \ DynBuf.cpp \ EventsXML.cpp \ ExternalSource.cpp \ + FSDriver.cpp \ Fifo.cpp \ Hwmon.cpp \ KMod.cpp \ LocalCapture.cpp \ Logging.cpp \ main.cpp \ + MaliVideoDriver.cpp \ Monitor.cpp \ OlySocket.cpp \ OlyUtility.cpp \ @@ -55,7 +57,7 @@ LOCAL_SRC_FILES := \ mxml/mxml-set.c \ mxml/mxml-string.c -LOCAL_C_INCLUDES := $(LOCAL_PATH) +LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_MODULE := gatord LOCAL_MODULE_TAGS := optional diff --git a/tools/gator/daemon/Application.mk b/tools/gator/daemon/Application.mk new file mode 100644 index 000000000000..631ba54148d1 --- /dev/null +++ b/tools/gator/daemon/Application.mk @@ -0,0 +1 @@ +APP_PLATFORM := android-8 diff --git a/tools/gator/daemon/Buffer.cpp b/tools/gator/daemon/Buffer.cpp index 93557dabed9f..dd19f7f8be76 100644 --- a/tools/gator/daemon/Buffer.cpp +++ b/tools/gator/daemon/Buffer.cpp @@ -15,11 +15,12 @@ #define mask (mSize - 1) enum { - CODE_PEA = 1, - CODE_KEYS = 2, - CODE_FORMAT = 3, - CODE_MAPS = 4, - CODE_COMM = 5, + CODE_PEA = 1, + CODE_KEYS = 2, + CODE_FORMAT = 3, + CODE_MAPS = 4, + CODE_COMM = 5, + CODE_KEYS_OLD = 6, }; // Summary Frame Messages @@ -167,7 +168,7 @@ void Buffer::check(const uint64_t time) { } } -void Buffer::packInt(int32_t x) { +void Buffer::packInt(char *const buf, const int size, int &writePos, int32_t x) { int packedBytes = 0; int more = true; while (more) { @@ -181,11 +182,15 @@ void Buffer::packInt(int32_t x) { b |= 0x80; } - mBuf[(mWritePos + packedBytes) & mask] = b; + buf[(writePos + packedBytes) & /*mask*/(size - 1)] = b; packedBytes++; } - mWritePos = (mWritePos + packedBytes) & mask; + writePos = (writePos + packedBytes) & /*mask*/(size - 1); +} + +void Buffer::packInt(int32_t x) { + packInt(mBuf, mSize, mWritePos, x); } void Buffer::packInt64(int64_t x) { @@ -320,6 +325,21 @@ void Buffer::keys(const int count, const __u64 *const ids, const int *const keys check(1); } +void Buffer::keysOld(const int keyCount, const int *const keys, const int bytes, const char *const buf) { + if (checkSpace((2 + keyCount) * MAXSIZE_PACK32 + bytes)) { + packInt(CODE_KEYS_OLD); + packInt(keyCount); + for (int i = 0; i < keyCount; ++i) { + packInt(keys[i]); + } + writeBytes(buf, bytes); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + void Buffer::format(const int length, const char *const format) { if (checkSpace(MAXSIZE_PACK32 + length + 1)) { packInt(CODE_FORMAT); diff --git a/tools/gator/daemon/Buffer.h b/tools/gator/daemon/Buffer.h index 50237771860c..2de1b97ac091 100644 --- a/tools/gator/daemon/Buffer.h +++ b/tools/gator/daemon/Buffer.h @@ -54,6 +54,7 @@ class Buffer { // Perf Attrs messages void pea(const struct perf_event_attr *const pea, int key); void keys(const int count, const __u64 *const ids, const int *const keys); + void keysOld(const int keyCount, const int *const keys, const int bytes, const char *const buf); void format(const int length, const char *const format); void maps(const int pid, const int tid, const char *const maps); void comm(const int pid, const int tid, const char *const image, const char *const comm); @@ -64,6 +65,11 @@ class Buffer { // Prefer a new member to using these functions if possible char *getWritePos() { return mBuf + mWritePos; } void advanceWrite(int bytes) { mWritePos = (mWritePos + bytes) & /*mask*/(mSize - 1); } + static void packInt(char *const buf, const int size, int &writePos, int32_t x); + void packInt(int32_t x); + void packInt64(int64_t x); + void writeBytes(const void *const data, size_t count); + void writeString(const char *const str); static void writeLEInt(unsigned char *buf, int v) { buf[0] = (v >> 0) & 0xFF; @@ -76,11 +82,6 @@ class Buffer { bool commitReady() const; bool checkSpace(int bytes); - void packInt(int32_t x); - void packInt64(int64_t x); - void writeBytes(const void *const data, size_t count); - void writeString(const char *const str); - const int32_t mCore; const int32_t mBufType; const int mSize; diff --git a/tools/gator/daemon/CapturedXML.cpp b/tools/gator/daemon/CapturedXML.cpp index cf79b72a1166..4a11415a00c9 100644 --- a/tools/gator/daemon/CapturedXML.cpp +++ b/tools/gator/daemon/CapturedXML.cpp @@ -33,7 +33,7 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { captured = mxmlNewElement(xml, "captured"); mxmlElementSetAttr(captured, "version", "1"); if (gSessionData->perf.isSetup()) { - mxmlElementSetAttr(captured, "type", "Perf"); + mxmlElementSetAttr(captured, "type", "Perf"); } mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION); if (includeTime) { // Send the following only after the capture is complete @@ -66,10 +66,15 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { mxml_node_t *const node = mxmlNewElement(counters, "counter"); mxmlElementSetAttrf(node, "key", "0x%x", counter.getKey()); mxmlElementSetAttr(node, "type", counter.getType()); - mxmlElementSetAttrf(node, "event", "0x%x", counter.getEvent()); + if (counter.getEvent() != -1) { + mxmlElementSetAttrf(node, "event", "0x%x", counter.getEvent()); + } if (counter.getCount() > 0) { mxmlElementSetAttrf(node, "count", "%d", counter.getCount()); } + if (counter.getCores() > 0) { + mxmlElementSetAttrf(node, "cores", "%d", counter.getCores()); + } } } @@ -89,7 +94,7 @@ void CapturedXML::write(char* path) { // Set full path snprintf(file, PATH_MAX, "%s/captured.xml", path); - + char* xml = getXML(true); if (util->writeToDisk(file, xml) < 0) { logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); diff --git a/tools/gator/daemon/CapturedXML.h b/tools/gator/daemon/CapturedXML.h index efc1e52bdba3..ed08c44bc3ff 100644 --- a/tools/gator/daemon/CapturedXML.h +++ b/tools/gator/daemon/CapturedXML.h @@ -23,4 +23,4 @@ class CapturedXML { const char * mxmlWhitespaceCB(mxml_node_t *node, int where); -#endif //__CAPTURED_XML_H__ +#endif //__CAPTURED_XML_H__ diff --git a/tools/gator/daemon/Child.cpp b/tools/gator/daemon/Child.cpp index ca33561ffdca..1901ecc6a724 100644 --- a/tools/gator/daemon/Child.cpp +++ b/tools/gator/daemon/Child.cpp @@ -26,13 +26,13 @@ #include "Driver.h" #include "PerfSource.h" #include "DriverSource.h" -#include "UserSpaceSource.h" #include "ExternalSource.h" +#include "UserSpaceSource.h" static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads static Source *primarySource = NULL; -static Source *userSpaceSource = NULL; static Source *externalSource = NULL; +static Source *userSpaceSource = NULL; static Sender* sender = NULL; // Shared by Child.cpp and spawned threads Child* child = NULL; // shared by Child.cpp and main.cpp @@ -147,16 +147,16 @@ static void *senderThread(void *) { prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0); sem_wait(&haltPipeline); - while (!primarySource->isDone() || (userSpaceSource != NULL && !userSpaceSource->isDone()) || (externalSource != NULL && !externalSource->isDone())) { + while (!primarySource->isDone() || + !externalSource->isDone() || + (userSpaceSource != NULL && !userSpaceSource->isDone())) { sem_wait(&senderSem); primarySource->write(sender); + externalSource->write(sender); if (userSpaceSource != NULL) { userSpaceSource->write(sender); } - if (externalSource != NULL) { - externalSource->write(sender); - } } // write end-of-capture sequence @@ -202,6 +202,10 @@ void Child::initialization() { void Child::endSession() { gSessionData->mSessionIsActive = false; primarySource->interrupt(); + externalSource->interrupt(); + if (userSpaceSource != NULL) { + userSpaceSource->interrupt(); + } sem_post(&haltPipeline); } @@ -227,9 +231,9 @@ void Child::run() { // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated if (!gSessionData->perf.isSetup()) { - primarySource = new DriverSource(&senderSem, &startProfile); + primarySource = new DriverSource(&senderSem, &startProfile); } else { - primarySource = new PerfSource(&senderSem, &startProfile); + primarySource = new PerfSource(&senderSem, &startProfile); } // Initialize all drivers @@ -280,11 +284,18 @@ void Child::run() { thread_creation_success = false; } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) { thread_creation_success = false; - } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)){ + } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)) { thread_creation_success = false; } - if (gSessionData->hwmon.countersEnabled()) { + externalSource = new ExternalSource(&senderSem); + if (!externalSource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); + handleException(); + } + externalSource->start(); + + if (gSessionData->hwmon.countersEnabled() || gSessionData->fsDriver.countersEnabled()) { userSpaceSource = new UserSpaceSource(&senderSem); if (!userSpaceSource->prepare()) { logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); @@ -292,14 +303,6 @@ void Child::run() { } userSpaceSource->start(); } - if (access("/tmp/gator", F_OK) == 0) { - externalSource = new ExternalSource(&senderSem); - if (!externalSource->prepare()) { - logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); - handleException(); - } - externalSource->start(); - } if (!thread_creation_success) { logg->logError(__FILE__, __LINE__, "Failed to create gator threads"); @@ -312,12 +315,10 @@ void Child::run() { // Start profiling primarySource->run(); - if (externalSource != NULL) { - externalSource->join(); - } if (userSpaceSource != NULL) { userSpaceSource->join(); } + externalSource->join(); // Wait for the other threads to exit pthread_join(senderThreadID, NULL); @@ -337,8 +338,8 @@ void Child::run() { logg->logMessage("Profiling ended."); - delete externalSource; delete userSpaceSource; + delete externalSource; delete primarySource; delete sender; delete localCapture; diff --git a/tools/gator/daemon/Child.h b/tools/gator/daemon/Child.h index 9e206d7113b8..a306a7760819 100644 --- a/tools/gator/daemon/Child.h +++ b/tools/gator/daemon/Child.h @@ -30,4 +30,4 @@ class Child { Child &operator=(const Child &); }; -#endif //__CHILD_H__ +#endif //__CHILD_H__ diff --git a/tools/gator/daemon/ConfigurationXML.cpp b/tools/gator/daemon/ConfigurationXML.cpp index fd479f2452cd..6590dd389196 100644 --- a/tools/gator/daemon/ConfigurationXML.cpp +++ b/tools/gator/daemon/ConfigurationXML.cpp @@ -21,12 +21,13 @@ static const char* ATTR_COUNTER = "counter"; static const char* ATTR_REVISION = "revision"; static const char* ATTR_EVENT = "event"; static const char* ATTR_COUNT = "count"; +static const char* ATTR_CORES = "cores"; ConfigurationXML::ConfigurationXML() { const char * configuration_xml; unsigned int configuration_xml_len; getDefaultConfigurationXml(configuration_xml, configuration_xml_len); - + char path[PATH_MAX]; getPath(path); @@ -53,7 +54,7 @@ ConfigurationXML::ConfigurationXML() { break; } - + validate(); } @@ -82,7 +83,7 @@ int ConfigurationXML::parse(const char* configurationXML) { node = mxmlGetFirstChild(tree); while (node && mxmlGetType(node) != MXML_ELEMENT) node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); - + ret = configurationsTag(node); node = mxmlGetFirstChild(node); @@ -127,7 +128,7 @@ void ConfigurationXML::validate(void) { #define CONFIGURATION_REVISION 3 int ConfigurationXML::configurationsTag(mxml_node_t *node) { const char* revision_string; - + revision_string = mxmlElementGetAttr(node, ATTR_REVISION); if (!revision_string) { return 1; //revision issue; @@ -158,6 +159,7 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER)); if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16)); if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10)); + if (mxmlElementGetAttr(node, ATTR_CORES)) counter.setCores(strtol(mxmlElementGetAttr(node, ATTR_CORES), NULL, 10)); if (counter.getCount() > 0) { gSessionData->mIsEBS = true; } diff --git a/tools/gator/daemon/Counter.h b/tools/gator/daemon/Counter.h index 689174573e4e..5202aa046362 100644 --- a/tools/gator/daemon/Counter.h +++ b/tools/gator/daemon/Counter.h @@ -27,6 +27,7 @@ class Counter { mEnabled = false; mEvent = -1; mCount = 0; + mCores = -1; mKey = 0; mDriver = NULL; } @@ -35,6 +36,7 @@ class Counter { void setEnabled(const bool enabled) { mEnabled = enabled; } void setEvent(const int event) { mEvent = event; } void setCount(const int count) { mCount = count; } + void setCores(const int cores) { mCores = cores; } void setKey(const int key) { mKey = key; } void setDriver(Driver *const driver) { mDriver = driver; } @@ -42,6 +44,7 @@ class Counter { bool isEnabled() const { return mEnabled; } int getEvent() const { return mEvent; } int getCount() const { return mCount; } + int getCores() const { return mCores; } int getKey() const { return mKey; } Driver *getDriver() const { return mDriver; } @@ -54,6 +57,7 @@ class Counter { bool mEnabled; int mEvent; int mCount; + int mCores; int mKey; Driver *mDriver; }; diff --git a/tools/gator/daemon/DriverSource.cpp b/tools/gator/daemon/DriverSource.cpp index f78ec6b7ce41..11d3095ef6d2 100644 --- a/tools/gator/daemon/DriverSource.cpp +++ b/tools/gator/daemon/DriverSource.cpp @@ -12,19 +12,24 @@ #include #include +#include #include +#include "Buffer.h" #include "Child.h" +#include "DynBuf.h" #include "Fifo.h" #include "Logging.h" +#include "Proc.h" #include "Sender.h" #include "SessionData.h" extern Child *child; -DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) { +DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mBuffer(NULL), mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) { int driver_version = 0; + mBuffer = new Buffer(0, FRAME_PERF_ATTRS, 4*1024*1024, senderSem); if (readIntDriver("/dev/gator/version", &driver_version) == -1) { logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); handleException(); @@ -43,7 +48,7 @@ DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mFifo(NULL), handleException(); } else { // Release version mismatch - logg->logError(__FILE__, __LINE__, + logg->logError(__FILE__, __LINE__, "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); handleException(); @@ -87,6 +92,28 @@ bool DriverSource::prepare() { return true; } +void DriverSource::bootstrapThread() { + prctl(PR_SET_NAME, (unsigned long)&"gatord-bootstrap", 0, 0, 0); + + DynBuf printb; + DynBuf b1; + DynBuf b2; + DynBuf b3; + + if (!readProc(mBuffer, false, &printb, &b1, &b2, &b3)) { + logg->logMessage("%s(%s:%i): readProc failed", __FUNCTION__, __FILE__, __LINE__); + handleException(); + } + + mBuffer->commit(1); + mBuffer->setDone(); +} + +void *DriverSource::bootstrapThreadStatic(void *arg) { + static_cast(arg)->bootstrapThread(); + return NULL; +} + void DriverSource::run() { // Get the initial pointer to the collect buffer char *collectBuffer = mFifo->start(); @@ -138,6 +165,12 @@ void DriverSource::run() { sem_post(mStartProfile); + pthread_t bootstrapThreadID; + if (pthread_create(&bootstrapThreadID, NULL, bootstrapThreadStatic, this) != 0) { + logg->logError(__FILE__, __LINE__, "Unable to start the gator_bootstrap thread"); + handleException(); + } + // Collect Data do { // This command will stall until data is received from the driver @@ -164,6 +197,8 @@ void DriverSource::run() { } while (bytesCollected > 0); logg->logMessage("Exit collect data loop"); + + pthread_join(bootstrapThreadID, NULL); } void DriverSource::interrupt() { @@ -174,7 +209,7 @@ void DriverSource::interrupt() { } bool DriverSource::isDone() { - return mLength <= 0; + return mLength <= 0 && (mBuffer == NULL || mBuffer->isDone()); } void DriverSource::write(Sender *sender) { @@ -182,6 +217,16 @@ void DriverSource::write(Sender *sender) { if (data != NULL) { sender->writeData(data, mLength, RESPONSE_APC_DATA); mFifo->release(); + // Assume the summary packet is in the first block received from the driver + gSessionData->mSentSummary = true; + } + if (mBuffer != NULL && !mBuffer->isDone()) { + mBuffer->write(sender); + if (mBuffer->isDone()) { + Buffer *buf = mBuffer; + mBuffer = NULL; + delete buf; + } } } @@ -227,7 +272,7 @@ int DriverSource::readInt64Driver(const char *fullpath, int64_t *value) { char *endptr; errno = 0; *value = strtoll(data, &endptr, 10); - if (errno != 0 || *endptr != '\n') { + if (errno != 0 || (*endptr != '\n' && *endptr != '\0')) { logg->logMessage("Invalid value in file %s", fullpath); return -1; } diff --git a/tools/gator/daemon/DriverSource.h b/tools/gator/daemon/DriverSource.h index dcf1078a239c..ec27b0815bbf 100644 --- a/tools/gator/daemon/DriverSource.h +++ b/tools/gator/daemon/DriverSource.h @@ -14,6 +14,7 @@ #include "Source.h" +class Buffer; class Fifo; class DriverSource : public Source { @@ -37,6 +38,10 @@ class DriverSource : public Source { static int writeReadDriver(const char *path, int64_t *value); private: + static void *bootstrapThreadStatic(void *arg); + void bootstrapThread(); + + Buffer *mBuffer; Fifo *mFifo; sem_t *const mSenderSem; sem_t *const mStartProfile; diff --git a/tools/gator/daemon/EventsXML.cpp b/tools/gator/daemon/EventsXML.cpp index a07a046f3353..cf0192ef671f 100644 --- a/tools/gator/daemon/EventsXML.cpp +++ b/tools/gator/daemon/EventsXML.cpp @@ -13,7 +13,7 @@ #include "OlyUtility.h" #include "SessionData.h" -char* EventsXML::getXML() { +mxml_node_t *EventsXML::getTree() { #include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len char path[PATH_MAX]; mxml_node_t *xml; @@ -38,6 +38,12 @@ char* EventsXML::getXML() { xml = mxmlLoadString(NULL, (const char *)events_xml, MXML_NO_CALLBACK); } + return xml; +} + +char *EventsXML::getXML() { + mxml_node_t *xml = getTree(); + // Add dynamic events from the drivers mxml_node_t *events = mxmlFindElement(xml, xml, "events", NULL, NULL, MXML_DESCEND); if (!events) { @@ -48,19 +54,19 @@ char* EventsXML::getXML() { driver->writeEvents(events); } - char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + char *string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); mxmlDelete(xml); return string; } -void EventsXML::write(const char* path) { +void EventsXML::write(const char *path) { char file[PATH_MAX]; // Set full path snprintf(file, PATH_MAX, "%s/events.xml", path); - - char* buf = getXML(); + + char *buf = getXML(); if (util->writeToDisk(file, buf) < 0) { logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); handleException(); diff --git a/tools/gator/daemon/EventsXML.h b/tools/gator/daemon/EventsXML.h index 6cd1560f7d4e..ff7a02fd3c78 100644 --- a/tools/gator/daemon/EventsXML.h +++ b/tools/gator/daemon/EventsXML.h @@ -9,9 +9,12 @@ #ifndef EVENTS_XML #define EVENTS_XML +#include "mxml/mxml.h" + class EventsXML { public: - char* getXML(); + mxml_node_t *getTree(); + char *getXML(); void write(const char* path); }; diff --git a/tools/gator/daemon/ExternalSource.cpp b/tools/gator/daemon/ExternalSource.cpp index fe5824b04812..b6ec301d0c08 100644 --- a/tools/gator/daemon/ExternalSource.cpp +++ b/tools/gator/daemon/ExternalSource.cpp @@ -8,41 +8,195 @@ #include "ExternalSource.h" +#include #include +#include #include "Logging.h" #include "OlySocket.h" #include "SessionData.h" -ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 1024, senderSem), mSock("/tmp/gator") { +static const char MALI_VIDEO[] = "\0mali-video"; +static const char MALI_VIDEO_STARTUP[] = "\0mali-video-startup"; +static const char MALI_VIDEO_V1[] = "MALI_VIDEO 1\n"; + +static bool setNonblock(const int fd) { + int flags; + + flags = fcntl(fd, F_GETFL); + if (flags < 0) { + logg->logMessage("fcntl getfl failed"); + return false; + } + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) { + logg->logMessage("fcntl setfl failed"); + return false; + } + + return true; +} + +ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 128*1024, senderSem), mMonitor(), mMveStartupUds(MALI_VIDEO_STARTUP, sizeof(MALI_VIDEO_STARTUP)), mInterruptFd(-1), mMveUds(-1) { + sem_init(&mBufferSem, 0, 0); } ExternalSource::~ExternalSource() { } +void ExternalSource::waitFor(const uint64_t currTime, const int bytes) { + while (mBuffer.bytesAvailable() <= bytes) { + mBuffer.check(currTime); + sem_wait(&mBufferSem); + } +} + +void ExternalSource::configureConnection(const int fd, const char *const handshake, size_t size) { + if (!setNonblock(fd)) { + logg->logError(__FILE__, __LINE__, "Unable to set nonblock on fh"); + handleException(); + } + + if (!mMonitor.add(fd)) { + logg->logError(__FILE__, __LINE__, "Unable to add fh to monitor"); + handleException(); + } + + // Write the handshake to the circular buffer + waitFor(1, Buffer::MAXSIZE_PACK32 + 4 + size - 1); + mBuffer.packInt(fd); + mBuffer.writeLEInt((unsigned char *)mBuffer.getWritePos(), size - 1); + mBuffer.advanceWrite(4); + mBuffer.writeBytes(handshake, size - 1); +} + +bool ExternalSource::connectMve() { + if (!gSessionData->maliVideo.countersEnabled()) { + return true; + } + + mMveUds = OlySocket::connect(MALI_VIDEO, sizeof(MALI_VIDEO)); + if (mMveUds < 0) { + return false; + } + + if (!gSessionData->maliVideo.start(mMveUds)) { + return false; + } + + configureConnection(mMveUds, MALI_VIDEO_V1, sizeof(MALI_VIDEO_V1)); + + return true; +} + bool ExternalSource::prepare() { + if (!mMonitor.init() || !setNonblock(mMveStartupUds.getFd()) || !mMonitor.add(mMveStartupUds.getFd())) { + return false; + } + + connectMve(); + return true; } void ExternalSource::run() { - prctl(PR_SET_NAME, (unsigned long)&"gatord-uds", 0, 0, 0); + int pipefd[2]; + + prctl(PR_SET_NAME, (unsigned long)&"gatord-external", 0, 0, 0); + + if (pipe(pipefd) != 0) { + logg->logError(__FILE__, __LINE__, "pipe failed"); + handleException(); + } + mInterruptFd = pipefd[1]; + + if (!mMonitor.add(pipefd[0])) { + logg->logError(__FILE__, __LINE__, "Monitor::add failed"); + handleException(); + } while (gSessionData->mSessionIsActive) { - // Will be aborted when the socket is closed at the end of the capture - int length = mSock.receive(mBuffer.getWritePos(), mBuffer.contiguousSpaceAvailable()); - if (length <= 0) { - break; + struct epoll_event events[16]; + // Clear any pending sem posts + while (sem_trywait(&mBufferSem) == 0); + int ready = mMonitor.wait(events, ARRAY_LENGTH(events), -1); + if (ready < 0) { + logg->logError(__FILE__, __LINE__, "Monitor::wait failed"); + handleException(); } - mBuffer.advanceWrite(length); - mBuffer.check(0); + const uint64_t currTime = getTime(); + + for (int i = 0; i < ready; ++i) { + const int fd = events[i].data.fd; + if (fd == mMveStartupUds.getFd()) { + // Mali Video Engine says it's alive + int client = mMveStartupUds.acceptConnection(); + // Don't read from this connection, establish a new connection to Mali-V500 + close(client); + if (!connectMve()) { + logg->logError(__FILE__, __LINE__, "Unable to configure incoming Mali video connection"); + handleException(); + } + } else if (fd == pipefd[0]) { + // Means interrupt has been called and mSessionIsActive should be reread + } else { + while (true) { + waitFor(currTime, Buffer::MAXSIZE_PACK32 + 4); + + mBuffer.packInt(fd); + char *const bytesPos = mBuffer.getWritePos(); + mBuffer.advanceWrite(4); + const int contiguous = mBuffer.contiguousSpaceAvailable(); + const int bytes = read(fd, mBuffer.getWritePos(), contiguous); + if (bytes < 0) { + if (errno == EAGAIN) { + // Nothing left to read, and Buffer convention dictates that writePos can't go backwards + mBuffer.writeLEInt((unsigned char *)bytesPos, 0); + break; + } + // Something else failed, close the socket + mBuffer.writeLEInt((unsigned char *)bytesPos, -1); + close(fd); + break; + } else if (bytes == 0) { + // The other side is closed + mBuffer.writeLEInt((unsigned char *)bytesPos, -1); + close(fd); + break; + } + + mBuffer.writeLEInt((unsigned char *)bytesPos, bytes); + mBuffer.advanceWrite(bytes); + + // Short reads also mean nothing is left to read + if (bytes < contiguous) { + break; + } + } + } + } + + // Only call mBufferCheck once per iteration + mBuffer.check(currTime); } mBuffer.setDone(); + + mInterruptFd = -1; + close(pipefd[0]); + close(pipefd[1]); } void ExternalSource::interrupt() { - // Do nothing + if (mInterruptFd >= 0) { + int8_t c = 0; + // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread + if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) { + logg->logError(__FILE__, __LINE__, "write failed"); + handleException(); + } + } } bool ExternalSource::isDone() { @@ -50,7 +204,12 @@ bool ExternalSource::isDone() { } void ExternalSource::write(Sender *sender) { + // Don't send external data until the summary packet is sent so that monotonic delta is available + if (!gSessionData->mSentSummary) { + return; + } if (!mBuffer.isDone()) { mBuffer.write(sender); + sem_post(&mBufferSem); } } diff --git a/tools/gator/daemon/ExternalSource.h b/tools/gator/daemon/ExternalSource.h index 2052bdf2823e..2e7ed27df255 100644 --- a/tools/gator/daemon/ExternalSource.h +++ b/tools/gator/daemon/ExternalSource.h @@ -12,6 +12,7 @@ #include #include "Buffer.h" +#include "Monitor.h" #include "OlySocket.h" #include "Source.h" @@ -29,8 +30,16 @@ class ExternalSource : public Source { void write(Sender *sender); private: + void waitFor(const uint64_t currTime, const int bytes); + void configureConnection(const int fd, const char *const handshake, size_t size); + bool connectMve(); + + sem_t mBufferSem; Buffer mBuffer; - OlySocket mSock; + Monitor mMonitor; + OlyServerSocket mMveStartupUds; + int mInterruptFd; + int mMveUds; // Intentionally unimplemented ExternalSource(const ExternalSource &); diff --git a/tools/gator/daemon/FSDriver.cpp b/tools/gator/daemon/FSDriver.cpp new file mode 100644 index 000000000000..40c8df1af222 --- /dev/null +++ b/tools/gator/daemon/FSDriver.cpp @@ -0,0 +1,212 @@ +/** + * Copyright (C) ARM Limited 2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "FSDriver.h" + +#include +#include +#include +#include +#include + +#include "Buffer.h" +#include "Counter.h" +#include "DriverSource.h" +#include "Logging.h" +#include "SessionData.h" + +class FSCounter { +public: + FSCounter(FSCounter *next, char *name, const char *regex); + ~FSCounter(); + + FSCounter *getNext() const { return next; } + int getKey() const { return key; } + bool isEnabled() const { return enabled; } + void setEnabled(const bool enabled) { this->enabled = enabled; } + const char *getName() const { return name; } + int64_t read(); + +private: + FSCounter *const next; + regex_t reg; + char *name; + const int key; + int enabled : 1, + useRegex : 1; + + // Intentionally unimplemented + FSCounter(const FSCounter &); + FSCounter &operator=(const FSCounter &); +}; + +FSCounter::FSCounter(FSCounter *next, char *name, const char *regex) : next(next), name(name), key(getEventKey()), enabled(false), useRegex(regex != NULL) { + if (useRegex) { + int result = regcomp(®, regex, REG_EXTENDED); + if (result != 0) { + char buf[128]; + regerror(result, ®, buf, sizeof(buf)); + logg->logError(__FILE__, __LINE__, "Invalid regex '%s': %s", regex, buf); + handleException(); + } + } +} + +FSCounter::~FSCounter() { + free(name); + if (useRegex) { + regfree(®); + } +} + +int64_t FSCounter::read() { + int64_t value; + if (useRegex) { + char buf[4096]; + size_t pos = 0; + const int fd = open(name, O_RDONLY); + if (fd < 0) { + goto fail; + } + while (pos < sizeof(buf) - 1) { + const ssize_t bytes = ::read(fd, buf + pos, sizeof(buf) - pos - 1); + if (bytes < 0) { + goto fail; + } else if (bytes == 0) { + break; + } + pos += bytes; + } + close(fd); + buf[pos] = '\0'; + + regmatch_t match[2]; + int result = regexec(®, buf, 2, match, 0); + if (result != 0) { + regerror(result, ®, buf, sizeof(buf)); + logg->logError(__FILE__, __LINE__, "Parsing %s failed: %s", name, buf); + handleException(); + } + + if (match[1].rm_so < 0) { + logg->logError(__FILE__, __LINE__, "Parsing %s failed", name); + handleException(); + } + char *endptr; + errno = 0; + value = strtoll(buf + match[1].rm_so, &endptr, 0); + if (errno != 0) { + logg->logError(__FILE__, __LINE__, "Parsing %s failed: %s", name, strerror(errno)); + handleException(); + } + } else { + if (DriverSource::readInt64Driver(name, &value) != 0) { + goto fail; + } + } + return value; + + fail: + logg->logError(__FILE__, __LINE__, "Unable to read %s", name); + handleException(); +} + +FSDriver::FSDriver() : counters(NULL) { +} + +FSDriver::~FSDriver() { + while (counters != NULL) { + FSCounter * counter = counters; + counters = counter->getNext(); + delete counter; + } +} + +void FSDriver::setup(mxml_node_t *const xml) { + // fs driver does not currently work with perf + if (gSessionData->perf.isSetup()) { + return; + } + + mxml_node_t *node = xml; + while (true) { + node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND); + if (node == NULL) { + break; + } + const char *counter = mxmlElementGetAttr(node, "counter"); + if ((counter != NULL) && (counter[0] == '/')) { + const char *regex = mxmlElementGetAttr(node, "regex"); + counters = new FSCounter(counters, strdup(counter), regex); + } + } +} + +FSCounter *FSDriver::findCounter(const Counter &counter) const { + for (FSCounter * fsCounter = counters; fsCounter != NULL; fsCounter = fsCounter->getNext()) { + if (strcmp(fsCounter->getName(), counter.getType()) == 0) { + return fsCounter; + } + } + + return NULL; +} + +bool FSDriver::claimCounter(const Counter &counter) const { + return findCounter(counter) != NULL; +} + +bool FSDriver::countersEnabled() const { + for (FSCounter *counter = counters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled()) { + return true; + } + } + return false; +} + +void FSDriver::resetCounters() { + for (FSCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + counter->setEnabled(false); + } +} + +void FSDriver::setupCounter(Counter &counter) { + FSCounter *const fsCounter = findCounter(counter); + if (fsCounter == NULL) { + counter.setEnabled(false); + return; + } + fsCounter->setEnabled(true); + counter.setKey(fsCounter->getKey()); +} + +int FSDriver::writeCounters(mxml_node_t *root) const { + int count = 0; + for (FSCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (access(counter->getName(), R_OK) == 0) { + mxml_node_t *node = mxmlNewElement(root, "counter"); + mxmlElementSetAttr(node, "name", counter->getName()); + ++count; + } + } + + return count; +} + +void FSDriver::start() { +} + +void FSDriver::read(Buffer * const buffer) { + for (FSCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (!counter->isEnabled()) { + continue; + } + buffer->event(counter->getKey(), counter->read()); + } +} diff --git a/tools/gator/daemon/FSDriver.h b/tools/gator/daemon/FSDriver.h new file mode 100644 index 000000000000..ef3955362331 --- /dev/null +++ b/tools/gator/daemon/FSDriver.h @@ -0,0 +1,44 @@ +/** + * Copyright (C) ARM Limited 2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FSDRIVER_H +#define FSDRIVER_H + +#include "Driver.h" + +class Buffer; +class FSCounter; + +class FSDriver : public Driver { +public: + FSDriver(); + ~FSDriver(); + + void setup(mxml_node_t *const xml); + + bool claimCounter(const Counter &counter) const; + bool countersEnabled() const; + void resetCounters(); + void setupCounter(Counter &counter); + + int writeCounters(mxml_node_t *root) const; + + void start(); + void read(Buffer * buffer); + +private: + FSCounter *findCounter(const Counter &counter) const; + + FSCounter *counters; + + // Intentionally unimplemented + FSDriver(const FSDriver &); + FSDriver &operator=(const FSDriver &); +}; + +#endif // FSDRIVER_H diff --git a/tools/gator/daemon/Fifo.h b/tools/gator/daemon/Fifo.h index 7dd7426132d8..bdda3f549b50 100644 --- a/tools/gator/daemon/Fifo.h +++ b/tools/gator/daemon/Fifo.h @@ -45,4 +45,4 @@ class Fifo { Fifo &operator=(const Fifo &); }; -#endif //__FIFO_H__ +#endif //__FIFO_H__ diff --git a/tools/gator/daemon/Hwmon.cpp b/tools/gator/daemon/Hwmon.cpp index 778f30755dfe..e44424743ef0 100644 --- a/tools/gator/daemon/Hwmon.cpp +++ b/tools/gator/daemon/Hwmon.cpp @@ -28,6 +28,7 @@ public: const char *getTitle() const { return title; } bool isDuplicate() const { return duplicate; } const char *getDisplay() const { return display; } + const char *getCounterClass() const { return counter_class; } const char *getUnit() const { return unit; } int getModifier() const { return modifier; } @@ -58,6 +59,7 @@ private: char *label; const char *title; const char *display; + const char *counter_class; const char *unit; int modifier; double previous_value; @@ -87,7 +89,8 @@ HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, co case SENSORS_FEATURE_IN: title = "Voltage"; input = SENSORS_SUBFEATURE_IN_INPUT; - display = "average"; + display = "maximum"; + counter_class = "absolute"; unit = "V"; modifier = 1000; monotonic = false; @@ -96,6 +99,7 @@ HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, co title = "Fan"; input = SENSORS_SUBFEATURE_FAN_INPUT; display = "average"; + counter_class = "absolute"; unit = "RPM"; modifier = 1; monotonic = false; @@ -104,6 +108,7 @@ HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, co title = "Temperature"; input = SENSORS_SUBFEATURE_TEMP_INPUT; display = "maximum"; + counter_class = "absolute"; unit = "°C"; modifier = 1000; monotonic = false; @@ -111,7 +116,8 @@ HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, co case SENSORS_FEATURE_POWER: title = "Power"; input = SENSORS_SUBFEATURE_POWER_INPUT; - display = "average"; + display = "maximum"; + counter_class = "absolute"; unit = "W"; modifier = 1000000; monotonic = false; @@ -120,6 +126,7 @@ HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, co title = "Energy"; input = SENSORS_SUBFEATURE_ENERGY_INPUT; display = "accumulate"; + counter_class = "delta"; unit = "J"; modifier = 1000000; monotonic = true; @@ -127,7 +134,8 @@ HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, co case SENSORS_FEATURE_CURR: title = "Current"; input = SENSORS_SUBFEATURE_CURR_INPUT; - display = "average"; + display = "maximum"; + counter_class = "absolute"; unit = "A"; modifier = 1000; monotonic = false; @@ -136,6 +144,7 @@ HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, co title = "Humidity"; input = SENSORS_SUBFEATURE_HUMIDITY_INPUT; display = "average"; + counter_class = "absolute"; unit = "%"; modifier = 1000; monotonic = false; @@ -311,6 +320,7 @@ void Hwmon::writeEvents(mxml_node_t *root) const { mxmlElementSetAttr(node, "name", counter->getLabel()); } mxmlElementSetAttr(node, "display", counter->getDisplay()); + mxmlElementSetAttr(node, "class", counter->getCounterClass()); mxmlElementSetAttr(node, "units", counter->getUnit()); if (counter->getModifier() != 1) { mxmlElementSetAttrf(node, "modifier", "%d", counter->getModifier()); diff --git a/tools/gator/daemon/KMod.cpp b/tools/gator/daemon/KMod.cpp index 9300002f3fb2..73e123d2f14e 100644 --- a/tools/gator/daemon/KMod.cpp +++ b/tools/gator/daemon/KMod.cpp @@ -58,10 +58,15 @@ void KMod::setupCounter(Counter &counter) { return; } + int value = 0; snprintf(text, sizeof(text), "%s/key", base); - int key = 0; - DriverSource::readIntDriver(text, &key); - counter.setKey(key); + DriverSource::readIntDriver(text, &value); + counter.setKey(value); + + snprintf(text, sizeof(text), "%s/cores", base); + if (DriverSource::readIntDriver(text, &value) == 0) { + counter.setCores(value); + } snprintf(text, sizeof(text), "%s/event", base); DriverSource::writeDriver(text, counter.getEvent()); diff --git a/tools/gator/daemon/LocalCapture.h b/tools/gator/daemon/LocalCapture.h index aadeccecf0cc..b1e7219795cf 100644 --- a/tools/gator/daemon/LocalCapture.h +++ b/tools/gator/daemon/LocalCapture.h @@ -23,4 +23,4 @@ class LocalCapture { int removeDirAndAllContents(char* path); }; -#endif //__LOCAL_CAPTURE_H__ +#endif //__LOCAL_CAPTURE_H__ diff --git a/tools/gator/daemon/Logging.h b/tools/gator/daemon/Logging.h index 6ae328046989..4934bb079754 100644 --- a/tools/gator/daemon/Logging.h +++ b/tools/gator/daemon/Logging.h @@ -33,4 +33,4 @@ extern Logging* logg; extern void handleException() __attribute__ ((noreturn)); -#endif //__LOGGING_H__ +#endif //__LOGGING_H__ diff --git a/tools/gator/daemon/Makefile b/tools/gator/daemon/Makefile index 24ee94045470..2ed49fdb688b 100644 --- a/tools/gator/daemon/Makefile +++ b/tools/gator/daemon/Makefile @@ -8,14 +8,14 @@ # targets run 'make SOFTFLOAT=1 SYSROOT=/path/to/sysroot', see # README_Streamline.txt for more details -CPP = $(CROSS_COMPILE)g++ -GCC = $(CROSS_COMPILE)gcc +CC = $(CROSS_COMPILE)gcc +CXX = $(CROSS_COMPILE)g++ # -mthumb-interwork is required for interworking to ARM or Thumb stdlibc -CFLAGS += -mthumb-interwork +CPPFLAGS += -mthumb-interwork ifeq ($(SOFTFLOAT),1) - CFLAGS += -marm -march=armv4t -mfloat-abi=soft + CPPFLAGS += -marm -march=armv4t -mfloat-abi=soft LDFLAGS += -marm -march=armv4t -mfloat-abi=soft endif ifneq ($(SYSROOT),) diff --git a/tools/gator/daemon/Makefile_aarch64 b/tools/gator/daemon/Makefile_aarch64 index 10b4b4a71ab1..efd1fa002182 100644 --- a/tools/gator/daemon/Makefile_aarch64 +++ b/tools/gator/daemon/Makefile_aarch64 @@ -4,12 +4,9 @@ # # Uncomment and define CROSS_COMPILE if it is not already defined -# CROSS_COMPILE=/path/to/cross-compiler/arm-linux-gnueabihf- -# NOTE: This toolchain uses the hardfloat abi by default. For non-hardfloat -# targets it is necessary to add options -# '-marm -march=armv4t -mfloat-abi=soft'. +# CROSS_COMPILE=/path/to/cross-compiler/aarch64-linux-gnu- -CPP = $(CROSS_COMPILE)g++ -GCC = $(CROSS_COMPILE)gcc +CC = $(CROSS_COMPILE)gcc +CXX = $(CROSS_COMPILE)g++ include common.mk diff --git a/tools/gator/daemon/MaliVideoDriver.cpp b/tools/gator/daemon/MaliVideoDriver.cpp new file mode 100644 index 000000000000..18b413b01a37 --- /dev/null +++ b/tools/gator/daemon/MaliVideoDriver.cpp @@ -0,0 +1,253 @@ +/** + * Copyright (C) ARM Limited 2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "MaliVideoDriver.h" + +#include + +#include "Buffer.h" +#include "Counter.h" +#include "Logging.h" +#include "SessionData.h" + +// From instr/src/mve_instr_comm_protocol.h +typedef enum mve_instr_configuration_type { + MVE_INSTR_RAW = 1 << 0, + MVE_INSTR_COUNTERS = 1 << 1, + MVE_INSTR_EVENTS = 1 << 2, + MVE_INSTR_ACTIVITIES = 1 << 3, + + // Raw always pushed regardless + MVE_INSTR_PULL = 1 << 12, + // Raw always unpacked regardless + MVE_INSTR_PACKED_COMM = 1 << 13, + // Don’t send ACKt response + MVE_INSTR_NO_AUTO_ACK = 1 << 14, +} mve_instr_configuration_type_t; + +static const char COUNTER[] = "ARM_Mali-V500_cnt"; +static const char EVENT[] = "ARM_Mali-V500_evn"; +static const char ACTIVITY[] = "ARM_Mali-V500_act"; + +class MaliVideoCounter { +public: + MaliVideoCounter(MaliVideoCounter *next, const char *name, const MaliVideoCounterType type, const int id) : mNext(next), mName(name), mType(type), mId(id), mKey(getEventKey()), mEnabled(false) { + } + + ~MaliVideoCounter() { + delete mName; + } + + MaliVideoCounter *getNext() const { return mNext; } + const char *getName() const { return mName; } + MaliVideoCounterType getType() const { return mType; } + int getId() const { return mId; } + int getKey() const { return mKey; } + bool isEnabled() const { return mEnabled; } + void setEnabled(const bool enabled) { mEnabled = enabled; } + +private: + MaliVideoCounter *const mNext; + const char *const mName; + const MaliVideoCounterType mType; + // Mali Video id + const int mId; + // Streamline key + const int mKey; + bool mEnabled; +}; + +MaliVideoDriver::MaliVideoDriver() : mCounters(NULL), mActivityCount(0) { +} + +MaliVideoDriver::~MaliVideoDriver() { + while (mCounters != NULL) { + MaliVideoCounter *counter = mCounters; + mCounters = counter->getNext(); + delete counter; + } +} + +void MaliVideoDriver::setup(mxml_node_t *const xml) { + // hwmon does not currently work with perf + if (gSessionData->perf.isSetup()) { + return; + } + + mxml_node_t *node = xml; + while (true) { + node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND); + if (node == NULL) { + break; + } + const char *counter = mxmlElementGetAttr(node, "counter"); + if (counter == NULL) { + // Ignore + } else if (strncmp(counter, COUNTER, sizeof(COUNTER) - 1) == 0) { + const int i = strtol(counter + sizeof(COUNTER) - 1, NULL, 10); + mCounters = new MaliVideoCounter(mCounters, strdup(counter), MVCT_COUNTER, i); + } else if (strncmp(counter, EVENT, sizeof(EVENT) - 1) == 0) { + const int i = strtol(counter + sizeof(EVENT) - 1, NULL, 10); + mCounters = new MaliVideoCounter(mCounters, strdup(counter), MVCT_EVENT, i); + } else if (strcmp(counter, ACTIVITY) == 0) { + mCounters = new MaliVideoCounter(mCounters, strdup(ACTIVITY), MVCT_ACTIVITY, 0); + mActivityCount = 0; + while (true) { + char buf[32]; + snprintf(buf, sizeof(buf), "activity%i", mActivityCount + 1); + if (mxmlElementGetAttr(node, buf) == NULL) { + break; + } + ++mActivityCount; + } + } + } +} + +MaliVideoCounter *MaliVideoDriver::findCounter(const Counter &counter) const { + for (MaliVideoCounter *maliVideoCounter = mCounters; maliVideoCounter != NULL; maliVideoCounter = maliVideoCounter->getNext()) { + if (strcmp(maliVideoCounter->getName(), counter.getType()) == 0) { + return maliVideoCounter; + } + } + + return NULL; +} + +bool MaliVideoDriver::claimCounter(const Counter &counter) const { + return findCounter(counter) != NULL; +} + +bool MaliVideoDriver::countersEnabled() const { + for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled()) { + return true; + } + } + return false; +} + +void MaliVideoDriver::resetCounters() { + for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + counter->setEnabled(false); + } +} + +void MaliVideoDriver::setupCounter(Counter &counter) { + MaliVideoCounter *const maliVideoCounter = findCounter(counter); + if (maliVideoCounter == NULL) { + counter.setEnabled(false); + return; + } + maliVideoCounter->setEnabled(true); + counter.setKey(maliVideoCounter->getKey()); +} + +int MaliVideoDriver::writeCounters(mxml_node_t *root) const { + if (access("/dev/mv500", F_OK) != 0) { + return 0; + } + + int count = 0; + for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + mxml_node_t *node = mxmlNewElement(root, "counter"); + mxmlElementSetAttr(node, "name", counter->getName()); + ++count; + } + + return count; +} + +void MaliVideoDriver::marshalEnable(const MaliVideoCounterType type, char *const buf, const size_t bufsize, int &pos) { + // size + int numEnabled = 0; + for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled() && (counter->getType() == type)) { + ++numEnabled; + } + } + Buffer::packInt(buf, bufsize, pos, numEnabled*sizeof(uint32_t)); + for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled() && (counter->getType() == type)) { + Buffer::packInt(buf, bufsize, pos, counter->getId()); + } + } +} + +bool MaliVideoDriver::start(const int mveUds) { + char buf[256]; + int pos = 0; + + // code - MVE_INSTR_STARTUP + buf[pos++] = 'C'; + buf[pos++] = 'L'; + buf[pos++] = 'N'; + buf[pos++] = 'T'; + // size + Buffer::packInt(buf, sizeof(buf), pos, sizeof(uint32_t)); + // client_version_number + Buffer::packInt(buf, sizeof(buf), pos, 1); + + // code - MVE_INSTR_CONFIGURE + buf[pos++] = 'C'; + buf[pos++] = 'N'; + buf[pos++] = 'F'; + buf[pos++] = 'G'; + // size + Buffer::packInt(buf, sizeof(buf), pos, 5*sizeof(uint32_t)); + // configuration + Buffer::packInt(buf, sizeof(buf), pos, MVE_INSTR_COUNTERS | MVE_INSTR_EVENTS | MVE_INSTR_ACTIVITIES | MVE_INSTR_PACKED_COMM); + // communication_protocol_version + Buffer::packInt(buf, sizeof(buf), pos, 1); + // data_protocol_version + Buffer::packInt(buf, sizeof(buf), pos, 1); + // sample_rate - convert samples/second to ms/sample + Buffer::packInt(buf, sizeof(buf), pos, 1000/gSessionData->mSampleRate); + // live_rate - convert ns/flush to ms/flush + Buffer::packInt(buf, sizeof(buf), pos, gSessionData->mLiveRate/1000000); + + // code - MVE_INSTR_ENABLE_COUNTERS + buf[pos++] = 'C'; + buf[pos++] = 'F'; + buf[pos++] = 'G'; + buf[pos++] = 'c'; + marshalEnable(MVCT_COUNTER, buf, sizeof(buf), pos); + + // code - MVE_INSTR_ENABLE_EVENTS + buf[pos++] = 'C'; + buf[pos++] = 'F'; + buf[pos++] = 'G'; + buf[pos++] = 'e'; + marshalEnable(MVCT_EVENT, buf, sizeof(buf), pos); + + /* + // code - MVE_INSTR_ENABLE_ACTIVITIES + buf[pos++] = 'C'; + buf[pos++] = 'F'; + buf[pos++] = 'G'; + buf[pos++] = 'a'; + // size + Buffer::packInt(buf, sizeof(buf), pos, mActivityCount*sizeof(uint32_t)); + for (int i = 0; i < mActivityCount; ++i) { + // activity_id + Buffer::packInt(buf, sizeof(buf), pos, i); + } + */ + + int written = 0; + while (written < pos) { + size_t bytes = ::write(mveUds, buf + written, pos - written); + if (bytes <= 0) { + logg->logMessage("%s(%s:%i): write failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + written += bytes; + } + + return true; +} diff --git a/tools/gator/daemon/MaliVideoDriver.h b/tools/gator/daemon/MaliVideoDriver.h new file mode 100644 index 000000000000..00cb80889a74 --- /dev/null +++ b/tools/gator/daemon/MaliVideoDriver.h @@ -0,0 +1,50 @@ +/** + * Copyright (C) ARM Limited 2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MALIVIDEODRIVER_H +#define MALIVIDEODRIVER_H + +#include "Driver.h" + +class MaliVideoCounter; + +enum MaliVideoCounterType { + MVCT_COUNTER, + MVCT_EVENT, + MVCT_ACTIVITY, +}; + +class MaliVideoDriver : public Driver { +public: + MaliVideoDriver(); + ~MaliVideoDriver(); + + void setup(mxml_node_t *const xml); + + bool claimCounter(const Counter &counter) const; + bool countersEnabled() const; + void resetCounters(); + void setupCounter(Counter &counter); + + int writeCounters(mxml_node_t *root) const; + + bool start(const int mveUds); + +private: + MaliVideoCounter *findCounter(const Counter &counter) const; + void marshalEnable(const MaliVideoCounterType type, char *const buf, const size_t bufsize, int &pos); + + MaliVideoCounter *mCounters; + int mActivityCount; + + // Intentionally unimplemented + MaliVideoDriver(const MaliVideoDriver &); + MaliVideoDriver &operator=(const MaliVideoDriver &); +}; + +#endif // MALIVIDEODRIVER_H diff --git a/tools/gator/daemon/Monitor.cpp b/tools/gator/daemon/Monitor.cpp index 90d5c47706c7..b34a15f0eb0c 100644 --- a/tools/gator/daemon/Monitor.cpp +++ b/tools/gator/daemon/Monitor.cpp @@ -18,8 +18,15 @@ Monitor::Monitor() : mFd(-1) { } Monitor::~Monitor() { - if (mFd >= -1) { - close(mFd); + if (mFd >= 0) { + ::close(mFd); + } +} + +void Monitor::close() { + if (mFd >= 0) { + ::close(mFd); + mFd = -1; } } diff --git a/tools/gator/daemon/Monitor.h b/tools/gator/daemon/Monitor.h index 6e268b6e1bed..7194e0e4ca50 100644 --- a/tools/gator/daemon/Monitor.h +++ b/tools/gator/daemon/Monitor.h @@ -16,6 +16,7 @@ class Monitor { Monitor(); ~Monitor(); + void close(); bool init(); bool add(const int fd); int wait(struct epoll_event *const events, int maxevents, int timeout); diff --git a/tools/gator/daemon/OlySocket.cpp b/tools/gator/daemon/OlySocket.cpp index 26e4768f3934..28774e36e510 100644 --- a/tools/gator/daemon/OlySocket.cpp +++ b/tools/gator/daemon/OlySocket.cpp @@ -9,6 +9,7 @@ #include "OlySocket.h" #include +#include #ifdef WIN32 #include #include @@ -43,16 +44,18 @@ OlyServerSocket::OlyServerSocket(int port) { createServerSocket(port); } -OlySocket::OlySocket(int port, const char* host) { - createClientSocket(host, port); -} - OlySocket::OlySocket(int socketID) : mSocketID(socketID) { } #ifndef WIN32 -OlyServerSocket::OlyServerSocket(const char* path) { +#define MIN(A, B) ({ \ + const __typeof__(A) __a = A; \ + const __typeof__(B) __b = B; \ + __a > __b ? __b : __a; \ +}) + +OlyServerSocket::OlyServerSocket(const char* path, const size_t pathSize) { // Create socket mFDServer = socket(PF_UNIX, SOCK_STREAM, 0); if (mFDServer < 0) { @@ -60,13 +63,11 @@ OlyServerSocket::OlyServerSocket(const char* path) { handleException(); } - unlink(path); - // Create sockaddr_in structure, ensuring non-populated fields are zero struct sockaddr_un sockaddr; memset((void*)&sockaddr, 0, sizeof(sockaddr)); sockaddr.sun_family = AF_UNIX; - strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1); + memcpy(sockaddr.sun_path, path, MIN(pathSize, sizeof(sockaddr.sun_path))); sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; // Bind the socket to an address @@ -82,24 +83,25 @@ OlyServerSocket::OlyServerSocket(const char* path) { } } -OlySocket::OlySocket(const char* path) { - mSocketID = socket(PF_UNIX, SOCK_STREAM, 0); - if (mSocketID < 0) { - return; +int OlySocket::connect(const char* path, const size_t pathSize) { + int fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + return -1; } // Create sockaddr_in structure, ensuring non-populated fields are zero struct sockaddr_un sockaddr; memset((void*)&sockaddr, 0, sizeof(sockaddr)); sockaddr.sun_family = AF_UNIX; - strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1); + memcpy(sockaddr.sun_path, path, MIN(pathSize, sizeof(sockaddr.sun_path))); sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; - if (connect(mSocketID, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { - close(mSocketID); - mSocketID = -1; - return; + if (::connect(fd, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { + close(fd); + return -1; } + + return fd; } #endif @@ -137,47 +139,6 @@ void OlyServerSocket::closeServerSocket() { mFDServer = 0; } -void OlySocket::createClientSocket(const char* hostname, int portno) { -#ifdef WIN32 - // TODO: Implement for Windows -#else - char buf[32]; - struct addrinfo hints, *res, *res0; - - snprintf(buf, sizeof(buf), "%d", portno); - mSocketID = -1; - memset((void*)&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - if (getaddrinfo(hostname, buf, &hints, &res0)) { - logg->logError(__FILE__, __LINE__, "Client socket failed to get address info for %s", hostname); - handleException(); - } - for (res=res0; res!=NULL; res = res->ai_next) { - if ( res->ai_family != PF_INET || res->ai_socktype != SOCK_STREAM ) { - continue; - } - mSocketID = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (mSocketID < 0) { - continue; - } - if (connect(mSocketID, res->ai_addr, res->ai_addrlen) < 0) { - close(mSocketID); - mSocketID = -1; - } - if (mSocketID > 0) { - break; - } - } - freeaddrinfo(res0); - if (mSocketID <= 0) { - logg->logError(__FILE__, __LINE__, "Could not connect to client socket. Ensure ARM Streamline is running."); - handleException(); - } -#endif -} - void OlyServerSocket::createServerSocket(int port) { int family = AF_INET6; diff --git a/tools/gator/daemon/OlySocket.h b/tools/gator/daemon/OlySocket.h index eab786b304bf..20c67cc695e1 100644 --- a/tools/gator/daemon/OlySocket.h +++ b/tools/gator/daemon/OlySocket.h @@ -9,13 +9,15 @@ #ifndef __OLY_SOCKET_H__ #define __OLY_SOCKET_H__ +#include + class OlySocket { public: - OlySocket(int port, const char* hostname); - OlySocket(int socketID); #ifndef WIN32 - OlySocket(const char* path); + static int connect(const char* path, const size_t pathSize); #endif + + OlySocket(int socketID); ~OlySocket(); void closeSocket(); @@ -29,21 +31,21 @@ class OlySocket { private: int mSocketID; - - void createClientSocket(const char* hostname, int port); }; class OlyServerSocket { public: OlyServerSocket(int port); #ifndef WIN32 - OlyServerSocket(const char* path); + OlyServerSocket(const char* path, const size_t pathSize); #endif ~OlyServerSocket(); int acceptConnection(); void closeServerSocket(); + int getFd() { return mFDServer; } + private: int mFDServer; diff --git a/tools/gator/daemon/PerfDriver.cpp b/tools/gator/daemon/PerfDriver.cpp index 8e25c22f6798..ac97a077d266 100644 --- a/tools/gator/daemon/PerfDriver.cpp +++ b/tools/gator/daemon/PerfDriver.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "Buffer.h" #include "Config.h" @@ -30,7 +31,7 @@ struct gator_cpu { const int cpuid; // Human readable name - const char core_name[32]; + const char *const core_name; // gatorfs event and Perf PMU name const char *const pmnc_name; const int pmnc_counters; @@ -62,9 +63,20 @@ static const struct gator_cpu gator_cpus[] = { static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-"; static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_"; +struct uncore_counter { + // gatorfs event and Perf PMU name + const char *const name; + const int count; +}; + +static const struct uncore_counter uncore_counters[] = { + { "CCI_400", 4 }, + { "CCI_400-r1", 4 }, +}; + class PerfCounter { public: - PerfCounter(PerfCounter *next, const char *name, uint32_t type, uint64_t config) : mNext(next), mName(name), mType(type), mCount(0), mKey(getEventKey()), mConfig(config), mEnabled(false) {} + PerfCounter(PerfCounter *next, const char *name, uint32_t type, uint64_t config, bool perCpu) : mNext(next), mName(name), mType(type), mCount(0), mKey(getEventKey()), mConfig(config), mEnabled(false), mPerCpu(perCpu) {} ~PerfCounter() { delete [] mName; } @@ -79,6 +91,7 @@ public: void setConfig(const uint64_t config) { mConfig = config; } bool isEnabled() const { return mEnabled; } void setEnabled(const bool enabled) { mEnabled = enabled; } + bool isPerCpu() const { return mPerCpu; } private: PerfCounter *const mNext; @@ -87,10 +100,11 @@ private: int mCount; const int mKey; uint64_t mConfig; - bool mEnabled; + int mEnabled : 1, + mPerCpu : 1; }; -PerfDriver::PerfDriver() : mCounters(NULL), mIsSetup(false) { +PerfDriver::PerfDriver() : mCounters(NULL), mIsSetup(false), mLegacySupport(false) { } PerfDriver::~PerfDriver() { @@ -105,13 +119,27 @@ void PerfDriver::addCpuCounters(const char *const counterName, const int type, c int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1; char *name = new char[len]; snprintf(name, len, "%s_ccnt", counterName); - mCounters = new PerfCounter(mCounters, name, type, -1); + mCounters = new PerfCounter(mCounters, name, type, -1, true); for (int j = 0; j < numCounters; ++j) { len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1; name = new char[len]; snprintf(name, len, "%s_cnt%d", counterName, j); - mCounters = new PerfCounter(mCounters, name, type, -1); + mCounters = new PerfCounter(mCounters, name, type, -1, true); + } +} + +void PerfDriver::addUncoreCounters(const char *const counterName, const int type, const int numCounters) { + int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1; + char *name = new char[len]; + snprintf(name, len, "%s_ccnt", counterName); + mCounters = new PerfCounter(mCounters, name, type, -1, false); + + for (int j = 0; j < numCounters; ++j) { + len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1; + name = new char[len]; + snprintf(name, len, "%s_cnt%d", counterName, j); + mCounters = new PerfCounter(mCounters, name, type, -1, false); } } @@ -139,10 +167,16 @@ bool PerfDriver::setup() { } } - if (KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 12, 0)) { + if (KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 4, 0)) { logg->logMessage("%s(%s:%i): Unsupported kernel version", __FUNCTION__, __FILE__, __LINE__); return false; } + mLegacySupport = KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 12, 0); + + if (access(EVENTS_PATH, R_OK) != 0) { + logg->logMessage("%s(%s:%i): " EVENTS_PATH " does not exist, is CONFIG_TRACING enabled?", __FUNCTION__, __FILE__, __LINE__); + return false; + } // Add supported PMUs bool foundCpu = false; @@ -174,6 +208,21 @@ bool PerfDriver::setup() { foundCpu = true; addCpuCounters(gator_cpus[i].pmnc_name, type, gator_cpus[i].pmnc_counters); } + + for (int i = 0; i < ARRAY_LENGTH(uncore_counters); ++i) { + if (strcmp(dirent->d_name, uncore_counters[i].name) != 0) { + continue; + } + + int type; + char buf[256]; + snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name); + if (DriverSource::readIntDriver(buf, &type) != 0) { + continue; + } + + addUncoreCounters(uncore_counters[i].name, type, uncore_counters[i].count); + } } closedir(dir); @@ -203,12 +252,12 @@ bool PerfDriver::setup() { id = getTracepointId("irq/softirq_exit", &printb); if (id >= 0) { - mCounters = new PerfCounter(mCounters, "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id); + mCounters = new PerfCounter(mCounters, "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id, true); } id = getTracepointId("irq/irq_handler_exit", &printb); if (id >= 0) { - mCounters = new PerfCounter(mCounters, "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id); + mCounters = new PerfCounter(mCounters, "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id, true); } //Linux_block_rq_wr @@ -218,7 +267,7 @@ bool PerfDriver::setup() { id = getTracepointId(SCHED_SWITCH, &printb); if (id >= 0) { - mCounters = new PerfCounter(mCounters, "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id); + mCounters = new PerfCounter(mCounters, "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id, true); } //Linux_meminfo_memused @@ -227,7 +276,7 @@ bool PerfDriver::setup() { //Linux_power_cpu_freq //Linux_power_cpu_idle - mCounters = new PerfCounter(mCounters, "Linux_cpu_wait_contention", TYPE_DERIVED, -1); + mCounters = new PerfCounter(mCounters, "Linux_cpu_wait_contention", TYPE_DERIVED, -1, false); //Linux_cpu_wait_io @@ -252,15 +301,16 @@ bool PerfDriver::summary(Buffer *const buffer) { } const int64_t timestamp = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec; - if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { - logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__); - return false; - } - const int64_t uptime = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec; + const int64_t uptime = getTime(); buffer->summary(timestamp, uptime, 0, buf); for (int i = 0; i < gSessionData->mCores; ++i) { + // Don't send information on a cpu we know nothing about + if (gSessionData->mCpuIds[i] == -1) { + continue; + } + int j; for (j = 0; j < ARRAY_LENGTH(gator_cpus); ++j) { if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) { @@ -270,7 +320,11 @@ bool PerfDriver::summary(Buffer *const buffer) { if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) { buffer->coreName(i, gSessionData->mCpuIds[i], gator_cpus[j].core_name); } else { - snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", gSessionData->mCpuIds[i]); + if (gSessionData->mCpuIds[i] == -1) { + snprintf(buf, sizeof(buf), "Unknown"); + } else { + snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", gSessionData->mCpuIds[i]); + } buffer->coreName(i, gSessionData->mCpuIds[i], buf); } } @@ -326,10 +380,10 @@ int PerfDriver::writeCounters(mxml_node_t *root) const { return count; } -bool PerfDriver::enable(PerfGroup *group, Buffer *const buffer) const { +bool PerfDriver::enable(PerfGroup *const group, Buffer *const buffer) const { for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED)) { - if (!group->add(buffer, counter->getKey(), counter->getType(), counter->getConfig(), counter->getCount(), 0, 0)) { + if (!group->add(buffer, counter->getKey(), counter->getType(), counter->getConfig(), counter->getCount(), counter->getCount() > 0 ? PERF_SAMPLE_TID | PERF_SAMPLE_IP : 0, counter->isPerCpu() ? PERF_GROUP_PER_CPU : 0)) { logg->logMessage("%s(%s:%i): PerfGroup::add failed", __FUNCTION__, __FILE__, __LINE__); return false; } diff --git a/tools/gator/daemon/PerfDriver.h b/tools/gator/daemon/PerfDriver.h index 3181b74f5570..2cae575a7059 100644 --- a/tools/gator/daemon/PerfDriver.h +++ b/tools/gator/daemon/PerfDriver.h @@ -27,6 +27,8 @@ class PerfDriver : public Driver { PerfDriver(); ~PerfDriver(); + bool getLegacySupport() const { return mLegacySupport; } + bool setup(); bool summary(Buffer *const buffer); bool isSetup() const { return mIsSetup; } @@ -37,16 +39,18 @@ class PerfDriver : public Driver { int writeCounters(mxml_node_t *root) const; - bool enable(PerfGroup *group, Buffer *const buffer) const; + bool enable(PerfGroup *const group, Buffer *const buffer) const; static long long getTracepointId(const char *const name, DynBuf *const printb); private: PerfCounter *findCounter(const Counter &counter) const; void addCpuCounters(const char *const counterName, const int type, const int numCounters); + void addUncoreCounters(const char *const counterName, const int type, const int numCounters); PerfCounter *mCounters; bool mIsSetup; + bool mLegacySupport; // Intentionally undefined PerfDriver(const PerfDriver &); diff --git a/tools/gator/daemon/PerfGroup.cpp b/tools/gator/daemon/PerfGroup.cpp index faf5fcaf15e6..2a0239f7c348 100644 --- a/tools/gator/daemon/PerfGroup.cpp +++ b/tools/gator/daemon/PerfGroup.cpp @@ -23,7 +23,9 @@ #define DEFAULT_PEA_ARGS(pea, additionalSampleType) \ pea.size = sizeof(pea); \ /* Emit time, read_format below, group leader id, and raw tracepoint info */ \ - pea.sample_type = PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_IDENTIFIER | additionalSampleType; \ + pea.sample_type = (gSessionData->perf.getLegacySupport() \ + ? PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_ID \ + : PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_IDENTIFIER ) | additionalSampleType; \ /* Emit emit value in group format */ \ pea.read_format = PERF_FORMAT_ID | PERF_FORMAT_GROUP; \ /* start out disabled */ \ @@ -39,6 +41,7 @@ static int sys_perf_event_open(struct perf_event_attr *const attr, const pid_t p PerfGroup::PerfGroup(PerfBuffer *const pb) : mPb(pb) { memset(&mAttrs, 0, sizeof(mAttrs)); + memset(&mPerCpu, 0, sizeof(mPerCpu)); memset(&mKeys, -1, sizeof(mKeys)); memset(&mFds, -1, sizeof(mFds)); } @@ -75,6 +78,7 @@ bool PerfGroup::add(Buffer *const buffer, const int key, const __u32 type, const mAttrs[i].freq = (flags & PERF_GROUP_FREQ ? 1 : 0); mAttrs[i].task = (flags & PERF_GROUP_TASK ? 1 : 0); mAttrs[i].sample_id_all = (flags & PERF_GROUP_SAMPLE_ID_ALL ? 1 : 0); + mPerCpu[i] = (flags & PERF_GROUP_PER_CPU); mKeys[i] = key; @@ -91,13 +95,17 @@ bool PerfGroup::prepareCPU(const int cpu) { continue; } + if ((cpu != 0) && !mPerCpu[i]) { + continue; + } + const int offset = i * gSessionData->mCores; if (mFds[cpu + offset] >= 0) { logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__); return false; } - logg->logMessage("%s(%s:%i): perf_event_open cpu: %i type: %lli config: %lli sample: %lli sample_type: %lli", __FUNCTION__, __FILE__, __LINE__, cpu, (long long)mAttrs[i].type, (long long)mAttrs[i].config, (long long)mAttrs[i].sample_period, (long long)mAttrs[i].sample_type); + logg->logMessage("%s(%s:%i): perf_event_open cpu: %i type: %lli config: %lli sample: %lli sample_type: 0x%llx pinned: %i mmap: %i comm: %i freq: %i task: %i sample_id_all: %i", __FUNCTION__, __FILE__, __LINE__, cpu, (long long)mAttrs[i].type, (long long)mAttrs[i].config, (long long)mAttrs[i].sample_period, (long long)mAttrs[i].sample_type, mAttrs[i].pinned, mAttrs[i].mmap, mAttrs[i].comm, mAttrs[i].freq, mAttrs[i].task, mAttrs[i].sample_id_all); mFds[cpu + offset] = sys_perf_event_open(&mAttrs[i], -1, cpu, i == 0 ? -1 : mFds[cpu], i == 0 ? 0 : PERF_FLAG_FD_OUTPUT); if (mFds[cpu + offset] < 0) { logg->logMessage("%s(%s:%i): failed %s", __FUNCTION__, __FILE__, __LINE__, strerror(errno)); @@ -125,7 +133,9 @@ int PerfGroup::onlineCPU(const int cpu, const bool start, Buffer *const buffer, } coreKeys[idCount] = mKeys[i]; - if (ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0) { + if (!gSessionData->perf.getLegacySupport() && ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0 && + // Workaround for running 32-bit gatord on 64-bit systems, kernel patch in the works + ioctl(fd, (PERF_EVENT_IOC_ID & ~IOCSIZE_MASK) | (8 << _IOC_SIZESHIFT), &ids[idCount]) != 0) { logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); return false; } @@ -137,7 +147,17 @@ int PerfGroup::onlineCPU(const int cpu, const bool start, Buffer *const buffer, return false; } - buffer->keys(idCount, ids, coreKeys); + if (!gSessionData->perf.getLegacySupport()) { + buffer->keys(idCount, ids, coreKeys); + } else { + char buf[1024]; + ssize_t bytes = read(mFds[cpu], buf, sizeof(buf)); + if (bytes < 0) { + logg->logMessage("read failed"); + return false; + } + buffer->keysOld(idCount, coreKeys, bytes, buf); + } if (start) { for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { diff --git a/tools/gator/daemon/PerfGroup.h b/tools/gator/daemon/PerfGroup.h index af496d41334c..3f1e2bb4d1c8 100644 --- a/tools/gator/daemon/PerfGroup.h +++ b/tools/gator/daemon/PerfGroup.h @@ -24,6 +24,7 @@ enum PerfGroupFlags { PERF_GROUP_FREQ = 1 << 2, PERF_GROUP_TASK = 1 << 3, PERF_GROUP_SAMPLE_ID_ALL = 1 << 4, + PERF_GROUP_PER_CPU = 1 << 5, }; class PerfGroup { @@ -43,6 +44,7 @@ class PerfGroup { private: // +1 for the group leader struct perf_event_attr mAttrs[MAX_PERFORMANCE_COUNTERS + 1]; + bool mPerCpu[MAX_PERFORMANCE_COUNTERS + 1]; int mKeys[MAX_PERFORMANCE_COUNTERS + 1]; int mFds[NR_CPUS * (MAX_PERFORMANCE_COUNTERS + 1)]; PerfBuffer *const mPb; diff --git a/tools/gator/daemon/PerfSource.cpp b/tools/gator/daemon/PerfSource.cpp index 1f1cb1988f00..ecfaa66832bd 100644 --- a/tools/gator/daemon/PerfSource.cpp +++ b/tools/gator/daemon/PerfSource.cpp @@ -37,7 +37,7 @@ static bool sendTracepointFormat(Buffer *const buffer, const char *const name, D return true; } -PerfSource::PerfSource(sem_t *senderSem, sem_t *startProfile) : mSummary(0, FRAME_SUMMARY, 1024, senderSem), mBuffer(0, FRAME_PERF_ATTRS, 1024*1024, senderSem), mCountersBuf(), mCountersGroup(&mCountersBuf), mMonitor(), mUEvent(), mSenderSem(senderSem), mStartProfile(startProfile), mInterruptFd(-1), mIsDone(false) { +PerfSource::PerfSource(sem_t *senderSem, sem_t *startProfile) : mSummary(0, FRAME_SUMMARY, 1024, senderSem), mBuffer(0, FRAME_PERF_ATTRS, 4*1024*1024, senderSem), mCountersBuf(), mCountersGroup(&mCountersBuf), mMonitor(), mUEvent(), mSenderSem(senderSem), mStartProfile(startProfile), mInterruptFd(-1), mIsDone(false) { long l = sysconf(_SC_PAGE_SIZE); if (l < 0) { logg->logError(__FILE__, __LINE__, "Unable to obtain the page size"); @@ -74,6 +74,9 @@ bool PerfSource::prepare() { DynBuf b3; long long schedSwitchId; + // Reread cpuinfo since cores may have changed since startup + gSessionData->readCpuInfo(); + if (0 || !mMonitor.init() || !mUEvent.init() @@ -83,14 +86,14 @@ bool PerfSource::prepare() { || !sendTracepointFormat(&mBuffer, SCHED_SWITCH, &printb, &b1) // Only want RAW but not IP on sched_switch and don't want TID on SAMPLE_ID - || !mCountersGroup.add(&mBuffer, 100/**/, PERF_TYPE_TRACEPOINT, schedSwitchId, 1, PERF_SAMPLE_RAW, PERF_GROUP_MMAP | PERF_GROUP_COMM | PERF_GROUP_TASK | PERF_GROUP_SAMPLE_ID_ALL) + || !mCountersGroup.add(&mBuffer, 100/**/, PERF_TYPE_TRACEPOINT, schedSwitchId, 1, PERF_SAMPLE_RAW, PERF_GROUP_MMAP | PERF_GROUP_COMM | PERF_GROUP_TASK | PERF_GROUP_SAMPLE_ID_ALL | PERF_GROUP_PER_CPU) // Only want TID and IP but not RAW on timer - || (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && !mCountersGroup.add(&mBuffer, 99/**/, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP, 0)) + || (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && !mCountersGroup.add(&mBuffer, 99/**/, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP, PERF_GROUP_PER_CPU)) || !gSessionData->perf.enable(&mCountersGroup, &mBuffer) || 0) { - logg->logMessage("%s(%s:%i): perf setup failed, are you running Linux 3.12 or later?", __FUNCTION__, __FILE__, __LINE__); + logg->logMessage("%s(%s:%i): perf setup failed, are you running Linux 3.4 or later?", __FUNCTION__, __FILE__, __LINE__); return false; } @@ -134,7 +137,7 @@ bool PerfSource::prepare() { return false; } - if (!readProc(&mBuffer, &printb, &b1, &b2, &b3)) { + if (!readProc(&mBuffer, true, &printb, &b1, &b2, &b3)) { logg->logMessage("%s(%s:%i): readProc failed", __FUNCTION__, __FILE__, __LINE__); return false; } @@ -260,6 +263,7 @@ bool PerfSource::isDone () { void PerfSource::write (Sender *sender) { if (!mSummary.isDone()) { mSummary.write(sender); + gSessionData->mSentSummary = true; } if (!mBuffer.isDone()) { mBuffer.write(sender); diff --git a/tools/gator/daemon/Proc.cpp b/tools/gator/daemon/Proc.cpp index e0b9e2259cf9..9f01770d6609 100644 --- a/tools/gator/daemon/Proc.cpp +++ b/tools/gator/daemon/Proc.cpp @@ -57,14 +57,57 @@ static bool readProcStat(ProcStat *const ps, const char *const pathname, DynBuf return true; } -static bool readProcTask(Buffer *const buffer, const int pid, const char *const image, DynBuf *const printb, DynBuf *const b) { +static const char *readProcExe(DynBuf *const printb, const int pid, const int tid, DynBuf *const b) { + if (tid == -1 ? !printb->printf("/proc/%i/exe", pid) + : !printb->printf("/proc/%i/task/%i/exe", pid, tid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return NULL; + } + + const int err = b->readlink(printb->getBuf()); + const char *image; + if (err == 0) { + image = strrchr(b->getBuf(), '/'); + if (image == NULL) { + image = b->getBuf(); + } else { + ++image; + } + } else if (err == -ENOENT) { + // readlink /proc/[pid]/exe returns ENOENT for kernel threads + image = "\0"; + } else { + logg->logMessage("%s(%s:%i): DynBuf::readlink failed", __FUNCTION__, __FILE__, __LINE__); + return NULL; + } + + // Android apps are run by app_process but the cmdline is changed to reference the actual app name + if (strcmp(image, "app_process") != 0) { + return image; + } + + if (tid == -1 ? !printb->printf("/proc/%i/cmdline", pid) + : !printb->printf("/proc/%i/task/%i/cmdline", pid, tid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return NULL; + } + + if (!b->read(printb->getBuf())) { + logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__); + return NULL; + } + + return b->getBuf(); +} + +static bool readProcTask(Buffer *const buffer, const int pid, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2) { bool result = false; - if (!b->printf("/proc/%i/task", pid)) { + if (!b1->printf("/proc/%i/task", pid)) { logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); return result; } - DIR *task = opendir(b->getBuf()); + DIR *task = opendir(b1->getBuf()); if (task == NULL) { logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__); return result; @@ -84,11 +127,17 @@ static bool readProcTask(Buffer *const buffer, const int pid, const char *const goto fail; } ProcStat ps; - if (!readProcStat(&ps, printb->getBuf(), b)) { + if (!readProcStat(&ps, printb->getBuf(), b1)) { logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__); goto fail; } + const char *const image = readProcExe(printb, pid, tid, b2); + if (image == NULL) { + logg->logMessage("%s(%s:%i): readImage failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + buffer->comm(pid, tid, image, ps.comm); } @@ -100,7 +149,7 @@ static bool readProcTask(Buffer *const buffer, const int pid, const char *const return result; } -bool readProc(Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3) { +bool readProc(Buffer *const buffer, bool sendMaps, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3) { bool result = false; DIR *proc = opendir("/proc"); @@ -128,42 +177,29 @@ bool readProc(Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynB goto fail; } - if (!printb->printf("/proc/%i/exe", pid)) { - logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); - goto fail; - } - const int err = b1->readlink(printb->getBuf()); - const char *image; - if (err == 0) { - image = strrchr(b1->getBuf(), '/'); - if (image == NULL) { - image = b1->getBuf(); - } else { - ++image; + if (sendMaps) { + if (!printb->printf("/proc/%i/maps", pid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + if (!b2->read(printb->getBuf())) { + logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the process exited", __FUNCTION__, __FILE__, __LINE__); + // This is not a fatal error - the process just doesn't exist any more + continue; } - } else if (err == -ENOENT) { - // readlink /proc/[pid]/exe returns ENOENT for kernel threads - image = "\0"; - } else { - logg->logMessage("%s(%s:%i): DynBuf::readlink failed", __FUNCTION__, __FILE__, __LINE__); - goto fail; - } - if (!printb->printf("/proc/%i/maps", pid)) { - logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); - goto fail; + buffer->maps(pid, pid, b2->getBuf()); } - if (!b2->read(printb->getBuf())) { - logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the process exited", __FUNCTION__, __FILE__, __LINE__); - // This is not a fatal error - the process just doesn't exist any more - continue; - } - - buffer->maps(pid, pid, b2->getBuf()); if (ps.numThreads <= 1) { + const char *const image = readProcExe(printb, pid, -1, b1); + if (image == NULL) { + logg->logMessage("%s(%s:%i): readImage failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + buffer->comm(pid, pid, image, ps.comm); } else { - if (!readProcTask(buffer, pid, image, printb, b3)) { + if (!readProcTask(buffer, pid, printb, b1, b3)) { logg->logMessage("%s(%s:%i): readProcTask failed", __FUNCTION__, __FILE__, __LINE__); goto fail; } diff --git a/tools/gator/daemon/Proc.h b/tools/gator/daemon/Proc.h index 057b6109848a..31c2eecb7aeb 100644 --- a/tools/gator/daemon/Proc.h +++ b/tools/gator/daemon/Proc.h @@ -12,6 +12,6 @@ class Buffer; class DynBuf; -bool readProc(Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3); +bool readProc(Buffer *const buffer, bool sendMaps, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3); #endif // PROC_H diff --git a/tools/gator/daemon/Sender.h b/tools/gator/daemon/Sender.h index 4c359dba82f8..33b6cc3c5d8d 100644 --- a/tools/gator/daemon/Sender.h +++ b/tools/gator/daemon/Sender.h @@ -39,4 +39,4 @@ class Sender { Sender &operator=(const Sender &); }; -#endif //__SENDER_H__ +#endif //__SENDER_H__ diff --git a/tools/gator/daemon/SessionData.cpp b/tools/gator/daemon/SessionData.cpp index c169299af872..14d995fc39fa 100644 --- a/tools/gator/daemon/SessionData.cpp +++ b/tools/gator/daemon/SessionData.cpp @@ -9,6 +9,7 @@ #include "SessionData.h" #include +#include #include "SessionXML.h" #include "Logging.h" @@ -27,6 +28,15 @@ void SessionData::initialize() { mSessionIsActive = false; mLocalCapture = false; mOneShot = false; + mSentSummary = false; + const size_t cpuIdSize = sizeof(int)*NR_CPUS; + // Share mCpuIds across all instances of gatord + mCpuIds = (int *)mmap(NULL, cpuIdSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (mCpuIds == MAP_FAILED) { + logg->logError(__FILE__, __LINE__, "Unable to mmap shared memory for cpuids"); + handleException(); + } + memset(mCpuIds, -1, cpuIdSize); readCpuInfo(); mConfigurationXMLPath = NULL; mSessionXMLPath = NULL; @@ -91,10 +101,9 @@ void SessionData::parseSessionXML(char* xmlString) { void SessionData::readCpuInfo() { char temp[256]; // arbitrarily large amount strcpy(mCoreName, "unknown"); - memset(&mCpuIds, -1, sizeof(mCpuIds)); mMaxCpuId = -1; - FILE* f = fopen("/proc/cpuinfo", "r"); + FILE* f = fopen("/proc/cpuinfo", "r"); if (f == NULL) { logg->logMessage("Error opening /proc/cpuinfo\n" "The core name in the captured xml file will be 'unknown'."); @@ -102,10 +111,18 @@ void SessionData::readCpuInfo() { } bool foundCoreName = false; - int processor = 0; + int processor = -1; while (fgets(temp, sizeof(temp), f)) { - if (strlen(temp) > 0) { - temp[strlen(temp) - 1] = 0; // Replace the line feed with a null + const size_t len = strlen(temp); + + if (len == 1) { + // New section, clear the processor. Streamline will not know the cpus if the pre Linux 3.8 format of cpuinfo is encountered but also that no incorrect information will be transmitted. + processor = -1; + continue; + } + + if (len > 0) { + temp[len - 1] = '\0'; // Replace the line feed with a null } const bool foundHardware = strstr(temp, "Hardware") != 0; @@ -127,10 +144,15 @@ void SessionData::readCpuInfo() { } if (foundCPUPart) { - mCpuIds[processor] = strtol(position, NULL, 0); + const int cpuId = strtol(position, NULL, 0); // If this does not have the full topology in /proc/cpuinfo, mCpuIds[0] may not have the 1 CPU part emitted - this guarantees it's in mMaxCpuId - if (mCpuIds[processor] > mMaxCpuId) { - mMaxCpuId = mCpuIds[processor]; + if (cpuId > mMaxCpuId) { + mMaxCpuId = cpuId; + } + if (processor >= NR_CPUS) { + logg->logMessage("Too many processors, please increase NR_CPUS"); + } else if (processor >= 0) { + mCpuIds[processor] = cpuId; } } @@ -142,10 +164,23 @@ void SessionData::readCpuInfo() { if (!foundCoreName) { logg->logMessage("Could not determine core name from /proc/cpuinfo\n" - "The core name in the captured xml file will be 'unknown'."); + "The core name in the captured xml file will be 'unknown'."); } fclose(f); - } +} + +uint64_t getTime() { + struct timespec ts; +#ifndef CLOCK_MONOTONIC_RAW + // Android doesn't have this defined but it was added in Linux 2.6.28 +#define CLOCK_MONOTONIC_RAW 4 +#endif + if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { + logg->logError(__FILE__, __LINE__, "Failed to get uptime"); + handleException(); + } + return (NS_PER_S*ts.tv_sec + ts.tv_nsec); +} int getEventKey() { // key 0 is reserved as a timestamp diff --git a/tools/gator/daemon/SessionData.h b/tools/gator/daemon/SessionData.h index ea34240e2df7..835082d86c4b 100644 --- a/tools/gator/daemon/SessionData.h +++ b/tools/gator/daemon/SessionData.h @@ -13,12 +13,16 @@ #include "Config.h" #include "Counter.h" +#include "FSDriver.h" #include "Hwmon.h" +#include "MaliVideoDriver.h" #include "PerfDriver.h" -#define PROTOCOL_VERSION 18 +#define PROTOCOL_VERSION 19 #define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions +#define NS_PER_S ((uint64_t)1000000000) + struct ImageLinkList { char* path; struct ImageLinkList *next; @@ -32,9 +36,12 @@ class SessionData { ~SessionData(); void initialize(); void parseSessionXML(char* xmlString); + void readCpuInfo(); Hwmon hwmon; + FSDriver fsDriver; PerfDriver perf; + MaliVideoDriver maliVideo; char mCoreName[MAX_STRING_LEN]; struct ImageLinkList *mImages; @@ -49,7 +56,8 @@ class SessionData { bool mLocalCapture; bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled bool mIsEBS; - + bool mSentSummary; + int mBacktraceDepth; int mTotalBufferSize; // number of MB to use for the entire collection buffer int mSampleRate; @@ -57,7 +65,7 @@ class SessionData { int mDuration; int mCores; int mPageSize; - int mCpuIds[NR_CPUS]; + int *mCpuIds; int mMaxCpuId; // PMU Counters @@ -65,8 +73,6 @@ class SessionData { Counter mCounters[MAX_PERFORMANCE_COUNTERS]; private: - void readCpuInfo(); - // Intentionally unimplemented SessionData(const SessionData &); SessionData &operator=(const SessionData &); @@ -74,6 +80,7 @@ class SessionData { extern SessionData* gSessionData; +uint64_t getTime(); int getEventKey(); #endif // SESSION_DATA_H diff --git a/tools/gator/daemon/SessionXML.cpp b/tools/gator/daemon/SessionXML.cpp index 55b2f9280709..8cdc9409ca21 100644 --- a/tools/gator/daemon/SessionXML.cpp +++ b/tools/gator/daemon/SessionXML.cpp @@ -17,15 +17,15 @@ #include "SessionData.h" static const char* TAG_SESSION = "session"; -static const char* TAG_IMAGE = "image"; +static const char* TAG_IMAGE = "image"; -static const char* ATTR_VERSION = "version"; +static const char* ATTR_VERSION = "version"; static const char* ATTR_CALL_STACK_UNWINDING = "call_stack_unwinding"; static const char* ATTR_BUFFER_MODE = "buffer_mode"; -static const char* ATTR_SAMPLE_RATE = "sample_rate"; +static const char* ATTR_SAMPLE_RATE = "sample_rate"; static const char* ATTR_DURATION = "duration"; static const char* ATTR_PATH = "path"; -static const char* ATTR_LIVE_RATE = "live_rate"; +static const char* ATTR_LIVE_RATE = "live_rate"; SessionXML::SessionXML(const char *str) { parameters.buffer_mode[0] = 0; diff --git a/tools/gator/daemon/StreamlineSetup.cpp b/tools/gator/daemon/StreamlineSetup.cpp index caa665e67193..2b61eaeb290d 100644 --- a/tools/gator/daemon/StreamlineSetup.cpp +++ b/tools/gator/daemon/StreamlineSetup.cpp @@ -266,7 +266,7 @@ void StreamlineSetup::writeConfiguration(char* xml) { { ConfigurationXML configuration; } if (gSessionData->mCounterOverflow > 0) { - logg->logError(__FILE__, __LINE__, "Only %i performance counters counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow); + logg->logError(__FILE__, __LINE__, "Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow); handleException(); } } diff --git a/tools/gator/daemon/StreamlineSetup.h b/tools/gator/daemon/StreamlineSetup.h index 74bb197e35ff..b380f46b98f0 100644 --- a/tools/gator/daemon/StreamlineSetup.h +++ b/tools/gator/daemon/StreamlineSetup.h @@ -21,7 +21,7 @@ enum { COMMAND_APC_START = 2, COMMAND_APC_STOP = 3, COMMAND_DISCONNECT = 4, - COMMAND_PING = 5 + COMMAND_PING = 5 }; class StreamlineSetup { @@ -47,4 +47,4 @@ class StreamlineSetup { StreamlineSetup &operator=(const StreamlineSetup &); }; -#endif //__STREAMLINE_SETUP_H__ +#endif //__STREAMLINE_SETUP_H__ diff --git a/tools/gator/daemon/UEvent.cpp b/tools/gator/daemon/UEvent.cpp index 282e965fa67a..54d45751e3c9 100644 --- a/tools/gator/daemon/UEvent.cpp +++ b/tools/gator/daemon/UEvent.cpp @@ -8,11 +8,12 @@ #include "UEvent.h" -#include #include #include #include +#include + #include "Logging.h" static const char EMPTY[] = ""; diff --git a/tools/gator/daemon/UserSpaceSource.cpp b/tools/gator/daemon/UserSpaceSource.cpp index debe69636cff..8c328e0e0fb5 100644 --- a/tools/gator/daemon/UserSpaceSource.cpp +++ b/tools/gator/daemon/UserSpaceSource.cpp @@ -16,7 +16,6 @@ #include "Logging.h" #include "SessionData.h" -#define NS_PER_S ((uint64_t)1000000000) #define NS_PER_US 1000 extern Child *child; @@ -35,6 +34,7 @@ void UserSpaceSource::run() { prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); gSessionData->hwmon.start(); + gSessionData->fsDriver.start(); int64_t monotonic_started = 0; while (monotonic_started <= 0) { @@ -48,16 +48,7 @@ void UserSpaceSource::run() { uint64_t next_time = 0; while (gSessionData->mSessionIsActive) { - struct timespec ts; -#ifndef CLOCK_MONOTONIC_RAW - // Android doesn't have this defined but it was added in Linux 2.6.28 -#define CLOCK_MONOTONIC_RAW 4 -#endif - if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { - logg->logError(__FILE__, __LINE__, "Failed to get uptime"); - handleException(); - } - const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started; + const uint64_t curr_time = getTime() - monotonic_started; // Sample ten times a second ignoring gSessionData->mSampleRate next_time += NS_PER_S/10;//gSessionData->mSampleRate; if (next_time < curr_time) { @@ -67,6 +58,7 @@ void UserSpaceSource::run() { if (mBuffer.eventHeader(curr_time)) { gSessionData->hwmon.read(&mBuffer); + gSessionData->fsDriver.read(&mBuffer); // Only check after writing all counters so that time and corresponding counters appear in the same frame mBuffer.check(curr_time); } diff --git a/tools/gator/daemon/UserSpaceSource.h b/tools/gator/daemon/UserSpaceSource.h index fb5889d26ffb..9b3666016dc5 100644 --- a/tools/gator/daemon/UserSpaceSource.h +++ b/tools/gator/daemon/UserSpaceSource.h @@ -14,7 +14,7 @@ #include "Buffer.h" #include "Source.h" -// User space counters - currently just hwmon +// User space counters class UserSpaceSource : public Source { public: UserSpaceSource(sem_t *senderSem); diff --git a/tools/gator/daemon/c++.cpp b/tools/gator/daemon/c++.cpp new file mode 100644 index 000000000000..6041e5e96469 --- /dev/null +++ b/tools/gator/daemon/c++.cpp @@ -0,0 +1,40 @@ +/** + * Minimal set of C++ functions so that libstdc++ is not required + * + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +void operator delete(void *ptr) { + if (ptr != NULL) { + free(ptr); + } +} + +void operator delete[](void *ptr) { + operator delete(ptr); +} + +void *operator new(size_t size) { + void *ptr = malloc(size == 0 ? 1 : size); + if (ptr == NULL) { + abort(); + } + return ptr; +} + +void *operator new[](size_t size) { + return operator new(size); +} + +extern "C" +void __cxa_pure_virtual() { + printf("pure virtual method called\n"); + abort(); +} diff --git a/tools/gator/daemon/common.mk b/tools/gator/daemon/common.mk index d9dc14606b07..769a92e51a35 100644 --- a/tools/gator/daemon/common.mk +++ b/tools/gator/daemon/common.mk @@ -5,16 +5,17 @@ # -Werror treats warnings as errors # -std=c++0x is the planned new c++ standard # -std=c++98 is the 1998 c++ standard -CFLAGS += -O3 -Wall -fno-exceptions -pthread -MMD -DETCDIR=\"/etc\" -Ilibsensors +CPPFLAGS += -O3 -Wall -fno-exceptions -pthread -MMD -DETCDIR=\"/etc\" -Ilibsensors CXXFLAGS += -fno-rtti -Wextra # -Weffc++ ifeq ($(WERROR),1) - CFLAGS += -Werror + CPPFLAGS += -Werror endif # -s strips the binary of debug info LDFLAGS += -s +LDLIBS += -lrt -lm -pthread TARGET = gatord C_SRC = $(wildcard mxml/*.c) $(wildcard libsensors/*.c) -CPP_SRC = $(wildcard *.cpp) +CXX_SRC = $(wildcard *.cpp) all: $(TARGET) @@ -35,14 +36,15 @@ libsensors/conf-parse.c: ; ./escape $< > $@ %.o: %.c - $(GCC) -c $(CFLAGS) -o $@ $< + $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< %.o: %.cpp - $(CPP) -c $(CFLAGS) $(CXXFLAGS) -o $@ $< + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< -$(TARGET): $(CPP_SRC:%.cpp=%.o) $(C_SRC:%.c=%.o) - $(CPP) $(LDFLAGS) -o $@ $^ -lrt -pthread +$(TARGET): $(CXX_SRC:%.cpp=%.o) $(C_SRC:%.c=%.o) + $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ +# Intentionally ignore CC as a native binary is required escape: escape.c gcc $^ -o $@ diff --git a/tools/gator/daemon/defaults.xml b/tools/gator/daemon/defaults.xml index 5bf096cb2a45..39a0f656f7e6 100644 --- a/tools/gator/daemon/defaults.xml +++ b/tools/gator/daemon/defaults.xml @@ -58,5 +58,10 @@ + + + + + diff --git a/tools/gator/daemon/escape.c b/tools/gator/daemon/escape.c index c54aa1c3e75d..2b0863aaf425 100644 --- a/tools/gator/daemon/escape.c +++ b/tools/gator/daemon/escape.c @@ -6,7 +6,7 @@ * published by the Free Software Foundation. */ -/* +/* * The Makefile in the daemon folder builds and executes 'escape' * 'escape' creates configuration_xml.h from configuration.xml and events_xml.h from events-*.xml * these genereated xml files are then #included and built as part of the gatord binary diff --git a/tools/gator/daemon/events-CCI-400.xml b/tools/gator/daemon/events-CCI-400.xml index 4fa77117d2d8..20002efd1543 100644 --- a/tools/gator/daemon/events-CCI-400.xml +++ b/tools/gator/daemon/events-CCI-400.xml @@ -1,7 +1,6 @@ - - - - + + + - @@ -30,13 +28,11 @@ - - @@ -45,11 +41,9 @@ - - - - - + + + - @@ -79,13 +72,11 @@ - - diff --git a/tools/gator/daemon/events-CCN-504.xml b/tools/gator/daemon/events-CCN-504.xml index cfabf65949ed..6ef3e6483717 100644 --- a/tools/gator/daemon/events-CCN-504.xml +++ b/tools/gator/daemon/events-CCN-504.xml @@ -1,7 +1,6 @@ - - @@ -56,7 +54,6 @@ - - @@ -82,7 +78,6 @@ - - @@ -102,14 +96,12 @@ - - @@ -118,5 +110,4 @@ - diff --git a/tools/gator/daemon/events-Cortex-A53.xml b/tools/gator/daemon/events-Cortex-A53.xml index 577dcd94185e..5ba17907d5ab 100644 --- a/tools/gator/daemon/events-Cortex-A53.xml +++ b/tools/gator/daemon/events-Cortex-A53.xml @@ -1,171 +1,87 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/gator/daemon/events-Cortex-A57.xml b/tools/gator/daemon/events-Cortex-A57.xml index b7178c0c7427..fbe96c2d4eb2 100644 --- a/tools/gator/daemon/events-Cortex-A57.xml +++ b/tools/gator/daemon/events-Cortex-A57.xml @@ -1,171 +1,87 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/gator/daemon/events-Filesystem.xml b/tools/gator/daemon/events-Filesystem.xml new file mode 100644 index 000000000000..5feeb9014a63 --- /dev/null +++ b/tools/gator/daemon/events-Filesystem.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/tools/gator/daemon/events-L2C-310.xml b/tools/gator/daemon/events-L2C-310.xml index 4da4d1d63431..923fb90334d0 100644 --- a/tools/gator/daemon/events-L2C-310.xml +++ b/tools/gator/daemon/events-L2C-310.xml @@ -1,18 +1,18 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Linux.xml b/tools/gator/daemon/events-Linux.xml index 4d677e15db7e..c306dd62208e 100644 --- a/tools/gator/daemon/events-Linux.xml +++ b/tools/gator/daemon/events-Linux.xml @@ -11,7 +11,6 @@ - - + + - diff --git a/tools/gator/daemon/events-Mali-4xx.xml b/tools/gator/daemon/events-Mali-4xx.xml index 5a71386830ba..0a95dfeb6485 100644 --- a/tools/gator/daemon/events-Mali-4xx.xml +++ b/tools/gator/daemon/events-Mali-4xx.xml @@ -1,34 +1,33 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -37,7 +36,6 @@ - @@ -96,11 +94,10 @@ - - + + - - - @@ -131,10 +126,9 @@ - - - - + + + - - - - - - - - - - - + + + + + + + + + - - - - + + + - - - - - - - - - - - + + + + + + + + + - + +