aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-02-10 15:26:00 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-02-13 13:45:58 -0500
commit00d3f14cf9f12c21428121026a5e1d5f65926447 (patch)
treee5f355deef7b9ebb5b3bf65f9d589bd2a1cfbafa
parent79420f09e76e8e1dd1149d6ce9c20e06cbb5802a (diff)
mac80211: use cfg80211s BSS infrastructure
Remove all the code from mac80211 to keep track of BSSes and use the cfg80211-provided code completely. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/zd1211rw/zd_def.h6
-rw-r--r--net/mac80211/ieee80211_i.h42
-rw-r--r--net/mac80211/main.c6
-rw-r--r--net/mac80211/mesh.c10
-rw-r--r--net/mac80211/mesh.h1
-rw-r--r--net/mac80211/mlme.c213
-rw-r--r--net/mac80211/scan.c253
-rw-r--r--net/mac80211/spectmgmt.c7
8 files changed, 117 insertions, 421 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_def.h b/drivers/net/wireless/zd1211rw/zd_def.h
index b68f7c02c512..6ac597ffd3b9 100644
--- a/drivers/net/wireless/zd1211rw/zd_def.h
+++ b/drivers/net/wireless/zd1211rw/zd_def.h
@@ -33,9 +33,9 @@ typedef u16 __nocast zd_addr_t;
33#ifdef DEBUG 33#ifdef DEBUG
34# define dev_dbg_f(dev, fmt, args...) \ 34# define dev_dbg_f(dev, fmt, args...) \
35 dev_printk_f(KERN_DEBUG, dev, fmt, ## args) 35 dev_printk_f(KERN_DEBUG, dev, fmt, ## args)
36# define dev_dbg_f_limit(dev, fmt, args...) do {\ 36# define dev_dbg_f_limit(dev, fmt, args...) do { \
37 if (net_ratelimit()) 37 if (net_ratelimit()) \
38 dev_printk_f(KERN_DEBUG, dev, fmt, ## args) 38 dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \
39} while (0) 39} while (0)
40#else 40#else
41# define dev_dbg_f(dev, fmt, args...) do { (void)(dev); } while (0) 41# define dev_dbg_f(dev, fmt, args...) do { (void)(dev); } while (0)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 87d63fe61bf9..678278344d79 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -72,43 +72,36 @@ struct ieee80211_fragment_entry {
72 72
73 73
74struct ieee80211_bss { 74struct ieee80211_bss {
75 struct list_head list; 75 /* Yes, this is a hack */
76 struct ieee80211_bss *hnext; 76 struct cfg80211_bss cbss;
77 size_t ssid_len;
78 77
79 atomic_t users; 78 /* don't want to look up all the time */
80 79 size_t ssid_len;
81 u8 bssid[ETH_ALEN];
82 u8 ssid[IEEE80211_MAX_SSID_LEN]; 80 u8 ssid[IEEE80211_MAX_SSID_LEN];
81
83 u8 dtim_period; 82 u8 dtim_period;
84 u16 capability; /* host byte order */ 83
85 enum ieee80211_band band;
86 int freq;
87 int signal, noise, qual;
88 u8 *ies; /* all information elements from the last Beacon or Probe
89 * Response frames; note Beacon frame is not allowed to
90 * override values from Probe Response */
91 size_t ies_len;
92 bool wmm_used; 84 bool wmm_used;
85
86 unsigned long last_probe_resp;
87
93#ifdef CONFIG_MAC80211_MESH 88#ifdef CONFIG_MAC80211_MESH
94 u8 *mesh_id; 89 u8 *mesh_id;
95 size_t mesh_id_len; 90 size_t mesh_id_len;
96 u8 *mesh_cfg; 91 u8 *mesh_cfg;
97#endif 92#endif
93
98#define IEEE80211_MAX_SUPP_RATES 32 94#define IEEE80211_MAX_SUPP_RATES 32
99 u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; 95 u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
100 size_t supp_rates_len; 96 size_t supp_rates_len;
101 u64 timestamp;
102 int beacon_int;
103 97
104 unsigned long last_probe_resp; 98 /*
105 unsigned long last_update; 99 * During assocation, we save an ERP value from a probe response so
106
107 /* during assocation, we save an ERP value from a probe response so
108 * that we can feed ERP info to the driver when handling the 100 * that we can feed ERP info to the driver when handling the
109 * association completes. these fields probably won't be up-to-date 101 * association completes. these fields probably won't be up-to-date
110 * otherwise, you probably don't want to use them. */ 102 * otherwise, you probably don't want to use them.
111 int has_erp_value; 103 */
104 bool has_erp_value;
112 u8 erp_value; 105 u8 erp_value;
113}; 106};
114 107
@@ -668,9 +661,6 @@ struct ieee80211_local {
668 struct ieee80211_sub_if_data *scan_sdata; 661 struct ieee80211_sub_if_data *scan_sdata;
669 enum nl80211_channel_type oper_channel_type; 662 enum nl80211_channel_type oper_channel_type;
670 struct ieee80211_channel *oper_channel, *csa_channel; 663 struct ieee80211_channel *oper_channel, *csa_channel;
671 struct list_head bss_list;
672 struct ieee80211_bss *bss_hash[STA_HASH_SIZE];
673 spinlock_t bss_lock;
674 664
675 /* SNMP counters */ 665 /* SNMP counters */
676 /* dot11CountersTable */ 666 /* dot11CountersTable */
@@ -936,8 +926,6 @@ ieee80211_rx_result
936ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, 926ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata,
937 struct sk_buff *skb, 927 struct sk_buff *skb,
938 struct ieee80211_rx_status *rx_status); 928 struct ieee80211_rx_status *rx_status);
939void ieee80211_rx_bss_list_init(struct ieee80211_local *local);
940void ieee80211_rx_bss_list_deinit(struct ieee80211_local *local);
941int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, 929int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata,
942 char *ie, size_t len); 930 char *ie, size_t len);
943 931
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 954edfbb6b6f..b4973a1b6595 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -734,6 +734,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
734 734
735 wiphy->privid = mac80211_wiphy_privid; 735 wiphy->privid = mac80211_wiphy_privid;
736 wiphy->max_scan_ssids = 4; 736 wiphy->max_scan_ssids = 4;
737 /* Yes, putting cfg80211_bss into ieee80211_bss is a hack */
738 wiphy->bss_priv_size = sizeof(struct ieee80211_bss) -
739 sizeof(struct cfg80211_bss);
737 740
738 local = wiphy_priv(wiphy); 741 local = wiphy_priv(wiphy);
739 local->hw.wiphy = wiphy; 742 local->hw.wiphy = wiphy;
@@ -877,8 +880,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
877 mpriv->local = local; 880 mpriv->local = local;
878 local->mdev = mdev; 881 local->mdev = mdev;
879 882
880 ieee80211_rx_bss_list_init(local);
881
882 local->hw.workqueue = 883 local->hw.workqueue =
883 create_singlethread_workqueue(wiphy_name(local->hw.wiphy)); 884 create_singlethread_workqueue(wiphy_name(local->hw.wiphy));
884 if (!local->hw.workqueue) { 885 if (!local->hw.workqueue) {
@@ -1018,7 +1019,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
1018 1019
1019 rtnl_unlock(); 1020 rtnl_unlock();
1020 1021
1021 ieee80211_rx_bss_list_deinit(local);
1022 ieee80211_clear_tx_pending(local); 1022 ieee80211_clear_tx_pending(local);
1023 sta_info_stop(local); 1023 sta_info_stop(local);
1024 rate_control_deinitialize(local); 1024 rate_control_deinitialize(local);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 8a1fcaeee4f2..9a3e5de0410a 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -275,16 +275,6 @@ u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, struct mesh_t
275 & tbl->hash_mask; 275 & tbl->hash_mask;
276} 276}
277 277
278u8 mesh_id_hash(u8 *mesh_id, int mesh_id_len)
279{
280 if (!mesh_id_len)
281 return 1;
282 else if (mesh_id_len == 1)
283 return (u8) mesh_id[0];
284 else
285 return (u8) (mesh_id[0] + 2 * mesh_id[1]);
286}
287
288struct mesh_table *mesh_table_alloc(int size_order) 278struct mesh_table *mesh_table_alloc(int size_order)
289{ 279{
290 int i; 280 int i;
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 9e064ee98ee0..d891d7ddccd7 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -196,7 +196,6 @@ struct mesh_rmc {
196 196
197/* Public interfaces */ 197/* Public interfaces */
198/* Various */ 198/* Various */
199u8 mesh_id_hash(u8 *mesh_id, int mesh_id_len);
200int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr); 199int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
201int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, 200int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
202 struct ieee80211_sub_if_data *sdata); 201 struct ieee80211_sub_if_data *sdata);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index c5991ec047be..c51860f66731 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -55,10 +55,10 @@ static u8 *ieee80211_bss_get_ie(struct ieee80211_bss *bss, u8 ie)
55{ 55{
56 u8 *end, *pos; 56 u8 *end, *pos;
57 57
58 pos = bss->ies; 58 pos = bss->cbss.information_elements;
59 if (pos == NULL) 59 if (pos == NULL)
60 return NULL; 60 return NULL;
61 end = pos + bss->ies_len; 61 end = pos + bss->cbss.len_information_elements;
62 62
63 while (pos + 1 < end) { 63 while (pos + 1 < end) {
64 if (pos + 2 + pos[1] > end) 64 if (pos + 2 + pos[1] > end)
@@ -289,7 +289,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
289 local->hw.conf.channel->center_freq, 289 local->hw.conf.channel->center_freq,
290 ifsta->ssid, ifsta->ssid_len); 290 ifsta->ssid, ifsta->ssid_len);
291 if (bss) { 291 if (bss) {
292 if (bss->capability & WLAN_CAPABILITY_PRIVACY) 292 if (bss->cbss.capability & WLAN_CAPABILITY_PRIVACY)
293 capab |= WLAN_CAPABILITY_PRIVACY; 293 capab |= WLAN_CAPABILITY_PRIVACY;
294 if (bss->wmm_used) 294 if (bss->wmm_used)
295 wmm = 1; 295 wmm = 1;
@@ -300,7 +300,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
300 * b-only mode) */ 300 * b-only mode) */
301 rates_len = ieee80211_compatible_rates(bss, sband, &rates); 301 rates_len = ieee80211_compatible_rates(bss, sband, &rates);
302 302
303 if ((bss->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && 303 if ((bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
304 (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) 304 (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
305 capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; 305 capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
306 306
@@ -816,12 +816,12 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
816 ifsta->ssid, ifsta->ssid_len); 816 ifsta->ssid, ifsta->ssid_len);
817 if (bss) { 817 if (bss) {
818 /* set timing information */ 818 /* set timing information */
819 sdata->vif.bss_conf.beacon_int = bss->beacon_int; 819 sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval;
820 sdata->vif.bss_conf.timestamp = bss->timestamp; 820 sdata->vif.bss_conf.timestamp = bss->cbss.tsf;
821 sdata->vif.bss_conf.dtim_period = bss->dtim_period; 821 sdata->vif.bss_conf.dtim_period = bss->dtim_period;
822 822
823 bss_info_changed |= ieee80211_handle_bss_capability(sdata, 823 bss_info_changed |= ieee80211_handle_bss_capability(sdata,
824 bss->capability, bss->has_erp_value, bss->erp_value); 824 bss->cbss.capability, bss->has_erp_value, bss->erp_value);
825 825
826 ieee80211_rx_bss_put(local, bss); 826 ieee80211_rx_bss_put(local, bss);
827 } 827 }
@@ -1041,7 +1041,7 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata,
1041 if (!bss) 1041 if (!bss)
1042 return 0; 1042 return 0;
1043 1043
1044 bss_privacy = !!(bss->capability & WLAN_CAPABILITY_PRIVACY); 1044 bss_privacy = !!(bss->cbss.capability & WLAN_CAPABILITY_PRIVACY);
1045 wep_privacy = !!ieee80211_sta_wep_configured(sdata); 1045 wep_privacy = !!ieee80211_sta_wep_configured(sdata);
1046 privacy_invoked = !!(ifsta->flags & IEEE80211_STA_PRIVACY_INVOKED); 1046 privacy_invoked = !!(ifsta->flags & IEEE80211_STA_PRIVACY_INVOKED);
1047 1047
@@ -1416,8 +1416,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
1416 /* Add STA entry for the AP */ 1416 /* Add STA entry for the AP */
1417 sta = sta_info_get(local, ifsta->bssid); 1417 sta = sta_info_get(local, ifsta->bssid);
1418 if (!sta) { 1418 if (!sta) {
1419 struct ieee80211_bss *bss;
1420
1421 newsta = true; 1419 newsta = true;
1422 1420
1423 sta = sta_info_alloc(sdata, ifsta->bssid, GFP_ATOMIC); 1421 sta = sta_info_alloc(sdata, ifsta->bssid, GFP_ATOMIC);
@@ -1427,15 +1425,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
1427 rcu_read_unlock(); 1425 rcu_read_unlock();
1428 return; 1426 return;
1429 } 1427 }
1430 bss = ieee80211_rx_bss_get(local, ifsta->bssid,
1431 local->hw.conf.channel->center_freq,
1432 ifsta->ssid, ifsta->ssid_len);
1433 if (bss) {
1434 sta->last_signal = bss->signal;
1435 sta->last_qual = bss->qual;
1436 sta->last_noise = bss->noise;
1437 ieee80211_rx_bss_put(local, bss);
1438 }
1439 1428
1440 /* update new sta with its last rx activity */ 1429 /* update new sta with its last rx activity */
1441 sta->last_rx = jiffies; 1430 sta->last_rx = jiffies;
@@ -1691,10 +1680,11 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
1691 struct ieee80211_bss *bss) 1680 struct ieee80211_bss *bss)
1692{ 1681{
1693 return __ieee80211_sta_join_ibss(sdata, ifsta, 1682 return __ieee80211_sta_join_ibss(sdata, ifsta,
1694 bss->bssid, bss->beacon_int, 1683 bss->cbss.bssid,
1695 bss->freq, 1684 bss->cbss.beacon_interval,
1685 bss->cbss.channel->center_freq,
1696 bss->supp_rates_len, bss->supp_rates, 1686 bss->supp_rates_len, bss->supp_rates,
1697 bss->capability); 1687 bss->cbss.capability);
1698} 1688}
1699 1689
1700static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, 1690static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
@@ -1769,7 +1759,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
1769 } 1759 }
1770 1760
1771 /* was just updated in ieee80211_bss_info_update */ 1761 /* was just updated in ieee80211_bss_info_update */
1772 beacon_timestamp = bss->timestamp; 1762 beacon_timestamp = bss->cbss.tsf;
1773 1763
1774 /* 1764 /*
1775 * In STA mode, the remaining parameters should not be overridden 1765 * In STA mode, the remaining parameters should not be overridden
@@ -1784,8 +1774,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
1784 /* check if we need to merge IBSS */ 1774 /* check if we need to merge IBSS */
1785 if (sdata->vif.type == NL80211_IFTYPE_ADHOC && beacon && 1775 if (sdata->vif.type == NL80211_IFTYPE_ADHOC && beacon &&
1786 (!(sdata->u.sta.flags & IEEE80211_STA_BSSID_SET)) && 1776 (!(sdata->u.sta.flags & IEEE80211_STA_BSSID_SET)) &&
1787 bss->capability & WLAN_CAPABILITY_IBSS && 1777 bss->cbss.capability & WLAN_CAPABILITY_IBSS &&
1788 bss->freq == local->oper_channel->center_freq && 1778 bss->cbss.channel == local->oper_channel &&
1789 elems->ssid_len == sdata->u.sta.ssid_len && 1779 elems->ssid_len == sdata->u.sta.ssid_len &&
1790 memcmp(elems->ssid, sdata->u.sta.ssid, 1780 memcmp(elems->ssid, sdata->u.sta.ssid,
1791 sdata->u.sta.ssid_len) == 0) { 1781 sdata->u.sta.ssid_len) == 0) {
@@ -2230,37 +2220,6 @@ static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata,
2230 netif_carrier_off(sdata->dev); 2220 netif_carrier_off(sdata->dev);
2231} 2221}
2232 2222
2233
2234static int ieee80211_sta_match_ssid(struct ieee80211_if_sta *ifsta,
2235 const char *ssid, int ssid_len)
2236{
2237 int tmp, hidden_ssid;
2238
2239 if (ssid_len == ifsta->ssid_len &&
2240 !memcmp(ifsta->ssid, ssid, ssid_len))
2241 return 1;
2242
2243 if (ifsta->flags & IEEE80211_STA_AUTO_BSSID_SEL)
2244 return 0;
2245
2246 hidden_ssid = 1;
2247 tmp = ssid_len;
2248 while (tmp--) {
2249 if (ssid[tmp] != '\0') {
2250 hidden_ssid = 0;
2251 break;
2252 }
2253 }
2254
2255 if (hidden_ssid && (ifsta->ssid_len == ssid_len || ssid_len == 0))
2256 return 1;
2257
2258 if (ssid_len == 1 && ssid[0] == ' ')
2259 return 1;
2260
2261 return 0;
2262}
2263
2264static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata, 2223static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
2265 struct ieee80211_if_sta *ifsta) 2224 struct ieee80211_if_sta *ifsta)
2266{ 2225{
@@ -2319,8 +2278,6 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
2319{ 2278{
2320 struct ieee80211_local *local = sdata->local; 2279 struct ieee80211_local *local = sdata->local;
2321 struct ieee80211_bss *bss; 2280 struct ieee80211_bss *bss;
2322 int found = 0;
2323 u8 bssid[ETH_ALEN];
2324 int active_ibss; 2281 int active_ibss;
2325 2282
2326 if (ifsta->ssid_len == 0) 2283 if (ifsta->ssid_len == 0)
@@ -2331,56 +2288,39 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
2331 printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n", 2288 printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n",
2332 sdata->dev->name, active_ibss); 2289 sdata->dev->name, active_ibss);
2333#endif /* CONFIG_MAC80211_IBSS_DEBUG */ 2290#endif /* CONFIG_MAC80211_IBSS_DEBUG */
2334 spin_lock_bh(&local->bss_lock); 2291
2335 list_for_each_entry(bss, &local->bss_list, list) { 2292 if (active_ibss)
2336 if (ifsta->ssid_len != bss->ssid_len || 2293 return 0;
2337 memcmp(ifsta->ssid, bss->ssid, bss->ssid_len) != 0 2294
2338 || !(bss->capability & WLAN_CAPABILITY_IBSS)) 2295 if (ifsta->flags & IEEE80211_STA_BSSID_SET)
2339 continue; 2296 bss = ieee80211_rx_bss_get(local, ifsta->bssid, 0,
2340 if ((ifsta->flags & IEEE80211_STA_BSSID_SET) && 2297 ifsta->ssid, ifsta->ssid_len);
2341 memcmp(ifsta->bssid, bss->bssid, ETH_ALEN) != 0) 2298 else
2342 continue; 2299 bss = (void *)cfg80211_get_ibss(local->hw.wiphy,
2343#ifdef CONFIG_MAC80211_IBSS_DEBUG 2300 NULL,
2344 printk(KERN_DEBUG " bssid=%pM found\n", bss->bssid); 2301 ifsta->ssid, ifsta->ssid_len);
2345#endif /* CONFIG_MAC80211_IBSS_DEBUG */
2346 memcpy(bssid, bss->bssid, ETH_ALEN);
2347 found = 1;
2348 if (active_ibss || memcmp(bssid, ifsta->bssid, ETH_ALEN) != 0)
2349 break;
2350 }
2351 spin_unlock_bh(&local->bss_lock);
2352 2302
2353#ifdef CONFIG_MAC80211_IBSS_DEBUG 2303#ifdef CONFIG_MAC80211_IBSS_DEBUG
2354 if (found) 2304 if (bss)
2355 printk(KERN_DEBUG " sta_find_ibss: selected %pM current " 2305 printk(KERN_DEBUG " sta_find_ibss: selected %pM current "
2356 "%pM\n", bssid, ifsta->bssid); 2306 "%pM\n", bss->cbss.bssid, ifsta->bssid);
2357#endif /* CONFIG_MAC80211_IBSS_DEBUG */ 2307#endif /* CONFIG_MAC80211_IBSS_DEBUG */
2358 2308
2359 if (found && 2309 if (bss &&
2360 ((!(ifsta->flags & IEEE80211_STA_PREV_BSSID_SET)) || 2310 (!(ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) ||
2361 memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0)) { 2311 memcmp(ifsta->bssid, bss->cbss.bssid, ETH_ALEN))) {
2362 int ret; 2312 int ret;
2363 int search_freq;
2364
2365 if (ifsta->flags & IEEE80211_STA_AUTO_CHANNEL_SEL)
2366 search_freq = bss->freq;
2367 else
2368 search_freq = local->hw.conf.channel->center_freq;
2369
2370 bss = ieee80211_rx_bss_get(local, bssid, search_freq,
2371 ifsta->ssid, ifsta->ssid_len);
2372 if (!bss)
2373 goto dont_join;
2374 2313
2375 printk(KERN_DEBUG "%s: Selected IBSS BSSID %pM" 2314 printk(KERN_DEBUG "%s: Selected IBSS BSSID %pM"
2376 " based on configured SSID\n", 2315 " based on configured SSID\n",
2377 sdata->dev->name, bssid); 2316 sdata->dev->name, bss->cbss.bssid);
2317
2378 ret = ieee80211_sta_join_ibss(sdata, ifsta, bss); 2318 ret = ieee80211_sta_join_ibss(sdata, ifsta, bss);
2379 ieee80211_rx_bss_put(local, bss); 2319 ieee80211_rx_bss_put(local, bss);
2380 return ret; 2320 return ret;
2381 } 2321 } else if (bss)
2322 ieee80211_rx_bss_put(local, bss);
2382 2323
2383dont_join:
2384#ifdef CONFIG_MAC80211_IBSS_DEBUG 2324#ifdef CONFIG_MAC80211_IBSS_DEBUG
2385 printk(KERN_DEBUG " did not try to join ibss\n"); 2325 printk(KERN_DEBUG " did not try to join ibss\n");
2386#endif /* CONFIG_MAC80211_IBSS_DEBUG */ 2326#endif /* CONFIG_MAC80211_IBSS_DEBUG */
@@ -2436,51 +2376,44 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
2436 struct ieee80211_if_sta *ifsta) 2376 struct ieee80211_if_sta *ifsta)
2437{ 2377{
2438 struct ieee80211_local *local = sdata->local; 2378 struct ieee80211_local *local = sdata->local;
2439 struct ieee80211_bss *bss, *selected = NULL; 2379 struct ieee80211_bss *bss;
2440 int top_rssi = 0, freq; 2380 u8 *bssid = ifsta->bssid, *ssid = ifsta->ssid;
2441 2381 u8 ssid_len = ifsta->ssid_len;
2442 spin_lock_bh(&local->bss_lock); 2382 u16 capa_mask = WLAN_CAPABILITY_ESS;
2443 freq = local->oper_channel->center_freq; 2383 u16 capa_val = WLAN_CAPABILITY_ESS;
2444 list_for_each_entry(bss, &local->bss_list, list) { 2384 struct ieee80211_channel *chan = local->oper_channel;
2445 if (!(bss->capability & WLAN_CAPABILITY_ESS)) 2385
2446 continue; 2386 if (ifsta->flags & (IEEE80211_STA_AUTO_SSID_SEL |
2447 2387 IEEE80211_STA_AUTO_BSSID_SEL |
2448 if ((ifsta->flags & (IEEE80211_STA_AUTO_SSID_SEL | 2388 IEEE80211_STA_AUTO_CHANNEL_SEL)) {
2449 IEEE80211_STA_AUTO_BSSID_SEL | 2389 capa_mask |= WLAN_CAPABILITY_PRIVACY;
2450 IEEE80211_STA_AUTO_CHANNEL_SEL)) && 2390 if (sdata->default_key)
2451 (!!(bss->capability & WLAN_CAPABILITY_PRIVACY) ^ 2391 capa_val |= WLAN_CAPABILITY_PRIVACY;
2452 !!sdata->default_key))
2453 continue;
2454
2455 if (!(ifsta->flags & IEEE80211_STA_AUTO_CHANNEL_SEL) &&
2456 bss->freq != freq)
2457 continue;
2458
2459 if (!(ifsta->flags & IEEE80211_STA_AUTO_BSSID_SEL) &&
2460 memcmp(bss->bssid, ifsta->bssid, ETH_ALEN))
2461 continue;
2462
2463 if (!(ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL) &&
2464 !ieee80211_sta_match_ssid(ifsta, bss->ssid, bss->ssid_len))
2465 continue;
2466
2467 if (!selected || top_rssi < bss->signal) {
2468 selected = bss;
2469 top_rssi = bss->signal;
2470 }
2471 } 2392 }
2472 if (selected)
2473 atomic_inc(&selected->users);
2474 spin_unlock_bh(&local->bss_lock);
2475 2393
2476 if (selected) { 2394 if (ifsta->flags & IEEE80211_STA_AUTO_CHANNEL_SEL)
2477 ieee80211_set_freq(sdata, selected->freq); 2395 chan = NULL;
2396
2397 if (ifsta->flags & IEEE80211_STA_AUTO_BSSID_SEL)
2398 bssid = NULL;
2399
2400 if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL) {
2401 ssid = NULL;
2402 ssid_len = 0;
2403 }
2404
2405 bss = (void *)cfg80211_get_bss(local->hw.wiphy, chan,
2406 bssid, ssid, ssid_len,
2407 capa_mask, capa_val);
2408
2409 if (bss) {
2410 ieee80211_set_freq(sdata, bss->cbss.channel->center_freq);
2478 if (!(ifsta->flags & IEEE80211_STA_SSID_SET)) 2411 if (!(ifsta->flags & IEEE80211_STA_SSID_SET))
2479 ieee80211_sta_set_ssid(sdata, selected->ssid, 2412 ieee80211_sta_set_ssid(sdata, bss->ssid,
2480 selected->ssid_len); 2413 bss->ssid_len);
2481 ieee80211_sta_set_bssid(sdata, selected->bssid); 2414 ieee80211_sta_set_bssid(sdata, bss->cbss.bssid);
2482 ieee80211_sta_def_wmm_params(sdata, selected->supp_rates_len, 2415 ieee80211_sta_def_wmm_params(sdata, bss->supp_rates_len,
2483 selected->supp_rates); 2416 bss->supp_rates);
2484 if (sdata->u.sta.mfp == IEEE80211_MFP_REQUIRED) 2417 if (sdata->u.sta.mfp == IEEE80211_MFP_REQUIRED)
2485 sdata->u.sta.flags |= IEEE80211_STA_MFP_ENABLED; 2418 sdata->u.sta.flags |= IEEE80211_STA_MFP_ENABLED;
2486 else 2419 else
@@ -2489,14 +2422,14 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
2489 /* Send out direct probe if no probe resp was received or 2422 /* Send out direct probe if no probe resp was received or
2490 * the one we have is outdated 2423 * the one we have is outdated
2491 */ 2424 */
2492 if (!selected->last_probe_resp || 2425 if (!bss->last_probe_resp ||
2493 time_after(jiffies, selected->last_probe_resp 2426 time_after(jiffies, bss->last_probe_resp
2494 + IEEE80211_SCAN_RESULT_EXPIRE)) 2427 + IEEE80211_SCAN_RESULT_EXPIRE))
2495 ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE; 2428 ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE;
2496 else 2429 else
2497 ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE; 2430 ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
2498 2431
2499 ieee80211_rx_bss_put(local, selected); 2432 ieee80211_rx_bss_put(local, bss);
2500 ieee80211_sta_reset_auth(sdata, ifsta); 2433 ieee80211_sta_reset_auth(sdata, ifsta);
2501 return 0; 2434 return 0;
2502 } else { 2435 } else {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index fc88e2e2f923..f883ab9f1e6e 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -12,10 +12,7 @@
12 * published by the Free Software Foundation. 12 * published by the Free Software Foundation.
13 */ 13 */
14 14
15/* TODO: 15/* TODO: figure out how to avoid that the "current BSS" expires */
16 * figure out how to avoid that the "current BSS" expires
17 * use cfg80211's BSS handling
18 */
19 16
20#include <linux/wireless.h> 17#include <linux/wireless.h>
21#include <linux/if_arp.h> 18#include <linux/if_arp.h>
@@ -30,192 +27,29 @@
30#define IEEE80211_CHANNEL_TIME (HZ / 33) 27#define IEEE80211_CHANNEL_TIME (HZ / 33)
31#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5) 28#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5)
32 29
33void ieee80211_rx_bss_list_init(struct ieee80211_local *local)
34{
35 spin_lock_init(&local->bss_lock);
36 INIT_LIST_HEAD(&local->bss_list);
37}
38
39void ieee80211_rx_bss_list_deinit(struct ieee80211_local *local)
40{
41 struct ieee80211_bss *bss, *tmp;
42
43 list_for_each_entry_safe(bss, tmp, &local->bss_list, list)
44 ieee80211_rx_bss_put(local, bss);
45}
46
47struct ieee80211_bss * 30struct ieee80211_bss *
48ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, 31ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
49 u8 *ssid, u8 ssid_len) 32 u8 *ssid, u8 ssid_len)
50{ 33{
51 struct ieee80211_bss *bss; 34 return (void *)cfg80211_get_bss(local->hw.wiphy,
52 35 ieee80211_get_channel(local->hw.wiphy,
53 spin_lock_bh(&local->bss_lock); 36 freq),
54 bss = local->bss_hash[STA_HASH(bssid)]; 37 bssid, ssid, ssid_len,
55 while (bss) { 38 0, 0);
56 if (!bss_mesh_cfg(bss) &&
57 !memcmp(bss->bssid, bssid, ETH_ALEN) &&
58 bss->freq == freq &&
59 bss->ssid_len == ssid_len &&
60 (ssid_len == 0 || !memcmp(bss->ssid, ssid, ssid_len))) {
61 atomic_inc(&bss->users);
62 break;
63 }
64 bss = bss->hnext;
65 }
66 spin_unlock_bh(&local->bss_lock);
67 return bss;
68}
69
70/* Caller must hold local->bss_lock */
71static void __ieee80211_rx_bss_hash_add(struct ieee80211_local *local,
72 struct ieee80211_bss *bss)
73{
74 u8 hash_idx;
75
76 if (bss_mesh_cfg(bss))
77 hash_idx = mesh_id_hash(bss_mesh_id(bss),
78 bss_mesh_id_len(bss));
79 else
80 hash_idx = STA_HASH(bss->bssid);
81
82 bss->hnext = local->bss_hash[hash_idx];
83 local->bss_hash[hash_idx] = bss;
84}
85
86/* Caller must hold local->bss_lock */
87static void __ieee80211_rx_bss_hash_del(struct ieee80211_local *local,
88 struct ieee80211_bss *bss)
89{
90 struct ieee80211_bss *b, *prev = NULL;
91 b = local->bss_hash[STA_HASH(bss->bssid)];
92 while (b) {
93 if (b == bss) {
94 if (!prev)
95 local->bss_hash[STA_HASH(bss->bssid)] =
96 bss->hnext;
97 else
98 prev->hnext = bss->hnext;
99 break;
100 }
101 prev = b;
102 b = b->hnext;
103 }
104}
105
106static struct ieee80211_bss *
107ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq,
108 u8 *ssid, u8 ssid_len)
109{
110 struct ieee80211_bss *bss;
111
112 bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
113 if (!bss)
114 return NULL;
115 atomic_set(&bss->users, 2);
116 memcpy(bss->bssid, bssid, ETH_ALEN);
117 bss->freq = freq;
118 if (ssid && ssid_len <= IEEE80211_MAX_SSID_LEN) {
119 memcpy(bss->ssid, ssid, ssid_len);
120 bss->ssid_len = ssid_len;
121 }
122
123 spin_lock_bh(&local->bss_lock);
124 /* TODO: order by RSSI? */
125 list_add_tail(&bss->list, &local->bss_list);
126 __ieee80211_rx_bss_hash_add(local, bss);
127 spin_unlock_bh(&local->bss_lock);
128 return bss;
129}
130
131#ifdef CONFIG_MAC80211_MESH
132static struct ieee80211_bss *
133ieee80211_rx_mesh_bss_get(struct ieee80211_local *local, u8 *mesh_id, int mesh_id_len,
134 u8 *mesh_cfg, int freq)
135{
136 struct ieee80211_bss *bss;
137
138 spin_lock_bh(&local->bss_lock);
139 bss = local->bss_hash[mesh_id_hash(mesh_id, mesh_id_len)];
140 while (bss) {
141 if (bss_mesh_cfg(bss) &&
142 !memcmp(bss_mesh_cfg(bss), mesh_cfg, MESH_CFG_CMP_LEN) &&
143 bss->freq == freq &&
144 mesh_id_len == bss->mesh_id_len &&
145 (mesh_id_len == 0 || !memcmp(bss->mesh_id, mesh_id,
146 mesh_id_len))) {
147 atomic_inc(&bss->users);
148 break;
149 }
150 bss = bss->hnext;
151 }
152 spin_unlock_bh(&local->bss_lock);
153 return bss;
154} 39}
155 40
156static struct ieee80211_bss * 41static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss)
157ieee80211_rx_mesh_bss_add(struct ieee80211_local *local, u8 *mesh_id, int mesh_id_len,
158 u8 *mesh_cfg, int mesh_config_len, int freq)
159{ 42{
160 struct ieee80211_bss *bss; 43 struct ieee80211_bss *bss = (void *)cbss;
161
162 if (mesh_config_len != IEEE80211_MESH_CONFIG_LEN)
163 return NULL;
164
165 bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
166 if (!bss)
167 return NULL;
168
169 bss->mesh_cfg = kmalloc(MESH_CFG_CMP_LEN, GFP_ATOMIC);
170 if (!bss->mesh_cfg) {
171 kfree(bss);
172 return NULL;
173 }
174
175 if (mesh_id_len && mesh_id_len <= IEEE80211_MAX_MESH_ID_LEN) {
176 bss->mesh_id = kmalloc(mesh_id_len, GFP_ATOMIC);
177 if (!bss->mesh_id) {
178 kfree(bss->mesh_cfg);
179 kfree(bss);
180 return NULL;
181 }
182 memcpy(bss->mesh_id, mesh_id, mesh_id_len);
183 }
184
185 atomic_set(&bss->users, 2);
186 memcpy(bss->mesh_cfg, mesh_cfg, MESH_CFG_CMP_LEN);
187 bss->mesh_id_len = mesh_id_len;
188 bss->freq = freq;
189 spin_lock_bh(&local->bss_lock);
190 /* TODO: order by RSSI? */
191 list_add_tail(&bss->list, &local->bss_list);
192 __ieee80211_rx_bss_hash_add(local, bss);
193 spin_unlock_bh(&local->bss_lock);
194 return bss;
195}
196#endif
197 44
198static void ieee80211_rx_bss_free(struct ieee80211_bss *bss)
199{
200 kfree(bss->ies);
201 kfree(bss_mesh_id(bss)); 45 kfree(bss_mesh_id(bss));
202 kfree(bss_mesh_cfg(bss)); 46 kfree(bss_mesh_cfg(bss));
203 kfree(bss);
204} 47}
205 48
206void ieee80211_rx_bss_put(struct ieee80211_local *local, 49void ieee80211_rx_bss_put(struct ieee80211_local *local,
207 struct ieee80211_bss *bss) 50 struct ieee80211_bss *bss)
208{ 51{
209 local_bh_disable(); 52 cfg80211_put_bss((struct cfg80211_bss *)bss);
210 if (!atomic_dec_and_lock(&bss->users, &local->bss_lock)) {
211 local_bh_enable();
212 return;
213 }
214
215 __ieee80211_rx_bss_hash_del(local, bss);
216 list_del(&bss->list);
217 spin_unlock_bh(&local->bss_lock);
218 ieee80211_rx_bss_free(bss);
219} 53}
220 54
221struct ieee80211_bss * 55struct ieee80211_bss *
@@ -228,7 +62,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
228 bool beacon) 62 bool beacon)
229{ 63{
230 struct ieee80211_bss *bss; 64 struct ieee80211_bss *bss;
231 int clen, freq = channel->center_freq; 65 int clen;
232 enum cfg80211_signal_type sigtype = CFG80211_SIGNAL_TYPE_NONE; 66 enum cfg80211_signal_type sigtype = CFG80211_SIGNAL_TYPE_NONE;
233 s32 signal = 0; 67 s32 signal = 0;
234 68
@@ -240,39 +74,14 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
240 signal = (rx_status->signal * 100) / local->hw.max_signal; 74 signal = (rx_status->signal * 100) / local->hw.max_signal;
241 } 75 }
242 76
243 cfg80211_put_bss( 77 bss = (void *)cfg80211_inform_bss_frame(local->hw.wiphy, channel,
244 cfg80211_inform_bss_frame(local->hw.wiphy, channel, 78 mgmt, len, signal, sigtype,
245 mgmt, len, signal, sigtype, 79 GFP_ATOMIC);
246 GFP_ATOMIC));
247 80
248#ifdef CONFIG_MAC80211_MESH 81 if (!bss)
249 if (elems->mesh_config) 82 return NULL;
250 bss = ieee80211_rx_mesh_bss_get(local, elems->mesh_id, 83
251 elems->mesh_id_len, elems->mesh_config, freq); 84 bss->cbss.free_priv = ieee80211_rx_bss_free;
252 else
253#endif
254 bss = ieee80211_rx_bss_get(local, mgmt->bssid, freq,
255 elems->ssid, elems->ssid_len);
256 if (!bss) {
257#ifdef CONFIG_MAC80211_MESH
258 if (elems->mesh_config)
259 bss = ieee80211_rx_mesh_bss_add(local, elems->mesh_id,
260 elems->mesh_id_len, elems->mesh_config,
261 elems->mesh_config_len, freq);
262 else
263#endif
264 bss = ieee80211_rx_bss_add(local, mgmt->bssid, freq,
265 elems->ssid, elems->ssid_len);
266 if (!bss)
267 return NULL;
268 } else {
269#if 0
270 /* TODO: order by RSSI? */
271 spin_lock_bh(&local->bss_lock);
272 list_move_tail(&bss->list, &local->bss_list);
273 spin_unlock_bh(&local->bss_lock);
274#endif
275 }
276 85
277 /* save the ERP value so that it is available at association time */ 86 /* save the ERP value so that it is available at association time */
278 if (elems->erp_info && elems->erp_info_len >= 1) { 87 if (elems->erp_info && elems->erp_info_len >= 1) {
@@ -280,9 +89,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
280 bss->has_erp_value = 1; 89 bss->has_erp_value = 1;
281 } 90 }
282 91
283 bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int);
284 bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info);
285
286 if (elems->tim) { 92 if (elems->tim) {
287 struct ieee80211_tim_ie *tim_ie = 93 struct ieee80211_tim_ie *tim_ie =
288 (struct ieee80211_tim_ie *)elems->tim; 94 (struct ieee80211_tim_ie *)elems->tim;
@@ -311,34 +117,11 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
311 bss->supp_rates_len += clen; 117 bss->supp_rates_len += clen;
312 } 118 }
313 119
314 bss->band = rx_status->band;
315
316 bss->timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
317 bss->last_update = jiffies;
318 bss->signal = rx_status->signal;
319 bss->noise = rx_status->noise;
320 bss->qual = rx_status->qual;
321 bss->wmm_used = elems->wmm_param || elems->wmm_info; 120 bss->wmm_used = elems->wmm_param || elems->wmm_info;
322 121
323 if (!beacon) 122 if (!beacon)
324 bss->last_probe_resp = jiffies; 123 bss->last_probe_resp = jiffies;
325 124
326 /*
327 * For probe responses, or if we don't have any information yet,
328 * use the IEs from the beacon.
329 */
330 if (!bss->ies || !beacon) {
331 if (bss->ies == NULL || bss->ies_len < elems->total_len) {
332 kfree(bss->ies);
333 bss->ies = kmalloc(elems->total_len, GFP_ATOMIC);
334 }
335 if (bss->ies) {
336 memcpy(bss->ies, elems->ie_start, elems->total_len);
337 bss->ies_len = elems->total_len;
338 } else
339 bss->ies_len = 0;
340 }
341
342 return bss; 125 return bss;
343} 126}
344 127
@@ -350,7 +133,7 @@ void ieee80211_rx_bss_remove(struct ieee80211_sub_if_data *sdata, u8 *bssid,
350 133
351 bss = ieee80211_rx_bss_get(local, bssid, freq, ssid, ssid_len); 134 bss = ieee80211_rx_bss_get(local, bssid, freq, ssid, ssid_len);
352 if (bss) { 135 if (bss) {
353 atomic_dec(&bss->users); 136 cfg80211_unlink_bss(local->hw.wiphy, (void *)bss);
354 ieee80211_rx_bss_put(local, bss); 137 ieee80211_rx_bss_put(local, bss);
355 } 138 }
356} 139}
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index 8d4ec2968f8f..47bb2aed2813 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -102,8 +102,9 @@ void ieee80211_chswitch_work(struct work_struct *work)
102 goto exit; 102 goto exit;
103 103
104 sdata->local->oper_channel = sdata->local->csa_channel; 104 sdata->local->oper_channel = sdata->local->csa_channel;
105 /* XXX: shouldn't really modify cfg80211-owned data! */
105 if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL)) 106 if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL))
106 bss->freq = sdata->local->oper_channel->center_freq; 107 bss->cbss.channel = sdata->local->oper_channel;
107 108
108 ieee80211_rx_bss_put(sdata->local, bss); 109 ieee80211_rx_bss_put(sdata->local, bss);
109exit: 110exit:
@@ -158,7 +159,9 @@ void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
158 IEEE80211_QUEUE_STOP_REASON_CSA); 159 IEEE80211_QUEUE_STOP_REASON_CSA);
159 ifsta->flags |= IEEE80211_STA_CSA_RECEIVED; 160 ifsta->flags |= IEEE80211_STA_CSA_RECEIVED;
160 mod_timer(&ifsta->chswitch_timer, 161 mod_timer(&ifsta->chswitch_timer,
161 jiffies + msecs_to_jiffies(sw_elem->count * bss->beacon_int)); 162 jiffies +
163 msecs_to_jiffies(sw_elem->count *
164 bss->cbss.beacon_interval));
162 } 165 }
163} 166}
164 167
t">) < 0) return NULL; if (read_expected(EVENT_OP, ":") < 0) return NULL; if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; return token; fail: free_token(token); return NULL; } static int event_read_id(void) { char *token; int id; if (read_expected_item(EVENT_ITEM, "ID") < 0) return -1; if (read_expected(EVENT_OP, ":") < 0) return -1; if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; id = strtoul(token, NULL, 0); free_token(token); return id; fail: free_token(token); return -1; } static int field_is_string(struct format_field *field) { if ((field->flags & FIELD_IS_ARRAY) && (strstr(field->type, "char") || strstr(field->type, "u8") || strstr(field->type, "s8"))) return 1; return 0; } static int field_is_dynamic(struct format_field *field) { if (strncmp(field->type, "__data_loc", 10) == 0) return 1; return 0; } static int field_is_long(struct format_field *field) { /* includes long long */ if (strstr(field->type, "long")) return 1; return 0; } static int event_read_fields(struct event_format *event, struct format_field **fields) { struct format_field *field = NULL; enum event_type type; char *token; char *last_token; int count = 0; do { type = read_token(&token); if (type == EVENT_NEWLINE) { free_token(token); return count; } count++; if (test_type_token(type, token, EVENT_ITEM, "field")) goto fail; free_token(token); type = read_token(&token); /* * The ftrace fields may still use the "special" name. * Just ignore it. */ if (event->flags & EVENT_FL_ISFTRACE && type == EVENT_ITEM && strcmp(token, "special") == 0) { free_token(token); type = read_token(&token); } if (test_type_token(type, token, EVENT_OP, ":") < 0) goto fail; free_token(token); if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; last_token = token; field = malloc_or_die(sizeof(*field)); memset(field, 0, sizeof(*field)); field->event = event; /* read the rest of the type */ for (;;) { type = read_token(&token); if (type == EVENT_ITEM || (type == EVENT_OP && strcmp(token, "*") == 0) || /* * Some of the ftrace fields are broken and have * an illegal "." in them. */ (event->flags & EVENT_FL_ISFTRACE && type == EVENT_OP && strcmp(token, ".") == 0)) { if (strcmp(token, "*") == 0) field->flags |= FIELD_IS_POINTER; if (field->type) { field->type = realloc(field->type, strlen(field->type) + strlen(last_token) + 2); strcat(field->type, " "); strcat(field->type, last_token); free(last_token); } else field->type = last_token; last_token = token; continue; } break; } if (!field->type) { die("no type found"); goto fail; } field->name = last_token; if (test_type(type, EVENT_OP)) goto fail; if (strcmp(token, "[") == 0) { enum event_type last_type = type; char *brackets = token; int len; field->flags |= FIELD_IS_ARRAY; type = read_token(&token); if (type == EVENT_ITEM) field->arraylen = strtoul(token, NULL, 0); else field->arraylen = 0; while (strcmp(token, "]") != 0) { if (last_type == EVENT_ITEM && type == EVENT_ITEM) len = 2; else len = 1; last_type = type; brackets = realloc(brackets, strlen(brackets) + strlen(token) + len); if (len == 2) strcat(brackets, " "); strcat(brackets, token); /* We only care about the last token */ field->arraylen = strtoul(token, NULL, 0); free_token(token); type = read_token(&token); if (type == EVENT_NONE) { die("failed to find token"); goto fail; } } free_token(token); brackets = realloc(brackets, strlen(brackets) + 2); strcat(brackets, "]"); /* add brackets to type */ type = read_token(&token); /* * If the next token is not an OP, then it is of * the format: type [] item; */ if (type == EVENT_ITEM) { field->type = realloc(field->type, strlen(field->type) + strlen(field->name) + strlen(brackets) + 2); strcat(field->type, " "); strcat(field->type, field->name); free_token(field->name); strcat(field->type, brackets); field->name = token; type = read_token(&token); } else { field->type = realloc(field->type, strlen(field->type) + strlen(brackets) + 1); strcat(field->type, brackets); } free(brackets); } if (field_is_string(field)) field->flags |= FIELD_IS_STRING; if (field_is_dynamic(field)) field->flags |= FIELD_IS_DYNAMIC; if (field_is_long(field)) field->flags |= FIELD_IS_LONG; if (test_type_token(type, token, EVENT_OP, ";")) goto fail; free_token(token); if (read_expected(EVENT_ITEM, "offset") < 0) goto fail_expect; if (read_expected(EVENT_OP, ":") < 0) goto fail_expect; if (read_expect_type(EVENT_ITEM, &token)) goto fail; field->offset = strtoul(token, NULL, 0); free_token(token); if (read_expected(EVENT_OP, ";") < 0) goto fail_expect; if (read_expected(EVENT_ITEM, "size") < 0) goto fail_expect; if (read_expected(EVENT_OP, ":") < 0) goto fail_expect; if (read_expect_type(EVENT_ITEM, &token)) goto fail; field->size = strtoul(token, NULL, 0); free_token(token); if (read_expected(EVENT_OP, ";") < 0) goto fail_expect; type = read_token(&token); if (type != EVENT_NEWLINE) { /* newer versions of the kernel have a "signed" type */ if (test_type_token(type, token, EVENT_ITEM, "signed")) goto fail; free_token(token); if (read_expected(EVENT_OP, ":") < 0) goto fail_expect; if (read_expect_type(EVENT_ITEM, &token)) goto fail; /* add signed type */ free_token(token); if (read_expected(EVENT_OP, ";") < 0) goto fail_expect; if (read_expect_type(EVENT_NEWLINE, &token)) goto fail; } free_token(token); if (field->flags & FIELD_IS_ARRAY) { if (field->arraylen) field->elementsize = field->size / field->arraylen; else field->elementsize = event->pevent->long_size; } else field->elementsize = field->size; *fields = field; fields = &field->next; } while (1); return 0; fail: free_token(token); fail_expect: if (field) free(field); return -1; } static int event_read_format(struct event_format *event) { char *token; int ret; if (read_expected_item(EVENT_ITEM, "format") < 0) return -1; if (read_expected(EVENT_OP, ":") < 0) return -1; if (read_expect_type(EVENT_NEWLINE, &token)) goto fail; free_token(token); ret = event_read_fields(event, &event->format.common_fields); if (ret < 0) return ret; event->format.nr_common = ret; ret = event_read_fields(event, &event->format.fields); if (ret < 0) return ret; event->format.nr_fields = ret; return 0; fail: free_token(token); return -1; } static enum event_type process_arg_token(struct event_format *event, struct print_arg *arg, char **tok, enum event_type type); static enum event_type process_arg(struct event_format *event, struct print_arg *arg, char **tok) { enum event_type type; char *token; type = read_token(&token); *tok = token; return process_arg_token(event, arg, tok, type); } static enum event_type process_op(struct event_format *event, struct print_arg *arg, char **tok); static enum event_type process_cond(struct event_format *event, struct print_arg *top, char **tok) { struct print_arg *arg, *left, *right; enum event_type type; char *token = NULL; arg = alloc_arg(); left = alloc_arg(); right = alloc_arg(); arg->type = PRINT_OP; arg->op.left = left; arg->op.right = right; *tok = NULL; type = process_arg(event, left, &token); again: /* Handle other operations in the arguments */ if (type == EVENT_OP && strcmp(token, ":") != 0) { type = process_op(event, left, &token); goto again; } if (test_type_token(type, token, EVENT_OP, ":")) goto out_free; arg->op.op = token; type = process_arg(event, right, &token); top->op.right = arg; *tok = token; return type; out_free: /* Top may point to itself */ top->op.right = NULL; free_token(token); free_arg(arg); return EVENT_ERROR; } static enum event_type process_array(struct event_format *event, struct print_arg *top, char **tok) { struct print_arg *arg; enum event_type type; char *token = NULL; arg = alloc_arg(); *tok = NULL; type = process_arg(event, arg, &token); if (test_type_token(type, token, EVENT_OP, "]")) goto out_free; top->op.right = arg; free_token(token); type = read_token_item(&token); *tok = token; return type; out_free: free_token(*tok); *tok = NULL; free_arg(arg); return EVENT_ERROR; } static int get_op_prio(char *op) { if (!op[1]) { switch (op[0]) { case '*': case '/': case '%': return 6; case '+': case '-': return 7; /* '>>' and '<<' are 8 */ case '<': case '>': return 9; /* '==' and '!=' are 10 */ case '&': return 11; case '^': return 12; case '|': return 13; case '?': return 16; default: die("unknown op '%c'", op[0]); return -1; } } else { if (strcmp(op, "++") == 0 || strcmp(op, "--") == 0) { return 3; } else if (strcmp(op, ">>") == 0 || strcmp(op, "<<") == 0) { return 8; } else if (strcmp(op, ">=") == 0 || strcmp(op, "<=") == 0) { return 9; } else if (strcmp(op, "==") == 0 || strcmp(op, "!=") == 0) { return 10; } else if (strcmp(op, "&&") == 0) { return 14; } else if (strcmp(op, "||") == 0) { return 15; } else { die("unknown op '%s'", op); return -1; } } } static void set_op_prio(struct print_arg *arg) { /* single ops are the greatest */ if (!arg->op.left || arg->op.left->type == PRINT_NULL) { arg->op.prio = 0; return; } arg->op.prio = get_op_prio(arg->op.op); } /* Note, *tok does not get freed, but will most likely be saved */ static enum event_type process_op(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *left, *right = NULL; enum event_type type; char *token; /* the op is passed in via tok */ token = *tok; if (arg->type == PRINT_OP && !arg->op.left) { /* handle single op */ if (token[1]) { die("bad op token %s", token); goto out_free; } switch (token[0]) { case '!': case '+': case '-': break; default: do_warning("bad op token %s", token); goto out_free; } /* make an empty left */ left = alloc_arg(); left->type = PRINT_NULL; arg->op.left = left; right = alloc_arg(); arg->op.right = right; /* do not free the token, it belongs to an op */ *tok = NULL; type = process_arg(event, right, tok); } else if (strcmp(token, "?") == 0) { left = alloc_arg(); /* copy the top arg to the left */ *left = *arg; arg->type = PRINT_OP; arg->op.op = token; arg->op.left = left; arg->op.prio = 0; type = process_cond(event, arg, tok); } else if (strcmp(token, ">>") == 0 || strcmp(token, "<<") == 0 || strcmp(token, "&") == 0 || strcmp(token, "|") == 0 || strcmp(token, "&&") == 0 || strcmp(token, "||") == 0 || strcmp(token, "-") == 0 || strcmp(token, "+") == 0 || strcmp(token, "*") == 0 || strcmp(token, "^") == 0 || strcmp(token, "/") == 0 || strcmp(token, "<") == 0 || strcmp(token, ">") == 0 || strcmp(token, "==") == 0 || strcmp(token, "!=") == 0) { left = alloc_arg(); /* copy the top arg to the left */ *left = *arg; arg->type = PRINT_OP; arg->op.op = token; arg->op.left = left; set_op_prio(arg); type = read_token_item(&token); *tok = token; /* could just be a type pointer */ if ((strcmp(arg->op.op, "*") == 0) && type == EVENT_DELIM && (strcmp(token, ")") == 0)) { if (left->type != PRINT_ATOM) die("bad pointer type"); left->atom.atom = realloc(left->atom.atom, strlen(left->atom.atom) + 3); strcat(left->atom.atom, " *"); free(arg->op.op); *arg = *left; free(left); return type; } right = alloc_arg(); type = process_arg_token(event, right, tok, type); arg->op.right = right; } else if (strcmp(token, "[") == 0) { left = alloc_arg(); *left = *arg; arg->type = PRINT_OP; arg->op.op = token; arg->op.left = left; arg->op.prio = 0; type = process_array(event, arg, tok); } else { do_warning("unknown op '%s'", token); event->flags |= EVENT_FL_FAILED; /* the arg is now the left side */ goto out_free; } if (type == EVENT_OP && strcmp(*tok, ":") != 0) { int prio; /* higher prios need to be closer to the root */ prio = get_op_prio(*tok); if (prio > arg->op.prio) return process_op(event, arg, tok); return process_op(event, right, tok); } return type; out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_entry(struct event_format *event __unused, struct print_arg *arg, char **tok) { enum event_type type; char *field; char *token; if (read_expected(EVENT_OP, "->") < 0) goto out_err; if (read_expect_type(EVENT_ITEM, &token) < 0) goto out_free; field = token; arg->type = PRINT_FIELD; arg->field.name = field; type = read_token(&token); *tok = token; return type; out_free: free_token(token); out_err: *tok = NULL; return EVENT_ERROR; } static char *arg_eval (struct print_arg *arg); static unsigned long long eval_type_str(unsigned long long val, const char *type, int pointer) { int sign = 0; char *ref; int len; len = strlen(type); if (pointer) { if (type[len-1] != '*') { do_warning("pointer expected with non pointer type"); return val; } ref = malloc_or_die(len); memcpy(ref, type, len); /* chop off the " *" */ ref[len - 2] = 0; val = eval_type_str(val, ref, 0); free(ref); return val; } /* check if this is a pointer */ if (type[len - 1] == '*') return val; /* Try to figure out the arg size*/ if (strncmp(type, "struct", 6) == 0) /* all bets off */ return val; if (strcmp(type, "u8") == 0) return val & 0xff; if (strcmp(type, "u16") == 0) return val & 0xffff; if (strcmp(type, "u32") == 0) return val & 0xffffffff; if (strcmp(type, "u64") == 0 || strcmp(type, "s64")) return val; if (strcmp(type, "s8") == 0) return (unsigned long long)(char)val & 0xff; if (strcmp(type, "s16") == 0) return (unsigned long long)(short)val & 0xffff; if (strcmp(type, "s32") == 0) return (unsigned long long)(int)val & 0xffffffff; if (strncmp(type, "unsigned ", 9) == 0) { sign = 0; type += 9; } if (strcmp(type, "char") == 0) { if (sign) return (unsigned long long)(char)val & 0xff; else return val & 0xff; } if (strcmp(type, "short") == 0) { if (sign) return (unsigned long long)(short)val & 0xffff; else return val & 0xffff; } if (strcmp(type, "int") == 0) { if (sign) return (unsigned long long)(int)val & 0xffffffff; else return val & 0xffffffff; } return val; } /* * Try to figure out the type. */ static unsigned long long eval_type(unsigned long long val, struct print_arg *arg, int pointer) { if (arg->type != PRINT_TYPE) die("expected type argument"); return eval_type_str(val, arg->typecast.type, pointer); } static long long arg_num_eval(struct print_arg *arg) { long long left, right; long long val = 0; switch (arg->type) { case PRINT_ATOM: val = strtoll(arg->atom.atom, NULL, 0); break; case PRINT_TYPE: val = arg_num_eval(arg->typecast.item); val = eval_type(val, arg, 0); break; case PRINT_OP: switch (arg->op.op[0]) { case '|': left = arg_num_eval(arg->op.left); right = arg_num_eval(arg->op.right); if (arg->op.op[1]) val = left || right; else val = left | right; break; case '&': left = arg_num_eval(arg->op.left); right = arg_num_eval(arg->op.right); if (arg->op.op[1]) val = left && right; else val = left & right; break; case '<': left = arg_num_eval(arg->op.left); right = arg_num_eval(arg->op.right); switch (arg->op.op[1]) { case 0: val = left < right; break; case '<': val = left << right; break; case '=': val = left <= right; break; default: die("unknown op '%s'", arg->op.op); } break; case '>': left = arg_num_eval(arg->op.left); right = arg_num_eval(arg->op.right); switch (arg->op.op[1]) { case 0: val = left > right; break; case '>': val = left >> right; break; case '=': val = left >= right; break; default: die("unknown op '%s'", arg->op.op); } break; case '=': left = arg_num_eval(arg->op.left); right = arg_num_eval(arg->op.right); if (arg->op.op[1] != '=') die("unknown op '%s'", arg->op.op); val = left == right; break; case '!': left = arg_num_eval(arg->op.left); right = arg_num_eval(arg->op.right); switch (arg->op.op[1]) { case '=': val = left != right; break; default: die("unknown op '%s'", arg->op.op); } break; case '-': /* check for negative */ if (arg->op.left->type == PRINT_NULL) left = 0; else left = arg_num_eval(arg->op.left); right = arg_num_eval(arg->op.right); val = left - right; break; default: die("unknown op '%s'", arg->op.op); } break; case PRINT_NULL: case PRINT_FIELD ... PRINT_SYMBOL: case PRINT_STRING: default: die("invalid eval type %d", arg->type); } return val; } static char *arg_eval (struct print_arg *arg) { long long val; static char buf[20]; switch (arg->type) { case PRINT_ATOM: return arg->atom.atom; case PRINT_TYPE: return arg_eval(arg->typecast.item); case PRINT_OP: val = arg_num_eval(arg); sprintf(buf, "%lld", val); return buf; case PRINT_NULL: case PRINT_FIELD ... PRINT_SYMBOL: case PRINT_STRING: default: die("invalid eval type %d", arg->type); break; } return NULL; } static enum event_type process_fields(struct event_format *event, struct print_flag_sym **list, char **tok) { enum event_type type; struct print_arg *arg = NULL; struct print_flag_sym *field; char *token = *tok; char *value; do { free_token(token); type = read_token_item(&token); if (test_type_token(type, token, EVENT_OP, "{")) break; arg = alloc_arg(); free_token(token); type = process_arg(event, arg, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; field = malloc_or_die(sizeof(*field)); memset(field, 0, sizeof(field)); value = arg_eval(arg); field->value = strdup(value); free_arg(arg); arg = alloc_arg(); free_token(token); type = process_arg(event, arg, &token); if (test_type_token(type, token, EVENT_OP, "}")) goto out_free; value = arg_eval(arg); field->str = strdup(value); free_arg(arg); arg = NULL; *list = field; list = &field->next; free_token(token); type = read_token_item(&token); } while (type == EVENT_DELIM && strcmp(token, ",") == 0); *tok = token; return type; out_free: free_arg(arg); free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_flags(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *field; enum event_type type; char *token; memset(arg, 0, sizeof(*arg)); arg->type = PRINT_FLAGS; field = alloc_arg(); type = process_arg(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; free_token(token); arg->flags.field = field; type = read_token_item(&token); if (event_item_type(type)) { arg->flags.delim = token; type = read_token_item(&token); } if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; type = process_fields(event, &arg->flags.flags, &token); if (test_type_token(type, token, EVENT_DELIM, ")")) goto out_free; free_token(token); type = read_token_item(tok); return type; out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_symbols(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *field; enum event_type type; char *token; memset(arg, 0, sizeof(*arg)); arg->type = PRINT_SYMBOL; field = alloc_arg(); type = process_arg(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; arg->symbol.field = field; type = process_fields(event, &arg->symbol.symbols, &token); if (test_type_token(type, token, EVENT_DELIM, ")")) goto out_free; free_token(token); type = read_token_item(tok); return type; out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_dynamic_array(struct event_format *event, struct print_arg *arg, char **tok) { struct format_field *field; enum event_type type; char *token; memset(arg, 0, sizeof(*arg)); arg->type = PRINT_DYNAMIC_ARRAY; /* * The item within the parenthesis is another field that holds * the index into where the array starts. */ type = read_token(&token); *tok = token; if (type != EVENT_ITEM) goto out_free; /* Find the field */ field = pevent_find_field(event, token); if (!field) goto out_free; arg->dynarray.field = field; arg->dynarray.index = 0; if (read_expected(EVENT_DELIM, ")") < 0) goto out_free; type = read_token_item(&token); *tok = token; if (type != EVENT_OP || strcmp(token, "[") != 0) return type; free_token(token); arg = alloc_arg(); type = process_arg(event, arg, &token); if (type == EVENT_ERROR) goto out_free; if (!test_type_token(type, token, EVENT_OP, "]")) goto out_free; free_token(token); type = read_token_item(tok); return type; out_free: free(arg); free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_paren(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *item_arg; enum event_type type; char *token; type = process_arg(event, arg, &token); if (type == EVENT_ERROR) goto out_free; if (type == EVENT_OP) type = process_op(event, arg, &token); if (type == EVENT_ERROR) goto out_free; if (test_type_token(type, token, EVENT_DELIM, ")")) goto out_free; free_token(token); type = read_token_item(&token); /* * If the next token is an item or another open paren, then * this was a typecast. */ if (event_item_type(type) || (type == EVENT_DELIM && strcmp(token, "(") == 0)) { /* make this a typecast and contine */ /* prevous must be an atom */ if (arg->type != PRINT_ATOM) die("previous needed to be PRINT_ATOM"); item_arg = alloc_arg(); arg->type = PRINT_TYPE; arg->typecast.type = arg->atom.atom; arg->typecast.item = item_arg; type = process_arg_token(event, item_arg, &token, type); } *tok = token; return type; out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_str(struct event_format *event __unused, struct print_arg *arg, char **tok) { enum event_type type; char *token; if (read_expect_type(EVENT_ITEM, &token) < 0) goto out_free; arg->type = PRINT_STRING; arg->string.string = token; arg->string.offset = -1; if (read_expected(EVENT_DELIM, ")") < 0) goto out_err; type = read_token(&token); *tok = token; return type; out_free: free_token(token); out_err: *tok = NULL; return EVENT_ERROR; } static struct pevent_function_handler * find_func_handler(struct pevent *pevent, char *func_name) { struct pevent_function_handler *func; for (func = pevent->func_handlers; func; func = func->next) { if (strcmp(func->name, func_name) == 0) break; } return func; } static void remove_func_handler(struct pevent *pevent, char *func_name) { struct pevent_function_handler *func; struct pevent_function_handler **next; next = &pevent->func_handlers; while ((func = *next)) { if (strcmp(func->name, func_name) == 0) { *next = func->next; free_func_handle(func); break; } next = &func->next; } } static enum event_type process_func_handler(struct event_format *event, struct pevent_function_handler *func, struct print_arg *arg, char **tok) { struct print_arg **next_arg; struct print_arg *farg; enum event_type type; char *token; char *test; int i; arg->type = PRINT_FUNC; arg->func.func = func; *tok = NULL; next_arg = &(arg->func.args); for (i = 0; i < func->nr_args; i++) { farg = alloc_arg(); type = process_arg(event, farg, &token); if (i < (func->nr_args - 1)) test = ","; else test = ")"; if (test_type_token(type, token, EVENT_DELIM, test)) { free_arg(farg); free_token(token); return EVENT_ERROR; } *next_arg = farg; next_arg = &(farg->next); } type = read_token(&token); *tok = token; return type; } static enum event_type process_function(struct event_format *event, struct print_arg *arg, char *token, char **tok) { struct pevent_function_handler *func; if (strcmp(token, "__print_flags") == 0) { free_token(token); return process_flags(event, arg, tok); } if (strcmp(token, "__print_symbolic") == 0) { free_token(token); return process_symbols(event, arg, tok); } if (strcmp(token, "__get_str") == 0) { free_token(token); return process_str(event, arg, tok); } if (strcmp(token, "__get_dynamic_array") == 0) { free_token(token); return process_dynamic_array(event, arg, tok); } func = find_func_handler(event->pevent, token); if (func) { free_token(token); return process_func_handler(event, func, arg, tok); } do_warning("function %s not defined", token); free_token(token); return EVENT_ERROR; } static enum event_type process_arg_token(struct event_format *event, struct print_arg *arg, char **tok, enum event_type type) { char *token; char *atom; token = *tok; switch (type) { case EVENT_ITEM: if (strcmp(token, "REC") == 0) { free_token(token); type = process_entry(event, arg, &token); break; } atom = token; /* test the next token */ type = read_token_item(&token); /* * If the next token is a parenthesis, then this * is a function. */ if (type == EVENT_DELIM && strcmp(token, "(") == 0) { free_token(token); token = NULL; /* this will free atom. */ type = process_function(event, arg, atom, &token); break; } /* atoms can be more than one token long */ while (type == EVENT_ITEM) { atom = realloc(atom, strlen(atom) + strlen(token) + 2); strcat(atom, " "); strcat(atom, token); free_token(token); type = read_token_item(&token); } arg->type = PRINT_ATOM; arg->atom.atom = atom; break; case EVENT_DQUOTE: case EVENT_SQUOTE: arg->type = PRINT_ATOM; arg->atom.atom = token; type = read_token_item(&token); break; case EVENT_DELIM: if (strcmp(token, "(") == 0) { free_token(token); type = process_paren(event, arg, &token); break; } case EVENT_OP: /* handle single ops */ arg->type = PRINT_OP; arg->op.op = token; arg->op.left = NULL; type = process_op(event, arg, &token); /* On error, the op is freed */ if (type == EVENT_ERROR) arg->op.op = NULL; /* return error type if errored */ break; case EVENT_ERROR ... EVENT_NEWLINE: default: die("unexpected type %d", type); } *tok = token; return type; } static int event_read_print_args(struct event_format *event, struct print_arg **list) { enum event_type type = EVENT_ERROR; struct print_arg *arg; char *token; int args = 0; do { if (type == EVENT_NEWLINE) { type = read_token_item(&token); continue; } arg = alloc_arg(); type = process_arg(event, arg, &token); if (type == EVENT_ERROR) { free_token(token); free_arg(arg); return -1; } *list = arg; args++; if (type == EVENT_OP) { type = process_op(event, arg, &token); free_token(token); if (type == EVENT_ERROR) { *list = NULL; free_arg(arg); return -1; } list = &arg->next; continue; } if (type == EVENT_DELIM && strcmp(token, ",") == 0) { free_token(token); *list = arg; list = &arg->next; continue; } break; } while (type != EVENT_NONE); if (type != EVENT_NONE && type != EVENT_ERROR) free_token(token); return args; } static int event_read_print(struct event_format *event) { enum event_type type; char *token; int ret; if (read_expected_item(EVENT_ITEM, "print") < 0) return -1; if (read_expected(EVENT_ITEM, "fmt") < 0) return -1; if (read_expected(EVENT_OP, ":") < 0) return -1; if (read_expect_type(EVENT_DQUOTE, &token) < 0) goto fail; concat: event->print_fmt.format = token; event->print_fmt.args = NULL; /* ok to have no arg */ type = read_token_item(&token); if (type == EVENT_NONE) return 0; /* Handle concatenation of print lines */ if (type == EVENT_DQUOTE) { char *cat; cat = malloc_or_die(strlen(event->print_fmt.format) + strlen(token) + 1); strcpy(cat, event->print_fmt.format); strcat(cat, token); free_token(token); free_token(event->print_fmt.format); event->print_fmt.format = NULL; token = cat; goto concat; } if (test_type_token(type, token, EVENT_DELIM, ",")) goto fail; free_token(token); ret = event_read_print_args(event, &event->print_fmt.args); if (ret < 0) return -1; return ret; fail: free_token(token); return -1; } /** * pevent_find_common_field - return a common field by event * @event: handle for the event * @name: the name of the common field to return * * Returns a common field from the event by the given @name. * This only searchs the common fields and not all field. */ struct format_field * pevent_find_common_field(struct event_format *event, const char *name) { struct format_field *format; for (format = event->format.common_fields; format; format = format->next) { if (strcmp(format->name, name) == 0) break; } return format; } /** * pevent_find_field - find a non-common field * @event: handle for the event * @name: the name of the non-common field * * Returns a non-common field by the given @name. * This does not search common fields. */ struct format_field * pevent_find_field(struct event_format *event, const char *name) { struct format_field *format; for (format = event->format.fields; format; format = format->next) { if (strcmp(format->name, name) == 0) break; } return format; } /** * pevent_find_any_field - find any field by name * @event: handle for the event * @name: the name of the field * * Returns a field by the given @name. * This searchs the common field names first, then * the non-common ones if a common one was not found. */ struct format_field * pevent_find_any_field(struct event_format *event, const char *name) { struct format_field *format; format = pevent_find_common_field(event, name); if (format) return format; return pevent_find_field(event, name); } /** * pevent_read_number - read a number from data * @pevent: handle for the pevent * @ptr: the raw data * @size: the size of the data that holds the number * * Returns the number (converted to host) from the * raw data. */ unsigned long long pevent_read_number(struct pevent *pevent, const void *ptr, int size) { switch (size) { case 1: return *(unsigned char *)ptr; case 2: return data2host2(pevent, ptr); case 4: return data2host4(pevent, ptr); case 8: return data2host8(pevent, ptr); default: /* BUG! */ return 0; } } /** * pevent_read_number_field - read a number from data * @field: a handle to the field * @data: the raw data to read * @value: the value to place the number in * * Reads raw data according to a field offset and size, * and translates it into @value. * * Returns 0 on success, -1 otherwise. */ int pevent_read_number_field(struct format_field *field, const void *data, unsigned long long *value) { switch (field->size) { case 1: case 2: case 4: case 8: *value = pevent_read_number(field->event->pevent, data + field->offset, field->size); return 0; default: return -1; } } static int get_common_info(struct pevent *pevent, const char *type, int *offset, int *size) { struct event_format *event; struct format_field *field; /* * All events should have the same common elements. * Pick any event to find where the type is; */ if (!pevent->events) die("no event_list!"); event = pevent->events[0]; field = pevent_find_common_field(event, type); if (!field) die("field '%s' not found", type); *offset = field->offset; *size = field->size; return 0; } static int __parse_common(struct pevent *pevent, void *data, int *size, int *offset, const char *name) { int ret; if (!*size) { ret = get_common_info(pevent, name, offset, size); if (ret < 0) return ret; } return pevent_read_number(pevent, data + *offset, *size); } static int trace_parse_common_type(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->type_size, &pevent->type_offset, "common_type"); } static int parse_common_pid(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->pid_size, &pevent->pid_offset, "common_pid"); } static int parse_common_pc(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->pc_size, &pevent->pc_offset, "common_preempt_count"); } static int parse_common_flags(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->flags_size, &pevent->flags_offset, "common_flags"); } static int parse_common_lock_depth(struct pevent *pevent, void *data) { int ret; ret = __parse_common(pevent, data, &pevent->ld_size, &pevent->ld_offset, "common_lock_depth"); if (ret < 0) return -1; return ret; } static int events_id_cmp(const void *a, const void *b); /** * pevent_find_event - find an event by given id * @pevent: a handle to the pevent * @id: the id of the event * * Returns an event that has a given @id. */ struct event_format *pevent_find_event(struct pevent *pevent, int id) { struct event_format **eventptr; struct event_format key; struct event_format *pkey = &key; /* Check cache first */ if (pevent->last_event && pevent->last_event->id == id) return pevent->last_event; key.id = id; eventptr = bsearch(&pkey, pevent->events, pevent->nr_events, sizeof(*pevent->events), events_id_cmp); if (eventptr) { pevent->last_event = *eventptr; return *eventptr; } return NULL; } /** * pevent_find_event_by_name - find an event by given name * @pevent: a handle to the pevent * @sys: the system name to search for * @name: the name of the event to search for * * This returns an event with a given @name and under the system * @sys. If @sys is NULL the first event with @name is returned. */ struct event_format * pevent_find_event_by_name(struct pevent *pevent, const char *sys, const char *name) { struct event_format *event; int i; if (pevent->last_event && strcmp(pevent->last_event->name, name) == 0 && (!sys || strcmp(pevent->last_event->system, sys) == 0)) return pevent->last_event; for (i = 0; i < pevent->nr_events; i++) { event = pevent->events[i]; if (strcmp(event->name, name) == 0) { if (!sys) break; if (strcmp(event->system, sys) == 0) break; } } if (i == pevent->nr_events) event = NULL; pevent->last_event = event; return event; } static unsigned long long eval_num_arg(void *data, int size, struct event_format *event, struct print_arg *arg) { struct pevent *pevent = event->pevent; unsigned long long val = 0; unsigned long long left, right; struct print_arg *typearg = NULL; struct print_arg *larg; unsigned long offset; unsigned int field_size; switch (arg->type) { case PRINT_NULL: /* ?? */ return 0; case PRINT_ATOM: return strtoull(arg->atom.atom, NULL, 0); case PRINT_FIELD: if (!arg->field.field) { arg->field.field = pevent_find_any_field(event, arg->field.name); if (!arg->field.field) die("field %s not found", arg->field.name); } /* must be a number */ val = pevent_read_number(pevent, data + arg->field.field->offset, arg->field.field->size); break; case PRINT_FLAGS: case PRINT_SYMBOL: break; case PRINT_TYPE: val = eval_num_arg(data, size, event, arg->typecast.item); return eval_type(val, arg, 0); case PRINT_STRING: return 0; case PRINT_FUNC: { struct trace_seq s; trace_seq_init(&s); return process_defined_func(&s, data, size, event, arg); } case PRINT_OP: if (strcmp(arg->op.op, "[") == 0) { /* * Arrays are special, since we don't want * to read the arg as is. */ right = eval_num_arg(data, size, event, arg->op.right); /* handle typecasts */ larg = arg->op.left; while (larg->type == PRINT_TYPE) { if (!typearg) typearg = larg; larg = larg->typecast.item; } /* Default to long size */ field_size = pevent->long_size; switch (larg->type) { case PRINT_DYNAMIC_ARRAY: offset = pevent_read_number(pevent, data + larg->dynarray.field->offset, larg->dynarray.field->size); /* * The actual length of the dynamic array is stored * in the top half of the field, and the offset * is in the bottom half of the 32 bit field. */ offset &= 0xffff; offset += right; break; case PRINT_FIELD: if (!larg->field.field) { larg->field.field = pevent_find_any_field(event, larg->field.name); if (!larg->field.field) die("field %s not found", larg->field.name); } field_size = larg->field.field->elementsize; offset = larg->field.field->offset + right * larg->field.field->elementsize; break; default: goto default_op; /* oops, all bets off */ } val = pevent_read_number(pevent, data + offset, field_size); if (typearg) val = eval_type(val, typearg, 1); break; } else if (strcmp(arg->op.op, "?") == 0) { left = eval_num_arg(data, size, event, arg->op.left); arg = arg->op.right; if (left) val = eval_num_arg(data, size, event, arg->op.left); else val = eval_num_arg(data, size, event, arg->op.right); break; } default_op: left = eval_num_arg(data, size, event, arg->op.left); right = eval_num_arg(data, size, event, arg->op.right); switch (arg->op.op[0]) { case '|': if (arg->op.op[1]) val = left || right; else val = left | right; break; case '&': if (arg->op.op[1]) val = left && right; else val = left & right; break; case '<': switch (arg->op.op[1]) { case 0: val = left < right; break; case '<': val = left << right; break; case '=': val = left <= right; break; default: die("unknown op '%s'", arg->op.op); } break; case '>': switch (arg->op.op[1]) { case 0: val = left > right; break; case '>': val = left >> right; break; case '=': val = left >= right; break; default: die("unknown op '%s'", arg->op.op); } break; case '=': if (arg->op.op[1] != '=') die("unknown op '%s'", arg->op.op); val = left == right; break; case '-': val = left - right; break; case '+': val = left + right; break; default: die("unknown op '%s'", arg->op.op); } break; default: /* not sure what to do there */ return 0; } return val; } struct flag { const char *name; unsigned long long value; }; static const struct flag flags[] = { { "HI_SOFTIRQ", 0 }, { "TIMER_SOFTIRQ", 1 }, { "NET_TX_SOFTIRQ", 2 }, { "NET_RX_SOFTIRQ", 3 }, { "BLOCK_SOFTIRQ", 4 }, { "BLOCK_IOPOLL_SOFTIRQ", 5 }, { "TASKLET_SOFTIRQ", 6 }, { "SCHED_SOFTIRQ", 7 }, { "HRTIMER_SOFTIRQ", 8 }, { "RCU_SOFTIRQ", 9 }, { "HRTIMER_NORESTART", 0 }, { "HRTIMER_RESTART", 1 }, }; static unsigned long long eval_flag(const char *flag) { int i; /* * Some flags in the format files do not get converted. * If the flag is not numeric, see if it is something that * we already know about. */ if (isdigit(flag[0])) return strtoull(flag, NULL, 0); for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++) if (strcmp(flags[i].name, flag) == 0) return flags[i].value; return 0; } static void print_str_arg(struct trace_seq *s, void *data, int size, struct event_format *event, struct print_arg *arg) { struct pevent *pevent = event->pevent; struct print_flag_sym *flag; unsigned long long val, fval; unsigned long addr; char *str; int print; int len; switch (arg->type) { case PRINT_NULL: /* ?? */ return; case PRINT_ATOM: trace_seq_puts(s, arg->atom.atom); return; case PRINT_FIELD: if (!arg->field.field) { arg->field.field = pevent_find_any_field(event, arg->field.name); if (!arg->field.field) die("field %s not found", arg->field.name); } /* Zero sized fields, mean the rest of the data */ len = arg->field.field->size ? : size; /* * Some events pass in pointers. If this is not an array * and the size is the same as long_size, assume that it * is a pointer. */ if (!(arg->field.field->flags & FIELD_IS_ARRAY) && len == pevent->long_size) { addr = *(unsigned long *)(data + arg->field.field->offset); trace_seq_printf(s, "%lx", addr); break; } str = malloc_or_die(len + 1); memcpy(str, data + arg->field.field->offset, len); str[len] = 0; trace_seq_puts(s, str); free(str); break; case PRINT_FLAGS: val = eval_num_arg(data, size, event, arg->flags.field); print = 0; for (flag = arg->flags.flags; flag; flag = flag->next) { fval = eval_flag(flag->value); if (!val && !fval) { trace_seq_puts(s, flag->str); break; } if (fval && (val & fval) == fval) { if (print && arg->flags.delim) trace_seq_puts(s, arg->flags.delim); trace_seq_puts(s, flag->str); print = 1; val &= ~fval; } } break; case PRINT_SYMBOL: val = eval_num_arg(data, size, event, arg->symbol.field); for (flag = arg->symbol.symbols; flag; flag = flag->next) { fval = eval_flag(flag->value); if (val == fval) { trace_seq_puts(s, flag->str); break; } } break; case PRINT_TYPE: break; case PRINT_STRING: { int str_offset; if (arg->string.offset == -1) { struct format_field *f; f = pevent_find_any_field(event, arg->string.string); arg->string.offset = f->offset; } str_offset = data2host4(pevent, data + arg->string.offset); str_offset &= 0xffff; trace_seq_puts(s, ((char *)data) + str_offset); break; } case PRINT_OP: /* * The only op for string should be ? : */ if (arg->op.op[0] != '?') return; val = eval_num_arg(data, size, event, arg->op.left); if (val) print_str_arg(s, data, size, event, arg->op.right->op.left); else print_str_arg(s, data, size, event, arg->op.right->op.right); break; case PRINT_FUNC: process_defined_func(s, data, size, event, arg); break; default: /* well... */ break; } } static unsigned long long process_defined_func(struct trace_seq *s, void *data, int size, struct event_format *event, struct print_arg *arg) { struct pevent_function_handler *func_handle = arg->func.func; struct pevent_func_params *param; unsigned long long *args; unsigned long long ret; struct print_arg *farg; struct trace_seq str; struct save_str { struct save_str *next; char *str; } *strings = NULL, *string; int i; if (!func_handle->nr_args) { ret = (*func_handle->func)(s, NULL); goto out; } farg = arg->func.args; param = func_handle->params; args = malloc_or_die(sizeof(*args) * func_handle->nr_args); for (i = 0; i < func_handle->nr_args; i++) { switch (param->type) { case PEVENT_FUNC_ARG_INT: case PEVENT_FUNC_ARG_LONG: case PEVENT_FUNC_ARG_PTR: args[i] = eval_num_arg(data, size, event, farg); break; case PEVENT_FUNC_ARG_STRING: trace_seq_init(&str); print_str_arg(&str, data, size, event, farg); trace_seq_terminate(&str); string = malloc_or_die(sizeof(*string)); string->next = strings; string->str = strdup(str.buffer); strings = string; break; default: /* * Something went totally wrong, this is not * an input error, something in this code broke. */ die("Unexpected end of arguments\n"); break; } farg = farg->next; } ret = (*func_handle->func)(s, args); free(args); while (strings) { string = strings; strings = string->next; free(string->str); free(string); } out: /* TBD : handle return type here */ return ret; } static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event_format *event) { struct pevent *pevent = event->pevent; struct format_field *field, *ip_field; struct print_arg *args, *arg, **next; unsigned long long ip, val; char *ptr; void *bptr; field = pevent->bprint_buf_field; ip_field = pevent->bprint_ip_field; if (!field) { field = pevent_find_field(event, "buf"); if (!field) die("can't find buffer field for binary printk"); ip_field = pevent_find_field(event, "ip"); if (!ip_field) die("can't find ip field for binary printk"); pevent->bprint_buf_field = field; pevent->bprint_ip_field = ip_field; } ip = pevent_read_number(pevent, data + ip_field->offset, ip_field->size); /* * The first arg is the IP pointer. */ args = alloc_arg(); arg = args; arg->next = NULL; next = &arg->next; arg->type = PRINT_ATOM; arg->atom.atom = malloc_or_die(32); sprintf(arg->atom.atom, "%lld", ip); /* skip the first "%pf : " */ for (ptr = fmt + 6, bptr = data + field->offset; bptr < data + size && *ptr; ptr++) { int ls = 0; if (*ptr == '%') { process_again: ptr++; switch (*ptr) { case '%': break; case 'l': ls++; goto process_again; case 'L': ls = 2; goto process_again; case '0' ... '9': goto process_again; case 'p': ls = 1; /* fall through */ case 'd': case 'u': case 'x': case 'i': /* the pointers are always 4 bytes aligned */ bptr = (void *)(((unsigned long)bptr + 3) & ~3); switch (ls) { case 0: case 1: ls = pevent->long_size; break; case 2: ls = 8; default: break; } val = pevent_read_number(pevent, bptr, ls); bptr += ls; arg = alloc_arg(); arg->next = NULL; arg->type = PRINT_ATOM; arg->atom.atom = malloc_or_die(32); sprintf(arg->atom.atom, "%lld", val); *next = arg; next = &arg->next; break; case 's': arg = alloc_arg(); arg->next = NULL; arg->type = PRINT_STRING; arg->string.string = strdup(bptr); bptr += strlen(bptr) + 1; *next = arg; next = &arg->next; default: break; } } } return args; } static void free_args(struct print_arg *args) { struct print_arg *next; while (args) { next = args->next; free_arg(args); args = next; } } static char * get_bprint_format(void *data, int size __unused, struct event_format *event) { struct pevent *pevent = event->pevent; unsigned long long addr; struct format_field *field; struct printk_map *printk; char *format; char *p; field = pevent->bprint_fmt_field; if (!field) { field = pevent_find_field(event, "fmt"); if (!field) die("can't find format field for binary printk"); printf("field->offset = %d size=%d\n", field->offset, field->size); pevent->bprint_fmt_field = field; } addr = pevent_read_number(pevent, data + field->offset, field->size); printk = find_printk(pevent, addr); if (!printk) { format = malloc_or_die(45); sprintf(format, "%%pf : (NO FORMAT FOUND at %llx)\n", addr); return format; } p = printk->printk; /* Remove any quotes. */ if (*p == '"') p++; format = malloc_or_die(strlen(p) + 10); sprintf(format, "%s : %s", "%pf", p); /* remove ending quotes and new line since we will add one too */ p = format + strlen(format) - 1; if (*p == '"') *p = 0; p -= 2; if (strcmp(p, "\\n") == 0) *p = 0; return format; } static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size, struct event_format *event, struct print_arg *arg) { unsigned char *buf; char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"; if (arg->type == PRINT_FUNC) { process_defined_func(s, data, size, event, arg); return; } if (arg->type != PRINT_FIELD) { trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type); return; } if (mac == 'm') fmt = "%.2x%.2x%.2x%.2x%.2x%.2x"; if (!arg->field.field) { arg->field.field = pevent_find_any_field(event, arg->field.name); if (!arg->field.field) die("field %s not found", arg->field.name); } if (arg->field.field->size != 6) { trace_seq_printf(s, "INVALIDMAC"); return; } buf = data + arg->field.field->offset; trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); } static void print_event_fields(struct trace_seq *s, void *data, int size, struct event_format *event) { struct format_field *field; unsigned long long val; unsigned int offset, len, i; field = event->format.fields; while (field) { trace_seq_printf(s, " %s=", field->name); if (field->flags & FIELD_IS_ARRAY) { offset = field->offset; len = field->size; if (field->flags & FIELD_IS_DYNAMIC) { val = pevent_read_number(event->pevent, data + offset, len); offset = val; len = offset >> 16; offset &= 0xffff; } if (field->flags & FIELD_IS_STRING) { trace_seq_printf(s, "%s", (char *)data + offset); } else { trace_seq_puts(s, "ARRAY["); for (i = 0; i < len; i++) { if (i) trace_seq_puts(s, ", "); trace_seq_printf(s, "%02x", *((unsigned char *)data + offset + i)); } trace_seq_putc(s, ']'); } } else { val = pevent_read_number(event->pevent, data + field->offset, field->size); if (field->flags & FIELD_IS_POINTER) { trace_seq_printf(s, "0x%llx", val); } else if (field->flags & FIELD_IS_SIGNED) { switch (field->size) { case 4: /* * If field is long then print it in hex. * A long usually stores pointers. */ if (field->flags & FIELD_IS_LONG) trace_seq_printf(s, "0x%x", (int)val); else trace_seq_printf(s, "%d", (int)val); break; case 2: trace_seq_printf(s, "%2d", (short)val); break; case 1: trace_seq_printf(s, "%1d", (char)val); break; default: trace_seq_printf(s, "%lld", val); } } else { if (field->flags & FIELD_IS_LONG) trace_seq_printf(s, "0x%llx", val); else trace_seq_printf(s, "%llu", val); } } field = field->next; } } static void pretty_print(struct trace_seq *s, void *data, int size, struct event_format *event) { struct pevent *pevent = event->pevent; struct print_fmt *print_fmt = &event->print_fmt; struct print_arg *arg = print_fmt->args; struct print_arg *args = NULL; const char *ptr = print_fmt->format; unsigned long long val; struct func_map *func; const char *saveptr; char *bprint_fmt = NULL; char format[32]; int show_func; int len; int ls; if (event->flags & EVENT_FL_FAILED) { trace_seq_printf(s, "[FAILED TO PARSE]"); print_event_fields(s, data, size, event); return; } if (event->flags & EVENT_FL_ISBPRINT) { bprint_fmt = get_bprint_format(data, size, event); args = make_bprint_args(bprint_fmt, data, size, event); arg = args; ptr = bprint_fmt; } for (; *ptr; ptr++) { ls = 0; if (*ptr == '\\') { ptr++; switch (*ptr) { case 'n': trace_seq_putc(s, '\n'); break; case 't': trace_seq_putc(s, '\t'); break; case 'r': trace_seq_putc(s, '\r'); break; case '\\': trace_seq_putc(s, '\\'); break; default: trace_seq_putc(s, *ptr); break; } } else if (*ptr == '%') { saveptr = ptr; show_func = 0; cont_process: ptr++; switch (*ptr) { case '%': trace_seq_putc(s, '%'); break; case '#': /* FIXME: need to handle properly */ goto cont_process; case 'l': ls++; goto cont_process; case 'L': ls = 2; goto cont_process; case '.': case 'z': case 'Z': case '0' ... '9': goto cont_process; case 'p': if (pevent->long_size == 4) ls = 1; else ls = 2; if (*(ptr+1) == 'F' || *(ptr+1) == 'f') { ptr++; show_func = *ptr; } else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') { print_mac_arg(s, *(ptr+1), data, size, event, arg); ptr++; break; } /* fall through */ case 'd': case 'i': case 'x': case 'X': case 'u': if (!arg) die("no argument match"); len = ((unsigned long)ptr + 1) - (unsigned long)saveptr; /* should never happen */ if (len > 32) die("bad format!"); memcpy(format, saveptr, len); format[len] = 0; val = eval_num_arg(data, size, event, arg); arg = arg->next; if (show_func) { func = find_func(pevent, val); if (func) { trace_seq_puts(s, func->func); if (show_func == 'F') trace_seq_printf(s, "+0x%llx", val - func->addr); break; } } if (pevent->long_size == 8 && ls) { char *p; ls = 2; /* make %l into %ll */ p = strchr(format, 'l'); if (p) memmove(p, p+1, strlen(p)+1); else if (strcmp(format, "%p") == 0) strcpy(format, "0x%llx"); } switch (ls) { case 0: trace_seq_printf(s, format, (int)val); break; case 1: trace_seq_printf(s, format, (long)val); break; case 2: trace_seq_printf(s, format, (long long)val); break; default: die("bad count (%d)", ls); } break; case 's': if (!arg) die("no matching argument"); print_str_arg(s, data, size, event, arg); arg = arg->next; break; default: trace_seq_printf(s, ">%c<", *ptr); } } else trace_seq_putc(s, *ptr); } if (args) { free_args(args); free(bprint_fmt); } } /** * pevent_data_lat_fmt - parse the data for the latency format * @pevent: a handle to the pevent * @s: the trace_seq to write to * @data: the raw data to read from * @size: currently unused. * * This parses out the Latency format (interrupts disabled, * need rescheduling, in hard/soft interrupt, preempt count * and lock depth) and places it into the trace_seq. */ void pevent_data_lat_fmt(struct pevent *pevent, struct trace_seq *s, struct record *record) { static int check_lock_depth = 1; static int lock_depth_exists; unsigned int lat_flags; unsigned int pc; int lock_depth; int hardirq; int softirq; void *data = record->data; lat_flags = parse_common_flags(pevent, data); pc = parse_common_pc(pevent, data); /* lock_depth may not always exist */ if (check_lock_depth) { struct format_field *field; struct event_format *event; check_lock_depth = 0; event = pevent->events[0]; field = pevent_find_common_field(event, "common_lock_depth"); if (field) lock_depth_exists = 1; } if (lock_depth_exists) lock_depth = parse_common_lock_depth(pevent, data); hardirq = lat_flags & TRACE_FLAG_HARDIRQ; softirq = lat_flags & TRACE_FLAG_SOFTIRQ; trace_seq_printf(s, "%c%c%c", (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' : (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ? 'X' : '.', (lat_flags & TRACE_FLAG_NEED_RESCHED) ? 'N' : '.', (hardirq && softirq) ? 'H' : hardirq ? 'h' : softirq ? 's' : '.'); if (pc) trace_seq_printf(s, "%x", pc); else trace_seq_putc(s, '.'); if (lock_depth_exists) {